buttons in table footer
parent
d72cf0a0b3
commit
d986fdf232
@ -0,0 +1,15 @@
|
||||
from common.models import LinesModel
|
||||
|
||||
|
||||
class LineFilterFormMixin:
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
user = self.request.user
|
||||
if user.line:
|
||||
form.fields['line'].queryset = form.fields['line'].queryset.filter(pk=user.line.pk)
|
||||
form.fields['line'].initial = user.line
|
||||
form.fields['line'].widget.attrs['readonly'] = True
|
||||
else:
|
||||
form.fields['line'].queryset = LinesModel.objects.filter(company=user.company)
|
||||
form.fields['line'].widget.attrs['readonly'] = False
|
||||
return form
|
||||
@ -0,0 +1,21 @@
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
|
||||
class CRUDListViewMixin:
|
||||
...
|
||||
# def post(self, request, *args, **kwargs):
|
||||
# if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
||||
# object_id = request.POST.get('object_id')
|
||||
# obj = get_object_or_404(self.model, id=object_id)
|
||||
# return JsonResponse(self.get_object_data(obj))
|
||||
# else:
|
||||
# return self.handle_form_submission(request, *args, **kwargs)
|
||||
|
||||
# def get_object_data(self, obj):
|
||||
# """Override this method in child views to specify which data to return"""
|
||||
# raise NotImplementedError
|
||||
#
|
||||
# def handle_form_submission(self, request, *args, **kwargs):
|
||||
# """Override this method in child views to handle form submission"""
|
||||
# raise NotImplementedError
|
||||
@ -1,8 +1,13 @@
|
||||
from django.urls import path
|
||||
from django.urls import path, include
|
||||
|
||||
from booking.views import CreateBookingView, BookingListView
|
||||
from booking.views.client_views import CreateBookingView, ClientBookingView, ClientBookingUpdateView
|
||||
from booking.views.employee_views import BookingListView
|
||||
|
||||
urlpatterns = [
|
||||
path('client/', CreateBookingView.as_view(), name='client_booking'),
|
||||
path('employee_bookings', BookingListView.as_view(), name='employee_bookings'),
|
||||
path('client/', include([
|
||||
path('', ClientBookingView.as_view(), name='client_booking'),
|
||||
path('create/', CreateBookingView.as_view(), name='client_booking_create'),
|
||||
path('update/<int:pk>/', ClientBookingUpdateView.as_view(), name='client_booking_update'),
|
||||
])),
|
||||
path('employee/', BookingListView.as_view(), name='employee_bookings'),
|
||||
]
|
||||
@ -1,52 +1,62 @@
|
||||
from django.shortcuts import render
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic import CreateView, ListView
|
||||
|
||||
from booking.forms import BookingCreateForm
|
||||
from DepoT.mixins.LineFiltweFormMixin import LineFilterFormMixin
|
||||
from booking.forms import BookingCreateForm, BookingUpdateForm
|
||||
from booking.models import Booking
|
||||
from common.utils.utils import filter_queryset_by_user
|
||||
|
||||
|
||||
# Create your views here.
|
||||
class CreateBookingView(CreateView):
|
||||
template_name = 'client-booking-content.html'
|
||||
class ClientBookingView(LoginRequiredMixin, UserPassesTestMixin, ListView):
|
||||
model = Booking
|
||||
form_class = BookingCreateForm
|
||||
success_url = reverse_lazy('client_booking')
|
||||
template_name = 'client/booking-list.html'
|
||||
paginate_by = 4
|
||||
context_object_name = 'objects'
|
||||
base_template = 'client-base.html'
|
||||
|
||||
def test_func(self):
|
||||
return self.request.user.has_company_perm('can_view_booking') or self.request.user.user_type == 'CA'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
queryset = self.model.objects.all().order_by('-created_on')
|
||||
user = self.request.user
|
||||
# !!! important
|
||||
queryset = filter_queryset_by_user( queryset, user)[:10]
|
||||
# !!! important
|
||||
context['recent'] = queryset
|
||||
context['base_template'] = self.base_template
|
||||
return context
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
user = self.request.user
|
||||
result = filter_queryset_by_user(queryset, user)
|
||||
return result
|
||||
|
||||
# If user has a specific line, limit the line choices
|
||||
if user.line:
|
||||
form.fields['line'].queryset = form.fields['line'].queryset.filter(pk=user.line.pk)
|
||||
form.fields['line'].initial = user.line
|
||||
form.fields['line'].widget.readonly = True #attrs['disabled'] = True
|
||||
return form
|
||||
|
||||
class CreateBookingView(LoginRequiredMixin, UserPassesTestMixin, LineFilterFormMixin, CreateView):
|
||||
template_name = 'client/booking-create.html'
|
||||
model = Booking
|
||||
form_class = BookingCreateForm
|
||||
success_url = reverse_lazy('client_booking')
|
||||
|
||||
def form_valid(self, form):
|
||||
# todo more validation
|
||||
form.instance.created_by = self.request.user.id
|
||||
form.instance.updated_by = self.request.user.id
|
||||
form.instance.vehicles_left = form.cleaned_data.get('vehicles')
|
||||
if self.request.user.line:
|
||||
form.instance.line = self.request.user.line
|
||||
return super().form_valid(form)
|
||||
|
||||
def test_func(self):
|
||||
return True # self.request.user.has_company_perm('can_edit_preinfo') or self.request.user.user_type == 'CA'
|
||||
|
||||
class BookingListView(ListView):
|
||||
template_name = 'employee/booking-list.html'
|
||||
|
||||
class ClientBookingUpdateView(LoginRequiredMixin, UserPassesTestMixin, LineFilterFormMixin, CreateView):
|
||||
template_name = 'client/booking-edit.html'
|
||||
model = Booking
|
||||
context_object_name = 'bookings'
|
||||
paginate_by = 30 # Number of containers per page
|
||||
form_class = BookingUpdateForm
|
||||
success_url = reverse_lazy('client_booking')
|
||||
|
||||
def form_valid(self, form):
|
||||
# todo more validation
|
||||
form.instance.updated_by = self.request.user.id
|
||||
return super().form_valid(form)
|
||||
|
||||
def test_func(self):
|
||||
return True # self.request.user.has_company_perm('can_edit_preinfo') or self.request.user.user_type == 'CA'
|
||||
@ -0,0 +1,10 @@
|
||||
from django.views.generic import ListView
|
||||
|
||||
from booking.models import Booking
|
||||
|
||||
|
||||
class BookingListView(ListView):
|
||||
template_name = 'employee/booking-list.html'
|
||||
model = Booking
|
||||
context_object_name = 'bookings'
|
||||
paginate_by = 30 # Number of containers per page
|
||||
@ -1,12 +1,14 @@
|
||||
from django.urls import path, include
|
||||
from common import views
|
||||
|
||||
from common.views.barrier_views import BarrierDashboardView
|
||||
from common.views.employee_views import EmployeeDashboardView
|
||||
from common.views.client_views import ClientDashboardView
|
||||
from common.views.shared_views import IndexView, DashboardRedirectView
|
||||
urlpatterns = [
|
||||
path('', views.IndexView.as_view(), name='index'),
|
||||
path('', IndexView.as_view(), name='index'),
|
||||
path('dashboard/', include([
|
||||
path('', views.DashboardRedirectView.as_view(), name='dashboard'),
|
||||
path('client/', views.ClientDashboardView.as_view(), name='client_dashboard'),
|
||||
path('barrier/', views.BarrierDashboardView.as_view(), name='barrier_dashboard'),
|
||||
path('employee/', views.EmployeeDashboardView.as_view(), name='employee_dashboard'),
|
||||
path('', DashboardRedirectView.as_view(), name='dashboard'),
|
||||
path('client/', ClientDashboardView.as_view(), name='client_dashboard'),
|
||||
path('barrier/', BarrierDashboardView.as_view(), name='barrier_dashboard'),
|
||||
path('employee/', EmployeeDashboardView.as_view(), name='employee_dashboard'),
|
||||
]))
|
||||
]
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
|
||||
class BarrierDashboardView(TemplateView):
|
||||
template_name = 'barrier/barrier-dashboard.html'
|
||||
extra_context = {
|
||||
'title': 'Client Dashboard',
|
||||
'description': 'This is the client dashboard page.',
|
||||
}
|
||||
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return render(request, self.template_name, self.extra_context)
|
||||
@ -0,0 +1,13 @@
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
|
||||
class ClientDashboardView(TemplateView):
|
||||
template_name = 'client-dashboard-content.html'
|
||||
extra_context = {
|
||||
'title': 'Client Dashboard',
|
||||
'description': 'This is the client dashboard page.',
|
||||
}
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return render(request, self.template_name, self.extra_context)
|
||||
@ -0,0 +1,23 @@
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from booking.models import Booking
|
||||
from containers.models import Container
|
||||
from preinfo.models import Preinfo
|
||||
|
||||
|
||||
class EmployeeDashboardView(TemplateView):
|
||||
template_name = 'employee-dashboard-content.html'
|
||||
extra_context = {
|
||||
'title': 'Employee Dashboard',
|
||||
'description': 'This is the depot employee dashboard page.',
|
||||
}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
containers = Container.objects.filter(expedited=False).count()
|
||||
preinfos = Preinfo.objects.filter(received=False).count()
|
||||
bookings = Booking.objects.filter(status='active').count()
|
||||
context['containers'] = containers
|
||||
context['preinfos'] = preinfos
|
||||
context['bookings'] = bookings
|
||||
return context
|
||||
@ -1,14 +1,17 @@
|
||||
from booking.models import Booking
|
||||
from containers.models import Container
|
||||
|
||||
def get_container_for_booking(booking):
|
||||
def get_container_for_booking(booking_number):
|
||||
filters = {
|
||||
'expedited': False,
|
||||
}
|
||||
|
||||
booking = Booking.objects.get(number=booking_number)
|
||||
if not booking:
|
||||
return None
|
||||
if booking.container_number:
|
||||
filters['container_number'] = booking.container_number
|
||||
else:
|
||||
filters['container_type'] = booking.container_type
|
||||
filters['line'] = booking.line
|
||||
|
||||
return Container.objects.filter(**filters).order_by('created_at').first()
|
||||
return Container.objects.filter(**filters).order_by('received_on').first()
|
||||
@ -1,12 +1,13 @@
|
||||
from django.urls import include, path
|
||||
|
||||
from containers.views import ContainerReceive, ContainerExpedition, ContainerSearchView, ContainersListView
|
||||
from containers.views.employee_views import ContainersListView
|
||||
from containers.views.barrier_views import ContainerReceive, ContainerExpedition, ContainerSearchView
|
||||
|
||||
urlpatterns = [
|
||||
path('search', ContainerSearchView.as_view(), name='container_search'),
|
||||
path('employee_containers', ContainersListView.as_view(), name='employee_containers'),
|
||||
path('<int:pk>/', include([
|
||||
path('receive', ContainerReceive.as_view(), name='container_receive'),
|
||||
path('expedition', ContainerExpedition.as_view(), name='container_expedition'),
|
||||
path('search/', ContainerSearchView.as_view(), name='container_search'),
|
||||
path('employee/', ContainersListView.as_view(), name='employee_containers'),
|
||||
path('barrier/<int:pk>/', include([
|
||||
path('receive/', ContainerReceive.as_view(), name='container_receive'),
|
||||
path('expedition/', ContainerExpedition.as_view(), name='container_expedition'),
|
||||
])),
|
||||
]
|
||||
@ -0,0 +1,164 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.views import View
|
||||
from django.views.generic import CreateView, UpdateView, FormView, ListView
|
||||
|
||||
from booking.models import Booking
|
||||
from common.utils.utils import get_container_by_number
|
||||
from containers.forms import ContainerReceiveForm, ContainerExpeditionForm
|
||||
from containers.models import Container
|
||||
from containers.services import get_container_for_booking
|
||||
from preinfo.models import Preinfo
|
||||
|
||||
|
||||
# Create your views here.
|
||||
class ContainerReceive(CreateView):
|
||||
template_name = 'container-receive.html'
|
||||
model = Container
|
||||
form_class = ContainerReceiveForm
|
||||
success_url = reverse_lazy('dashboard')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
pk = self.kwargs.get('pk')
|
||||
try:
|
||||
preinfo =Preinfo.objects.filter(pk=pk, received=False).first()
|
||||
except Preinfo.DoesNotExist:
|
||||
preinfo = None
|
||||
|
||||
if preinfo:
|
||||
context['preinfo'] = preinfo
|
||||
context['containers'] = Container.objects.order_by('-received_on').all()[:10] # Fetch the last 10 containers
|
||||
return context
|
||||
return redirect(reverse_lazy('container_search'))
|
||||
|
||||
def form_valid(self, form):
|
||||
|
||||
# Get the preinfo_id from the POST data
|
||||
preinfo_id = self.request.POST.get('preinfo_id')
|
||||
try:
|
||||
preinfo = Preinfo.objects.get(id=preinfo_id)
|
||||
except Preinfo.DoesNotExist:
|
||||
preinfo = None
|
||||
|
||||
# validate if data is correct, comparing user data with preinfo data
|
||||
if preinfo and preinfo.container_number == form.cleaned_data.get('number') and not preinfo.received:
|
||||
preinfo.received = True
|
||||
preinfo.save()
|
||||
|
||||
form.instance.received_by = self.request.user
|
||||
form.instance.line = preinfo.line
|
||||
form.instance.container_type = preinfo.container_type
|
||||
else:
|
||||
form.add_error('number', 'Invalid data')
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ContainerSearchView(View):
|
||||
template_name = 'container-search.html'
|
||||
|
||||
def get(self, request):
|
||||
return render(request, self.template_name)
|
||||
|
||||
def post(self, request):
|
||||
booking = Booking.objects.get(number=request.POST.get('number'))
|
||||
if not booking:
|
||||
return render(request, self.template_name, {'error': 'Booking not found'})
|
||||
|
||||
filters = {
|
||||
'expedited': False,
|
||||
}
|
||||
if booking.container_number:
|
||||
filters['container_number'] = booking.container_number
|
||||
else:
|
||||
filters['container_type'] = booking.container_type
|
||||
filters['line'] = booking.line
|
||||
|
||||
container = Container.objects.filter(**filters).order_by('received_on').first()
|
||||
if not container:
|
||||
return render(request, self.template_name, {'error': 'Not found'})
|
||||
next_url = request.POST.get('param')
|
||||
return redirect(next_url, pk=booking.pk)
|
||||
|
||||
|
||||
class ContainerExpedition(UpdateView):
|
||||
template_name = 'container-expedition.html'
|
||||
model = Container
|
||||
form_class = ContainerExpeditionForm
|
||||
success_url = reverse_lazy('dashboard')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
pk = self.kwargs.get('pk')
|
||||
booking = Booking.objects.get(pk=pk)
|
||||
filters = {
|
||||
'expedited': False,
|
||||
}
|
||||
if booking.container_number:
|
||||
filters['container_number'] = booking.container_number
|
||||
else:
|
||||
filters['container_type'] = booking.container_type
|
||||
filters['line'] = booking.line
|
||||
|
||||
container = Container.objects.filter(**filters).order_by('received_on').first()
|
||||
context['container'] = container
|
||||
context['booking'] = booking
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
booking_id = self.request.POST.get('booking_id')
|
||||
container_id = self.request.POST.get('container_id')
|
||||
|
||||
try:
|
||||
booking = Booking.objects.get(id=booking_id)
|
||||
container = Container.objects.get(id=container_id)
|
||||
vehicle = form.cleaned_data['expedition_vehicle']
|
||||
# Update container
|
||||
container.expedited = True
|
||||
container.expedited_on = timezone.now()
|
||||
container.expedited_by = self.request.user
|
||||
container.expedition_vehicle = vehicle
|
||||
container.booking = booking
|
||||
container.save()
|
||||
|
||||
# Update booking status if needed
|
||||
booking.container_expedited_count += 1
|
||||
if booking.vehicles:
|
||||
vehicles_list = booking.vehicles.split(',')
|
||||
if vehicle in vehicles_list:
|
||||
vehicles_list.remove(vehicle)
|
||||
booking.vehicles = ','.join(vehicles_list)
|
||||
if booking.container_expedited_count >= booking.container_count:
|
||||
booking.status = 'completed' # or any other status you want to set
|
||||
booking.save()
|
||||
|
||||
return redirect(self.success_url)
|
||||
|
||||
except (Booking.DoesNotExist, Container.DoesNotExist):
|
||||
form.add_error(None, 'Invalid booking or container')
|
||||
return self.form_invalid(form)
|
||||
|
||||
|
||||
# try:
|
||||
# booking = Booking.objects.get(id=booking_id)
|
||||
# except Booking.DoesNotExist:
|
||||
# booking = None
|
||||
# # validate if data is correct, comparing user data with booking data
|
||||
# if booking and booking.container_number == form.cleaned_data.get('container_number') and not booking.expedited:
|
||||
# booking.expedited = True
|
||||
# booking.save()
|
||||
#
|
||||
# form.instance.expedited_by = self.request.user
|
||||
# form.instance.line = booking.line
|
||||
# form.instance.container_type = booking.container_type
|
||||
# else:
|
||||
# form.add_error('container_number', 'Invalid data')
|
||||
|
||||
# if preinfo:
|
||||
# context['preinfo'] = preinfo
|
||||
# context['containers'] = Container.objects.order_by('-received_on').all()[:10] # Fetch the last 10 containers
|
||||
# return context
|
||||
# return redirect(reverse_lazy('container_search'))
|
||||
|
||||
@ -1,9 +1,17 @@
|
||||
from django.urls import path
|
||||
from preinfo.views import ClientPreinfoView, check_preinfo, PreinfoSearchView, PreinfoListView
|
||||
from django.urls import path, include
|
||||
from preinfo.views.client_views import (ClientPreinfoView, check_preinfo, PreinfoSearchView, ClientPreinfoCreateView, ClientPreinfoUpdateView)
|
||||
from preinfo.views.employee_views import EmployeePreinfoView
|
||||
|
||||
urlpatterns = [
|
||||
path('client/', ClientPreinfoView.as_view(), name='client_preinfo'),
|
||||
path('client/', include([
|
||||
path('', ClientPreinfoView.as_view(), name='client_preinfo'),
|
||||
path('create/', ClientPreinfoCreateView.as_view(), name='client_preinfo_create'),
|
||||
path('update/<int:pk>/', ClientPreinfoUpdateView.as_view(), name='client_preinfo_update'),
|
||||
])),
|
||||
path('check-preinfo/', check_preinfo, name='check_preinfo'),
|
||||
path('preinfo-search/', PreinfoSearchView.as_view(), name='preinfo_search'),
|
||||
path('employee_preinfo/', PreinfoListView.as_view(), name='employee_preinfo'),
|
||||
path('employee/', include([
|
||||
path('', EmployeePreinfoView.as_view(), name='employee_preinfo'),
|
||||
])
|
||||
)
|
||||
]
|
||||
@ -0,0 +1,32 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||
from django.forms import forms
|
||||
from django.forms.widgets import HiddenInput
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.views import View
|
||||
from django.views.generic import TemplateView, FormView, CreateView, ListView
|
||||
|
||||
from DepoT.mixins.crudListViewMixin import CRUDListViewMixin
|
||||
from common.models import ContainerTypeModel, LinesModel
|
||||
from common.utils.utils import filter_queryset_by_user, get_preinfo_by_number
|
||||
from preinfo.forms import PreinfoBaseForm, PreinfoCreateForm, PreinfoEditForm
|
||||
from preinfo.models import Preinfo
|
||||
|
||||
class EmployeePreinfoView(LoginRequiredMixin, UserPassesTestMixin, ListView):
|
||||
model = Preinfo
|
||||
template_name = 'employee/preinfo-list.html'
|
||||
context_object_name = 'objects'
|
||||
paginate_by = 30
|
||||
form_class = PreinfoEditForm
|
||||
base_template = 'employee-base.html'
|
||||
|
||||
def test_func(self):
|
||||
return True # self.request.user.has_employee_perm('can_view_preinfo') or self.request.user.user_type == 'CA'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['base_template'] = self.base_template
|
||||
return context
|
||||
@ -0,0 +1,36 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const editBtn = document.getElementById('editBtn');
|
||||
const deleteBtn = document.getElementById('deleteBtn');
|
||||
const objectIdInput = document.getElementById('objectIdInput');
|
||||
const rows = document.querySelectorAll('.selectable-row');
|
||||
|
||||
if (editBtn) {
|
||||
editBtn.setAttribute('disabled', '');
|
||||
}
|
||||
if (deleteBtn) {
|
||||
deleteBtn.setAttribute('disabled', '');
|
||||
}
|
||||
|
||||
rows.forEach(row => {
|
||||
row.addEventListener('click', function() {
|
||||
// Remove previous selection
|
||||
document.querySelector('.selected-row')?.classList.remove('selected-row');
|
||||
|
||||
// Select current row
|
||||
this.classList.add('selected-row');
|
||||
|
||||
const objectId = this.dataset.id;
|
||||
if (editBtn) {
|
||||
editBtn.removeAttribute('disabled'); // Remove disabled attribute completely
|
||||
editBtn.href = editBtn.dataset.url.replace('0', objectId);
|
||||
}
|
||||
if (deleteBtn) {
|
||||
deleteBtn.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
// Check the hidden radio button
|
||||
const radio = this.querySelector('input[type="radio"]');
|
||||
radio.checked = true;
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,59 @@
|
||||
body {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #F2E8DB; /* Add this */
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
|
||||
.nav-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #1a202c;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: #718096;
|
||||
padding: 0.5rem;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.icon-button:hover {
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
.dashboard-wrapper {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.tables-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
/*background-color: #ece1d8;*/
|
||||
border: 1px solid #a57d52;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon-circle {
|
||||
padding: 0.75rem;
|
||||
border-radius: 50%;
|
||||
background-color: #ead9cb;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.dashboard-icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
stroke: #a57d52;
|
||||
}
|
||||
|
||||
.stat-info h3 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
/*color: #a57d52;*/
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 1.875rem;
|
||||
font-weight: bold;
|
||||
/*color: #a57d52;*/
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
.stat-change {
|
||||
font-size: 0.875rem;
|
||||
color: #68a357;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid #a57d52;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card-header h3 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
/*color: #a57d52;*/
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/*.dashboard-table {*/
|
||||
/* width: 100%;*/
|
||||
/* border-collapse: collapse;*/
|
||||
/*}*/
|
||||
|
||||
/*.dashboard-table th {*/
|
||||
/* text-align: left;*/
|
||||
/* padding: 0.75rem;*/
|
||||
/* font-size: 0.75rem;*/
|
||||
/* text-transform: uppercase;*/
|
||||
/* !*color: #a57d52;*!*/
|
||||
/*}*/
|
||||
|
||||
/*.dashboard-table td {*/
|
||||
/* padding: 0.75rem;*/
|
||||
/* font-size: 0.875rem;*/
|
||||
/* !*color: #666;*!*/
|
||||
/*}*/
|
||||
|
||||
/*.status-tag {*/
|
||||
/* display: inline-block;*/
|
||||
/* padding: 0.25rem 0.75rem;*/
|
||||
/* border-radius: 9999px;*/
|
||||
/* font-size: 0.75rem;*/
|
||||
/* font-weight: 600;*/
|
||||
/*}*/
|
||||
|
||||
/*.status-received {*/
|
||||
/* background-color: #e8f5e9;*/
|
||||
/* color: #2e7d32;*/
|
||||
/*}*/
|
||||
|
||||
/*.status-pending {*/
|
||||
/* background-color: #fff3e0;*/
|
||||
/* color: #ef6c00;*/
|
||||
/*}*/
|
||||
|
||||
/*.status-overdue {*/
|
||||
/* background-color: #ffebee;*/
|
||||
/* color: #c62828;*/
|
||||
/*}*/
|
||||
@ -0,0 +1,107 @@
|
||||
.sidebar {
|
||||
width: 16rem;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 1.25rem;
|
||||
border-bottom: 1px solid #a57d52;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
/*color: #a57d52;*/
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 0.875rem;
|
||||
/*color: #a57d52;*/
|
||||
}
|
||||
|
||||
.section-title {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.75rem;
|
||||
/*color: #a57d52;*/
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1.5rem;
|
||||
text-decoration: none;
|
||||
border-radius: 0.5rem;
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
border: 1px solid #a57d52;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background-color: rgba(165, 125, 82, 0.1);
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
flex: 1 1 auto; /* Changed from flex-grow: 1 */
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
margin-right: 0.75rem;
|
||||
stroke: currentColor;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
margin-top: auto; /* Added this line */
|
||||
padding: 1rem;
|
||||
border-top: 1px solid #a57d52;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background-color: #a57d52;
|
||||
color: #ead9cb;
|
||||
border-radius: 9999px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
/*color: #a57d52;*/
|
||||
}
|
||||
|
||||
.company {
|
||||
font-size: 0.75rem;
|
||||
/*color: #a57d52;*/
|
||||
}
|
||||
|
||||
.logout {
|
||||
margin-left: auto;
|
||||
color: #a57d52;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
/*transition: all 0.2s;*/
|
||||
}
|
||||
|
||||
.logout:hover {
|
||||
border: 1px solid #a57d52;
|
||||
}
|
||||
@ -0,0 +1,196 @@
|
||||
/*.table {*/
|
||||
/* width: 100%;*/
|
||||
/* border-collapse: separate;*/
|
||||
/* border-spacing: 0;*/
|
||||
/* margin: 10px auto;*/
|
||||
/* border: 1px solid #a57d52;*/
|
||||
/* border-radius: 10px;*/
|
||||
/* overflow: hidden;*/
|
||||
/*}*/
|
||||
|
||||
/*.table th {*/
|
||||
/* text-align: left;*/
|
||||
/* padding: 0.75rem;*/
|
||||
/* font-size: 0.75rem;*/
|
||||
/* text-transform: uppercase;*/
|
||||
/* font-weight: 600;*/
|
||||
/* border-bottom: 1px solid #a57d52;*/
|
||||
/* background-color: #F2E8DB;*/
|
||||
/*}*/
|
||||
|
||||
/*.table td {*/
|
||||
/* padding: 0.75rem;*/
|
||||
/* font-size: 0.875rem;*/
|
||||
/* border-bottom: 1px solid #a57d52;*/
|
||||
/*}*/
|
||||
|
||||
/*!* Last row shouldn't have bottom border *!*/
|
||||
/*.table tr:last-child td {*/
|
||||
/* border-bottom: none;*/
|
||||
/*}*/
|
||||
|
||||
/*!* Selection styles *!*/
|
||||
/*.selectable-row {*/
|
||||
/* cursor: pointer;*/
|
||||
/*}*/
|
||||
|
||||
/*.selected-row {*/
|
||||
/* background-color: rgba(165, 125, 82, 0.1);*/
|
||||
/*}*/
|
||||
|
||||
/*.status-received {*/
|
||||
/* background-color: #e8f5e9;*/
|
||||
/* color: #2e7d32;*/
|
||||
/*}*/
|
||||
|
||||
/*.status-pending {*/
|
||||
/* background-color: #fff3e0;*/
|
||||
/* color: #ef6c00;*/
|
||||
/*}*/
|
||||
|
||||
/*.status-overdue {*/
|
||||
/* background-color: #ffebee;*/
|
||||
/* color: #c62828;*/
|
||||
/*}*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
thead {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px 16px;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
color: #666;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
|
||||
/*Rename dashboard-table to match exactly with your HTML */
|
||||
.dashboard-table,
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
margin: 10px auto;
|
||||
width: 100%;
|
||||
border: 1px solid #E1C6A8; /* Make border more visible */
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* Keep header and footer styles */
|
||||
thead {
|
||||
background-color: #E1C6A8;
|
||||
}
|
||||
|
||||
tfoot {
|
||||
background-color: #E1C6A8;
|
||||
}
|
||||
|
||||
/* Update cell styles */
|
||||
.dashboard-table th,
|
||||
.dashboard-table td,
|
||||
table th,
|
||||
table td {
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
border-right: 1px solid #E1C6A8; /* Add vertical borders */
|
||||
border-bottom: 1px solid #E1C6A8; /* Add horizontal borders */
|
||||
}
|
||||
|
||||
/* Remove right border from last cell in each row */
|
||||
.dashboard-table th:last-child,
|
||||
.dashboard-table td:last-child,
|
||||
table th:last-child,
|
||||
table td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
/* Remove bottom border from last row cells */
|
||||
.dashboard-table tr:last-child td,
|
||||
table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.footer-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer-left button,
|
||||
.footer-right button {
|
||||
margin: 0 3px;
|
||||
padding: 3px 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.page-number {
|
||||
margin: 0 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.status {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
border-radius: 12px;
|
||||
background-color: #e0f0ff;
|
||||
color: #0066cc;
|
||||
}
|
||||
|
||||
.actions button {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #0066cc;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.actions button:hover {
|
||||
color: #004999;
|
||||
}
|
||||
|
||||
.actions button:last-child {
|
||||
color: #cc0000;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.actions button:last-child:hover {
|
||||
color: #990000;
|
||||
}
|
||||
|
||||
@ -1,10 +1,23 @@
|
||||
{% load static %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>$Title$</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Line Operator Dashboard | Container Depot</title>
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&family=Urbanist:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="{% static 'styles/forms.css' %}?v={% now 'U' %}">
|
||||
|
||||
{# <script src="https://cdn.tailwindcss.com"></script>#}
|
||||
</head>
|
||||
<body>
|
||||
$END$
|
||||
{% block content %}
|
||||
|
||||
{% endblock content %}
|
||||
</body>
|
||||
</html>
|
||||
@ -1,162 +1,269 @@
|
||||
{% extends 'client-base.html' %}
|
||||
{% block content %}
|
||||
|
||||
<div id="dashboardContent" class="tab-content active">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
||||
<div class="card bg-white rounded-lg shadow p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-blue-100 text-blue-600">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<h3 class="text-lg font-semibold text-gray-700">Active Containers</h3>
|
||||
<p class="text-3xl font-bold text-gray-900">42</p>
|
||||
<p class="text-sm text-green-600">+3 since last week</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{# <div id="dashboardContent" class="tab-content active">#}
|
||||
{# <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">#}
|
||||
{# <div class="card bg-white rounded-lg shadow p-6">#}
|
||||
{# <div class="flex items-center">#}
|
||||
{# <div class="p-3 rounded-full bg-blue-100 text-blue-600">#}
|
||||
{# <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">#}
|
||||
{# <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />#}
|
||||
{# </svg>#}
|
||||
{# </div>#}
|
||||
{# <div class="ml-4">#}
|
||||
{# <h3 class="text-lg font-semibold text-gray-700">Active Containers</h3>#}
|
||||
{# <p class="text-3xl font-bold text-gray-900">42</p>#}
|
||||
{# <p class="text-sm text-green-600">+3 since last week</p>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{##}
|
||||
{# <div class="card bg-white rounded-lg shadow p-6">#}
|
||||
{# <div class="flex items-center">#}
|
||||
{# <div class="p-3 rounded-full bg-green-100 text-green-600">#}
|
||||
{# <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">#}
|
||||
{# <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />#}
|
||||
{# </svg>#}
|
||||
{# </div>#}
|
||||
{# <div class="ml-4">#}
|
||||
{# <h3 class="text-lg font-semibold text-gray-700">Preinfo Sent</h3>#}
|
||||
{# <p class="text-3xl font-bold text-gray-900">18</p>#}
|
||||
{# <p class="text-sm text-green-600">+5 since last week</p>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{##}
|
||||
{# <div class="card bg-white rounded-lg shadow p-6">#}
|
||||
{# <div class="flex items-center">#}
|
||||
{# <div class="p-3 rounded-full bg-orange-100 text-orange-600">#}
|
||||
{# <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">#}
|
||||
{# <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />#}
|
||||
{# </svg>#}
|
||||
{# </div>#}
|
||||
{# <div class="ml-4">#}
|
||||
{# <h3 class="text-lg font-semibold text-gray-700">Bookings</h3>#}
|
||||
{# <p class="text-3xl font-bold text-gray-900">7</p>#}
|
||||
{# <p class="text-sm text-orange-600">2 require attention</p>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{##}
|
||||
{# <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">#}
|
||||
{# <div class="bg-white rounded-lg shadow">#}
|
||||
{# <div class="px-6 py-4 border-b border-gray-200">#}
|
||||
{# <h3 class="text-lg font-semibold text-gray-800">Recent Container Activity</h3>#}
|
||||
{# </div>#}
|
||||
{# <div class="p-6">#}
|
||||
{# <div class="overflow-x-auto">#}
|
||||
{# <table class="min-w-full divide-y divide-gray-200">#}
|
||||
{# <thead>#}
|
||||
{# <tr>#}
|
||||
{# <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Container</th>#}
|
||||
{# <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>#}
|
||||
{# <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>#}
|
||||
{# <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>#}
|
||||
{# </tr>#}
|
||||
{# </thead>#}
|
||||
{# <tbody class="divide-y divide-gray-200">#}
|
||||
{# <tr>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU1234567</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">40HC</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap">#}
|
||||
{# <span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">Received</span>#}
|
||||
{# </td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-15</td>#}
|
||||
{# </tr>#}
|
||||
{# <tr>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU7654321</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">20DV</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap">#}
|
||||
{# <span class="px-2 py-1 text-xs font-semibold rounded-full bg-blue-100 text-blue-800">Preinfo</span>#}
|
||||
{# </td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-14</td>#}
|
||||
{# </tr>#}
|
||||
{# <tr>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU2468135</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">40DV</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap">#}
|
||||
{# <span class="px-2 py-1 text-xs font-semibold rounded-full bg-orange-100 text-orange-800">Order</span>#}
|
||||
{# </td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-13</td>#}
|
||||
{# </tr>#}
|
||||
{# <tr>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU1357924</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">20RF</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap">#}
|
||||
{# <span class="px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">Expedited</span>#}
|
||||
{# </td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-12</td>#}
|
||||
{# </tr>#}
|
||||
{# </tbody>#}
|
||||
{# </table>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{##}
|
||||
{# <div class="bg-white rounded-lg shadow">#}
|
||||
{# <div class="px-6 py-4 border-b border-gray-200">#}
|
||||
{# <h3 class="text-lg font-semibold text-gray-800">Payment Status</h3>#}
|
||||
{# </div>#}
|
||||
{# <div class="p-6">#}
|
||||
{# <div class="overflow-x-auto">#}
|
||||
{# <table class="min-w-full divide-y divide-gray-200">#}
|
||||
{# <thead>#}
|
||||
{# <tr>#}
|
||||
{# <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Invoice</th>#}
|
||||
{# <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th>#}
|
||||
{# <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>#}
|
||||
{# <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Due Date</th>#}
|
||||
{# </tr>#}
|
||||
{# </thead>#}
|
||||
{# <tbody class="divide-y divide-gray-200">#}
|
||||
{# <tr>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">INV-2023-0042</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">$1,250.00</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap">#}
|
||||
{# <span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">Paid</span>#}
|
||||
{# </td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-10</td>#}
|
||||
{# </tr>#}
|
||||
{# <tr>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">INV-2023-0041</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">$875.50</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap">#}
|
||||
{# <span class="px-2 py-1 text-xs font-semibold rounded-full bg-yellow-100 text-yellow-800">Pending</span>#}
|
||||
{# </td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-20</td>#}
|
||||
{# </tr>#}
|
||||
{# <tr>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">INV-2023-0040</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">$2,100.00</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap">#}
|
||||
{# <span class="px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">Overdue</span>#}
|
||||
{# </td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-05</td>#}
|
||||
{# </tr>#}
|
||||
{# <tr>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">INV-2023-0039</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">$950.25</td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap">#}
|
||||
{# <span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">Paid</span>#}
|
||||
{# </td>#}
|
||||
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-05-28</td>#}
|
||||
{# </tr>#}
|
||||
{# </tbody>#}
|
||||
{# </table>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
|
||||
<div class="card bg-white rounded-lg shadow p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-green-100 text-green-600">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<h3 class="text-lg font-semibold text-gray-700">Preinfo Sent</h3>
|
||||
<p class="text-3xl font-bold text-gray-900">18</p>
|
||||
<p class="text-sm text-green-600">+5 since last week</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-white rounded-lg shadow p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-orange-100 text-orange-600">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<h3 class="text-lg font-semibold text-gray-700">Bookings</h3>
|
||||
<p class="text-3xl font-bold text-gray-900">7</p>
|
||||
<p class="text-sm text-orange-600">2 require attention</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="stats-grid">
|
||||
<div class="dashboard-card">
|
||||
<div class="card-content">
|
||||
<div class="icon-circle">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="dashboard-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3>Active Containers</h3>
|
||||
<p class="stat-number">42</p>
|
||||
<p class="stat-change">+3 since last week</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-card">
|
||||
<div class="card-content">
|
||||
<div class="icon-circle">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3>Preinfo sent</h3>
|
||||
<p class="stat-number">17</p>
|
||||
<p class="stat-change">+7 since last week</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-card">
|
||||
<div class="card-content">
|
||||
<div class="icon-circle">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3>Bookings active</h3>
|
||||
<p class="stat-number">4</p>
|
||||
<p class="stat-change">+8 since last week</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div class="bg-white rounded-lg shadow">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Recent Container Activity</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Container</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU1234567</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">40HC</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap">
|
||||
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">Received</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-15</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU7654321</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">20DV</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap">
|
||||
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-blue-100 text-blue-800">Preinfo</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-14</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU2468135</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">40DV</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap">
|
||||
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-orange-100 text-orange-800">Order</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-13</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU1357924</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">20RF</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap">
|
||||
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">Expedited</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-12</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Other two cards similar structure -->
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Payment Status</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Invoice</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Due Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">INV-2023-0042</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">$1,250.00</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap">
|
||||
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">Paid</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">INV-2023-0041</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">$875.50</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap">
|
||||
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-yellow-100 text-yellow-800">Pending</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-20</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">INV-2023-0040</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">$2,100.00</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap">
|
||||
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">Overdue</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-05</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">INV-2023-0039</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">$950.25</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap">
|
||||
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">Paid</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-05-28</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tables-grid">
|
||||
<div class="dashboard-card">
|
||||
<div class="card-header">
|
||||
<h3>Recent Container Activity</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Container</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>MSCU1234567</td>
|
||||
<td>40HC</td>
|
||||
<td><span class="status-tag status-received">Received</span></td>
|
||||
<td>2023-06-15</td>
|
||||
</tr>
|
||||
<!-- Other rows similar structure -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-card">
|
||||
<div class="card-header">
|
||||
<h3>Recent payments</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Container</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>MSCU1234567</td>
|
||||
<td>40HC</td>
|
||||
<td><span class="status-tag status-received">Received</span></td>
|
||||
<td>2023-06-15</td>
|
||||
</tr>
|
||||
<!-- Other rows similar structure -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -1,64 +1,66 @@
|
||||
{% load static %}
|
||||
<aside class="sidebar w-64 h-full text-white flex flex-col">
|
||||
<div class="p-5 border-b border-blue-700">
|
||||
<h1 class="text-xl font-bold">Container Depot</h1>
|
||||
<p class="text-sm text-blue-200">Line Operator Portal</p>
|
||||
</div>
|
||||
<aside class="sidebar">
|
||||
<div class="header">
|
||||
<h1>Container Depot</h1>
|
||||
<p class="subtitle">Line Operator Portal</p>
|
||||
</div>
|
||||
{% url 'dashboard' as dashboard_url %}
|
||||
{% url 'client_preinfo' as client_preinfo_url %}
|
||||
{% url 'client_booking' as client_booking_url %}
|
||||
{% url 'register' as register_url %}
|
||||
|
||||
<nav class="flex-grow py-4">
|
||||
<div class="px-4 py-2 text-xs text-blue-300 uppercase tracking-wider">Main</div>
|
||||
<a href="{% url 'dashboard' %}" class="nav-item active flex items-center px-6 py-3 text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="{% url 'client_preinfo' %}" id="preinfoNav" class="nav-item flex items-center px-6 py-3 text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||
</svg>
|
||||
Container Preinfo
|
||||
</a>
|
||||
<a href="{% url 'client_booking' %}" id="ordersNav" class="nav-item flex items-center px-6 py-3 text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
|
||||
</svg>
|
||||
Bookings
|
||||
</a>
|
||||
<a href="#" class="nav-item flex items-center px-6 py-3 text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
Reports
|
||||
</a>
|
||||
|
||||
{% if request.user.UserType.COMPANY_ADMIN %}
|
||||
|
||||
<div class="px-4 py-2 mt-6 text-xs text-blue-300 uppercase tracking-wider">Account</div>
|
||||
<a href="{% url 'register' %}" class="nav-item flex items-center px-6 py-3 text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<nav class="nav-menu">
|
||||
<div class="section-title">Main</div>
|
||||
<a href="{{ dashboard_url }}" class="nav-item {% if request.path == dashboard_url %}active{% endif %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="{{ client_preinfo_url }}" class="nav-item {% if request.path == client_preinfo_url %}active{% endif %}" id="preinfoNav">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
|
||||
</svg>
|
||||
Container Preinfo
|
||||
</a>
|
||||
<a href="{{ client_booking_url }}" class="nav-item {% if request.path == client_booking_url %}active{% endif %}" id="ordersNav">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" />
|
||||
</svg>
|
||||
Bookings
|
||||
</a>
|
||||
<div class="section-title account">Reports</div>
|
||||
<a href="#" class="nav-item">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<g>
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-1.95-9H15v2h-4.95a2.5 2.5 0 0 0 4.064 1.41l1.7 1.133A4.5 4.5 0 0 1 8.028 13H7v-2h1.027a4.5 4.5 0 0 1 7.788-2.543L14.114 9.59A2.5 2.5 0 0 0 10.05 11z"/>
|
||||
</g>
|
||||
</svg>
|
||||
Reports
|
||||
</a>
|
||||
{% if request.user.UserType == 'CA' %}
|
||||
<div class="section-title account">Account</div>
|
||||
<a href="{{ register_url }}" class="nav-item {% if request.path == register_url %}active{% endif %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
Settings
|
||||
Accounts
|
||||
</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
|
||||
<div class="p-4 border-t border-blue-700">
|
||||
<div class="flex items-center">
|
||||
<div class="w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center text-white font-bold">
|
||||
LO
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm font-medium">{{ request.user }}</p>
|
||||
<p class="text-xs text-blue-300">{{ request.user.company }}</p>
|
||||
</div>
|
||||
<a href="{% url 'login' %}" class="ml-auto text-white" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</nav>
|
||||
<div class="user-profile">
|
||||
<div class="avatar">LO</div>
|
||||
<div class="user-info">
|
||||
<p class="username">{{ request.user }}</p>
|
||||
<p class="company">{{ request.user.company }}</p>
|
||||
</div>
|
||||
</aside>
|
||||
<a href="{% url 'login' %}" class="logout">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -1,10 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>$Title$</title>
|
||||
</head>
|
||||
<body>
|
||||
$END$
|
||||
</body>
|
||||
</html>
|
||||
{% extends 'client-base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h2>Booking</h2>
|
||||
<p>Manage your container booking details here.</p>
|
||||
</div>
|
||||
<form id="preinfoForm" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Submit booking">
|
||||
</form>
|
||||
{%endblock content %}
|
||||
|
||||
|
||||
|
||||
@ -1,10 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>$Title$</title>
|
||||
</head>
|
||||
<body>
|
||||
$END$
|
||||
</body>
|
||||
</html>
|
||||
{% extends 'list-crud.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block table_header %}
|
||||
<th style="display: none;">Select</th>
|
||||
<th>Line</th>
|
||||
<th>Booking №</th>
|
||||
<th>Container type</th>
|
||||
<th>Container №</th>
|
||||
<th>Containers count</th>
|
||||
<th>Containers left</th>
|
||||
<th>Vehicles</th>
|
||||
<th>Vehicles left</th>
|
||||
<th>Created on</th>
|
||||
<th>Created by</th>
|
||||
{% endblock table_header %}
|
||||
|
||||
{% block table_data %}
|
||||
<td>{{ object.line.short_name }}</td>
|
||||
<td>{{ object.number }}</td>
|
||||
<td>{{ object.container_type }}</td>
|
||||
<td>{{ object.container_number }}</td>
|
||||
<td>{{ object.container_count }}</td>
|
||||
<td>{{ object.containers_left }}</td>
|
||||
<td>{{ object.vehicles }}</td>
|
||||
<td>{{ object.vehicles_left }}</td>
|
||||
<td>{{ object.created_on }}</td>
|
||||
<td>{{ object.created_by.username }}</td>
|
||||
{% endblock %}
|
||||
|
||||
{% block buttons %}
|
||||
<a href="{% url 'client_booking_create' %}" class="btn btn-primary">Create Preinfo</a>
|
||||
<a href="#" id="editBtn" data-url="{% url 'client_booking_update' pk=0 %}" class="btn btn-primary" disabled>Edit Preinfo</a>
|
||||
<button id="deleteButton" class="btn btn-danger">Delete Preinfo</button>
|
||||
{% endblock buttons %}
|
||||
|
||||
{% block create_modal_header %}
|
||||
<h2>Create Booking</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal_header %}
|
||||
<h2>Edit Booking</h2>
|
||||
{% endblock modal_header %}
|
||||
|
||||
@ -1,10 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>$Title$</title>
|
||||
</head>
|
||||
<body>
|
||||
$END$
|
||||
</body>
|
||||
</html>
|
||||
{% extends 'client-base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h2>Container Preinfo</h2>
|
||||
<p>Manage your container preinfo details here.</p>
|
||||
</div>
|
||||
<form id="preinfoForm" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Submit preinfo">
|
||||
</form>
|
||||
{%endblock content %}
|
||||
|
||||
|
||||
|
||||
@ -1,629 +1,20 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Barrier Staff Interface | Container Depot</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
.sidebar {
|
||||
background: linear-gradient(180deg, #0f4c81 0%, #1a6baf 100%);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.content-area {
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.nav-item {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.nav-item:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.nav-item.active {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-left: 4px solid white;
|
||||
}
|
||||
.card {
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.tab {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.tab-content {
|
||||
display: none;
|
||||
}
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
animation: fadeIn 0.5s ease-in-out;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
.form-section {
|
||||
animation: slideIn 0.5s ease-in-out;
|
||||
}
|
||||
@keyframes slideIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.camera-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
z-index: 50;
|
||||
display: none;
|
||||
animation: fadeIn 0.3s ease-in-out;
|
||||
}
|
||||
.camera-frame {
|
||||
border: 2px solid #fff;
|
||||
box-shadow: 0 0 0 2000px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.image-preview {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
background-color: #f3f4f6;
|
||||
border: 2px dashed #d1d5db;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.image-preview svg {
|
||||
color: #9ca3af;
|
||||
}
|
||||
.image-preview.has-image {
|
||||
border: none;
|
||||
}
|
||||
.image-preview.has-image svg {
|
||||
display: none;
|
||||
}
|
||||
.pulse {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0.7);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 10px rgba(255, 0, 0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="flex h-screen overflow-hidden">
|
||||
<!-- Sidebar -->
|
||||
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="content-area flex-1 overflow-y-auto">
|
||||
<!-- Top Navigation -->
|
||||
<header class="bg-white shadow-sm">
|
||||
<div class="flex items-center justify-between px-6 py-4">
|
||||
<div class="flex items-center">
|
||||
<h2 class="text-xl font-semibold text-gray-800">Receive Container</h2>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="relative">
|
||||
<span class="absolute top-0 right-0 -mt-1 -mr-1 flex h-4 w-4">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-4 w-4 bg-red-500 text-xs text-white justify-center items-center">3</span>
|
||||
</span>
|
||||
<button class="text-gray-500 hover:text-gray-700">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<span class="text-gray-700 font-medium">Gate North</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Container Expedition Content -->
|
||||
<div id="expediteContent" class="tab-content p-6">
|
||||
<div class="bg-white rounded-lg shadow mb-6">
|
||||
<div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800">Container Expedition</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">Process outgoing containers</p>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="bg-green-100 text-green-800 text-xs font-semibold px-3 py-1 rounded-full">Gate Open</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<form id="expediteForm" class="space-y-6">
|
||||
<div class="form-section grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div class="md:col-span-2">
|
||||
<label for="expediteContainerNumber" class="block text-sm font-medium text-gray-700 mb-1">Container Number</label>
|
||||
<div class="flex">
|
||||
<input type="text" id="expediteContainerNumber" name="expediteContainerNumber" placeholder="e.g. MSCU1234567" class="flex-1 px-4 py-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500" required>
|
||||
<button type="button" id="searchExpediteContainer" class="px-4 py-2 bg-blue-600 text-white rounded-r-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-gray-500">Enter the full container number including prefix</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="expeditionDate" class="block text-sm font-medium text-gray-700 mb-1">Expedition Date</label>
|
||||
<input type="date" id="expeditionDate" name="expeditionDate" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" required value="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Order Data (initially hidden) -->
|
||||
<div id="orderData" class="hidden">
|
||||
<div class="bg-green-50 border border-green-200 rounded-md p-4 mb-6">
|
||||
<div class="flex items-start">
|
||||
<div class="flex-shrink-0">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3 flex-1">
|
||||
<h3 class="text-sm font-medium text-green-800">Expedition Order Found</h3>
|
||||
<div class="mt-2 text-sm text-green-700">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p><span class="font-medium">Order ID:</span> <span id="orderID">ORD-2023-0015</span></p>
|
||||
<p><span class="font-medium">Line Operator:</span> <span id="orderOperator">Maersk Line</span></p>
|
||||
</div>
|
||||
<div>
|
||||
<p><span class="font-medium">Requested Pickup:</span> <span id="orderPickup">2023-06-20</span></p>
|
||||
<p><span class="font-medium">Transport Company:</span> <span id="orderTransport">ABC Trucking</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2"><span class="font-medium">Driver Info:</span> <span id="orderDriver">John Doe, +1 555-123-4567</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label for="expediteContainerType" class="block text-sm font-medium text-gray-700 mb-1">Container Type</label>
|
||||
<input type="text" id="expediteContainerType" name="expediteContainerType" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 bg-gray-100" value="40HC" readonly>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="expediteTruckInfo" class="block text-sm font-medium text-gray-700 mb-1">Truck Information</label>
|
||||
<input type="text" id="expediteTruckInfo" name="expediteTruckInfo" placeholder="License plate / Company" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<label for="driverIdentification" class="block text-sm font-medium text-gray-700 mb-1">Driver Identification</label>
|
||||
<input type="text" id="driverIdentification" name="driverIdentification" placeholder="Driver's license or ID number" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" required>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-3">Container Condition at Departure</label>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="flex items-center">
|
||||
<input type="radio" id="departureConditionGood" name="departureCondition" value="good" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" checked>
|
||||
<label for="departureConditionGood" class="ml-2 block text-sm text-gray-700">Good Condition</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input type="radio" id="departureConditionMinor" name="departureCondition" value="minor" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300">
|
||||
<label for="departureConditionMinor" class="ml-2 block text-sm text-gray-700">Minor Damage</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input type="radio" id="departureConditionMajor" name="departureCondition" value="major" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300">
|
||||
<label for="departureConditionMajor" class="ml-2 block text-sm text-gray-700">Major Damage</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-3">Container Images at Departure</label>
|
||||
<p class="text-sm text-gray-600 mb-3">Take photos of the container before it leaves the depot</p>
|
||||
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
|
||||
<div class="image-preview" id="expeditePreview1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="image-preview" id="expeditePreview2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" id="expediteTakePhoto" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
Take Photo
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<label for="expediteNotes" class="block text-sm font-medium text-gray-700 mb-1">Additional Notes</label>
|
||||
<textarea id="expediteNotes" name="expediteNotes" rows="3" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Any additional information about this expedition"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-section flex justify-end">
|
||||
<button type="submit" class="px-6 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
Complete Expedition
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow">
|
||||
<div class="px-6 py-4 border-b border-gray-200">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Pending Expedition Orders</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Order ID</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Container</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Line</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Pickup Date</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Transport</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">ORD-2023-0015</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">MSCU2468135</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">Maersk</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-20</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">ABC Trucking</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-blue-600">
|
||||
<button class="hover:text-blue-800">Process</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">ORD-2023-0014</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">Any 40HC</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">Maersk</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-19</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">XYZ Logistics</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-blue-600">
|
||||
<button class="hover:text-blue-800">Process</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">ORD-2023-0016</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">CMAU5678901</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">CMA CGM</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-21</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">Fast Transport</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-blue-600">
|
||||
<button class="hover:text-blue-800">Process</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% extends 'barrier/barrier-base.html' %}
|
||||
{% load static %}
|
||||
{% block content %}
|
||||
<form method="post">
|
||||
<h2 style="color: #a57d52; margin-bottom: 20px; text-align: center;">Container Expedition</h2>
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="booking_id" value="{{ booking.id }}">
|
||||
<input type="hidden" name="container_id" value="{{ container.id }}">
|
||||
<p>
|
||||
<label for="id_booking_number">Booking number:</label>
|
||||
<input type="text" name="booking_number" value="{{ booking.number }}" maxlength="11" required id="id_booking_number" readonly disabled>
|
||||
</p>
|
||||
{{ form.as_p }}
|
||||
<div >
|
||||
<button type="submit" >
|
||||
Expedite container
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Camera Overlay -->
|
||||
<div id="cameraOverlay" class="camera-overlay">
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="p-4 flex justify-between items-center">
|
||||
<h3 class="text-lg font-semibold text-white">Take Container Photo</h3>
|
||||
<button id="closeCamera" class="text-white hover:text-gray-300">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex-grow flex items-center justify-center p-4">
|
||||
<div class="relative">
|
||||
<div class="camera-frame w-full max-w-2xl aspect-[4/3] bg-gray-800 flex items-center justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
|
||||
<!-- Guide lines for container positioning -->
|
||||
<div class="absolute inset-0 border-2 border-dashed border-white opacity-50 m-8"></div>
|
||||
|
||||
<!-- Positioning guides -->
|
||||
<div class="absolute top-8 left-8 border-t-2 border-l-2 border-white w-8 h-8"></div>
|
||||
<div class="absolute top-8 right-8 border-t-2 border-r-2 border-white w-8 h-8"></div>
|
||||
<div class="absolute bottom-8 left-8 border-b-2 border-l-2 border-white w-8 h-8"></div>
|
||||
<div class="absolute bottom-8 right-8 border-b-2 border-r-2 border-white w-8 h-8"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 flex justify-center">
|
||||
<button id="capturePhoto" class="w-16 h-16 rounded-full bg-red-600 border-4 border-white flex items-center justify-center pulse">
|
||||
<div class="w-12 h-12 rounded-full bg-red-600"></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Set current date as default
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
document.getElementById('arrivalDate').value = today;
|
||||
document.getElementById('expeditionDate').value = today;
|
||||
|
||||
// Tab Navigation
|
||||
const receiveContent = document.getElementById('receiveContent');
|
||||
const expediteContent = document.getElementById('expediteContent');
|
||||
|
||||
const receiveNav = document.getElementById('receiveNav');
|
||||
const expediteNav = document.getElementById('expediteNav');
|
||||
|
||||
receiveNav.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Update content visibility
|
||||
receiveContent.classList.add('active');
|
||||
expediteContent.classList.remove('active');
|
||||
|
||||
// Update navigation styling
|
||||
document.querySelector('.nav-item.active').classList.remove('active');
|
||||
receiveNav.classList.add('active');
|
||||
|
||||
// Update header title
|
||||
document.querySelector('header h2').textContent = 'Receive Container';
|
||||
});
|
||||
|
||||
expediteNav.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Update content visibility
|
||||
receiveContent.classList.remove('active');
|
||||
expediteContent.classList.add('active');
|
||||
|
||||
// Update navigation styling
|
||||
document.querySelector('.nav-item.active').classList.remove('active');
|
||||
expediteNav.classList.add('active');
|
||||
|
||||
// Update header title
|
||||
document.querySelector('header h2').textContent = 'Expedite Container';
|
||||
});
|
||||
|
||||
// Container search functionality
|
||||
document.getElementById('searchContainer').addEventListener('click', function() {
|
||||
const containerNumber = document.getElementById('containerNumber').value;
|
||||
if (containerNumber) {
|
||||
document.getElementById('preinfoData').classList.remove('hidden');
|
||||
} else {
|
||||
alert('Please enter a container number');
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('searchExpediteContainer').addEventListener('click', function() {
|
||||
const containerNumber = document.getElementById('expediteContainerNumber').value;
|
||||
if (containerNumber) {
|
||||
document.getElementById('orderData').classList.remove('hidden');
|
||||
} else {
|
||||
alert('Please enter a container number');
|
||||
}
|
||||
});
|
||||
|
||||
// Camera functionality
|
||||
const cameraOverlay = document.getElementById('cameraOverlay');
|
||||
const takePhotoBtn = document.getElementById('takePhoto');
|
||||
const expediteTakePhotoBtn = document.getElementById('expediteTakePhoto');
|
||||
const closeCamera = document.getElementById('closeCamera');
|
||||
const capturePhoto = document.getElementById('capturePhoto');
|
||||
|
||||
let currentImageTarget = null;
|
||||
|
||||
function openCamera(imageTarget) {
|
||||
cameraOverlay.style.display = 'block';
|
||||
currentImageTarget = imageTarget;
|
||||
}
|
||||
|
||||
takePhotoBtn.addEventListener('click', function() {
|
||||
// Find the first empty preview slot
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
const previewEl = document.getElementById(`preview${i}`);
|
||||
if (!previewEl.classList.contains('has-image')) {
|
||||
openCamera(`preview${i}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expediteTakePhotoBtn.addEventListener('click', function() {
|
||||
// Find the first empty preview slot
|
||||
for (let i = 1; i <= 2; i++) {
|
||||
const previewEl = document.getElementById(`expeditePreview${i}`);
|
||||
if (!previewEl.classList.contains('has-image')) {
|
||||
openCamera(`expeditePreview${i}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
closeCamera.addEventListener('click', function() {
|
||||
cameraOverlay.style.display = 'none';
|
||||
});
|
||||
|
||||
capturePhoto.addEventListener('click', function() {
|
||||
if (currentImageTarget) {
|
||||
const previewEl = document.getElementById(currentImageTarget);
|
||||
|
||||
// Create a container SVG image
|
||||
const containerSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
containerSvg.setAttribute("class", "w-full h-full");
|
||||
containerSvg.setAttribute("viewBox", "0 0 120 120");
|
||||
|
||||
// Random container color
|
||||
const colors = ['#2563eb', '#059669', '#d97706', '#7c3aed'];
|
||||
const randomColor = colors[Math.floor(Math.random() * colors.length)];
|
||||
|
||||
// Create container body
|
||||
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
||||
rect.setAttribute("x", "10");
|
||||
rect.setAttribute("y", "20");
|
||||
rect.setAttribute("width", "100");
|
||||
rect.setAttribute("height", "80");
|
||||
rect.setAttribute("fill", randomColor);
|
||||
containerSvg.appendChild(rect);
|
||||
|
||||
// Add some container details
|
||||
const detail1 = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
||||
detail1.setAttribute("x", "20");
|
||||
detail1.setAttribute("y", "30");
|
||||
detail1.setAttribute("width", "80");
|
||||
detail1.setAttribute("height", "20");
|
||||
detail1.setAttribute("fill", shadeColor(randomColor, -20));
|
||||
containerSvg.appendChild(detail1);
|
||||
|
||||
const detail2 = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
||||
detail2.setAttribute("x", "20");
|
||||
detail2.setAttribute("y", "60");
|
||||
detail2.setAttribute("width", "80");
|
||||
detail2.setAttribute("height", "30");
|
||||
detail2.setAttribute("fill", shadeColor(randomColor, -20));
|
||||
containerSvg.appendChild(detail2);
|
||||
|
||||
// Clear the preview and add the new SVG
|
||||
previewEl.innerHTML = '';
|
||||
previewEl.appendChild(containerSvg);
|
||||
previewEl.classList.add('has-image');
|
||||
|
||||
// Close the camera
|
||||
cameraOverlay.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Helper function to darken/lighten colors
|
||||
function shadeColor(color, percent) {
|
||||
let R = parseInt(color.substring(1,3), 16);
|
||||
let G = parseInt(color.substring(3,5), 16);
|
||||
let B = parseInt(color.substring(5,7), 16);
|
||||
|
||||
R = parseInt(R * (100 + percent) / 100);
|
||||
G = parseInt(G * (100 + percent) / 100);
|
||||
B = parseInt(B * (100 + percent) / 100);
|
||||
|
||||
R = (R<255)?R:255;
|
||||
G = (G<255)?G:255;
|
||||
B = (B<255)?B:255;
|
||||
|
||||
R = Math.round(R);
|
||||
G = Math.round(G);
|
||||
B = Math.round(B);
|
||||
|
||||
const RR = ((R.toString(16).length==1)?"0"+R.toString(16):R.toString(16));
|
||||
const GG = ((G.toString(16).length==1)?"0"+G.toString(16):G.toString(16));
|
||||
const BB = ((B.toString(16).length==1)?"0"+B.toString(16):B.toString(16));
|
||||
|
||||
return "#"+RR+GG+BB;
|
||||
}
|
||||
|
||||
// Form submissions
|
||||
document.getElementById('receiveForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Check if container data is loaded
|
||||
if (document.getElementById('preinfoData').classList.contains('hidden')) {
|
||||
alert('Please search for a container first');
|
||||
return;
|
||||
}
|
||||
|
||||
// In a real app, this would send data to the server
|
||||
alert('Container reception completed successfully!');
|
||||
this.reset();
|
||||
document.getElementById('preinfoData').classList.add('hidden');
|
||||
|
||||
// Reset image previews
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
const previewEl = document.getElementById(`preview${i}`);
|
||||
previewEl.classList.remove('has-image');
|
||||
previewEl.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
// Add the first two images back
|
||||
document.getElementById('preview1').classList.add('has-image');
|
||||
document.getElementById('preview1').innerHTML = `
|
||||
<svg class="w-full h-full" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="10" y="20" width="100" height="80" fill="#2563eb" />
|
||||
<rect x="20" y="30" width="30" height="20" fill="#1e40af" />
|
||||
<rect x="70" y="30" width="30" height="20" fill="#1e40af" />
|
||||
<rect x="20" y="60" width="80" height="30" fill="#1e40af" />
|
||||
<circle cx="85" cy="45" r="8" fill="#ef4444" />
|
||||
</svg>
|
||||
`;
|
||||
|
||||
document.getElementById('preview2').classList.add('has-image');
|
||||
document.getElementById('preview2').innerHTML = `
|
||||
<svg class="w-full h-full" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="10" y="20" width="100" height="80" fill="#2563eb" />
|
||||
<rect x="20" y="30" width="80" height="20" fill="#1e40af" />
|
||||
<rect x="20" y="60" width="80" height="30" fill="#1e40af" />
|
||||
<path d="M85,40 Q95,50 85,60 Q75,50 85,40" fill="#ef4444" />
|
||||
</svg>
|
||||
`;
|
||||
});
|
||||
|
||||
document.getElementById('expediteForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Check if order data is loaded
|
||||
if (document.getElementById('orderData').classList.contains('hidden')) {
|
||||
alert('Please search for a container first');
|
||||
return;
|
||||
}
|
||||
|
||||
// In a real app, this would send data to the server
|
||||
alert('Container expedition completed successfully!');
|
||||
this.reset();
|
||||
document.getElementById('orderData').classList.add('hidden');
|
||||
|
||||
// Reset image previews
|
||||
for (let i = 1; i <= 2; i++) {
|
||||
const previewEl = document.getElementById(`expeditePreview${i}`);
|
||||
previewEl.classList.remove('has-image');
|
||||
previewEl.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'9498ddc2d702313f',t:'MTc0ODg4NzM5My4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>
|
||||
</html>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
@ -1,32 +1,25 @@
|
||||
{% extends 'employee-base.html' %}
|
||||
{% extends 'list-crud.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<table>
|
||||
<tr>
|
||||
<th>Preinfo ID</th>
|
||||
<th>Container №</th>
|
||||
<th>Container type</th>
|
||||
<th>Line</th>
|
||||
<th>Created on</th>
|
||||
<th>Created by</th>
|
||||
</tr>
|
||||
{% block table_header %}
|
||||
<th style="display: none;">Select</th>
|
||||
<th>Preinfo №</th>
|
||||
<th>Container №</th>
|
||||
<th>Container type</th>
|
||||
<th>Line</th>
|
||||
<th>Created on</th>
|
||||
<th>Created by</th>
|
||||
{% endblock table_header %}
|
||||
|
||||
{% for preinfo in preinfos %}
|
||||
<tr>
|
||||
<td>{{ preinfo.id }}</td>
|
||||
<td>{{ preinfo.container_number }}</td>
|
||||
<td>{{ preinfo.container_type }}</td>
|
||||
<td>{{ preinfo.line.short_name }}</td>
|
||||
<td>{{ preinfo.created_on }}</td>
|
||||
<td>{{ preinfo.created_by.username }}</td>
|
||||
<td>
|
||||
{# <a href="{% url 'preinfo_edit' preinfo.id %}">Edit</a> |#}
|
||||
{# <a href="{% url 'preinfo_delete' preinfo.id %}">Delete</a>#}
|
||||
</td>
|
||||
</tr>
|
||||
{% block table_data %}
|
||||
<td>{{ object.id }}</td>
|
||||
<td>{{ object.container_number }}</td>
|
||||
<td>{{ object.container_type }}</td>
|
||||
<td>{{ object.line.short_name }}</td>
|
||||
<td>{{ object.created_on }}</td>
|
||||
<td>{{ object.created_by.username }}</td>
|
||||
{% endblock %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
{% endblock content %}
|
||||
{% block modal_header %}
|
||||
<h2>Edit Preinfo</h2>
|
||||
{% endblock modal_header %}
|
||||
|
||||
Loading…
Reference in New Issue