Add IntelliJ IDEA project configuration files
This commit adds IntelliJ IDEA-specific configuration files for the project, including module setup, version control integration, inspection profiles, and workspace settings. These files facilitate development environment configuration for contributors using IntelliJ IDEA.master
parent
72e0fc963c
commit
daba5a8438
@ -0,0 +1,34 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.admin import UserAdmin
|
||||||
|
from .models import DepotUser
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(DepotUser)
|
||||||
|
class DepotUserAdmin(UserAdmin):
|
||||||
|
# Add your custom fields to the fieldsets
|
||||||
|
fieldsets = UserAdmin.fieldsets + (
|
||||||
|
('Additional Info', {'fields': (
|
||||||
|
'phone_number',
|
||||||
|
'company',
|
||||||
|
'line',
|
||||||
|
'company_permissions',
|
||||||
|
'new_field1',
|
||||||
|
'is_company_admin',
|
||||||
|
)}),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add fields to display in list view
|
||||||
|
list_display = UserAdmin.list_display + ('phone_number', 'company', 'line', 'is_company_admin')
|
||||||
|
|
||||||
|
# Add fields to the add form
|
||||||
|
add_fieldsets = UserAdmin.add_fieldsets + (
|
||||||
|
('Additional Info', {'fields': (
|
||||||
|
'email',
|
||||||
|
'phone_number',
|
||||||
|
'company',
|
||||||
|
'line',
|
||||||
|
'company_permissions',
|
||||||
|
'new_field1',
|
||||||
|
'is_company_admin',
|
||||||
|
)}),
|
||||||
|
)
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
from django.contrib.auth.backends import ModelBackend
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyUserBackend(ModelBackend):
|
||||||
|
def get_user_permissions(self, user_obj, obj=None):
|
||||||
|
if not user_obj.is_active or user_obj.is_anonymous:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
if user_obj.is_superuser:
|
||||||
|
return super().get_user_permissions(user_obj, obj)
|
||||||
|
|
||||||
|
perms = super().get_user_permissions(user_obj, obj)
|
||||||
|
if user_obj.company and user_obj.company.is_client:
|
||||||
|
# Filter permissions based on client company context
|
||||||
|
perms = {p for p in perms if p.startswith('accounts.client_')}
|
||||||
|
return perms
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.forms import AuthenticationForm
|
||||||
|
|
||||||
|
class LoginForm(AuthenticationForm):
|
||||||
|
field_order = ['username', 'password']
|
||||||
|
class Meta:
|
||||||
|
model = get_user_model()
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
from django.contrib.auth.models import AbstractUser
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class ClientPermission(models.Model):
|
||||||
|
class Meta:
|
||||||
|
managed = True
|
||||||
|
default_permissions = ()
|
||||||
|
permissions = (
|
||||||
|
('can_book_container', 'Can book container'),
|
||||||
|
('can_view_bookings', 'Can view bookings'),
|
||||||
|
('can_manage_company_users', 'Can manage company users'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DepotUser(AbstractUser):
|
||||||
|
|
||||||
|
phone_number = models.CharField(max_length=15, blank=True, null=True)
|
||||||
|
email = models.EmailField(unique=True, blank=False, null=False)
|
||||||
|
company = models.ForeignKey(
|
||||||
|
'common.CompanyModel',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='user_lines',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
line = models.ForeignKey(
|
||||||
|
'common.LinesModel',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='user_lines',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
company_permissions = models.ManyToManyField('ClientPermission')
|
||||||
|
new_field1 = models.BooleanField(default=False)
|
||||||
|
is_company_admin = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
def has_company_perm(self, perm_codename):
|
||||||
|
if self.is_superuser:
|
||||||
|
return True
|
||||||
|
return self.company_permissions.filter(
|
||||||
|
codename=perm_codename,
|
||||||
|
is_client_permission=self.company
|
||||||
|
).exists()
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from django.contrib.auth import views as auth_views
|
||||||
|
from accounts import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('login/', views.DepotLoginView.as_view(), name='login'),
|
||||||
|
path('relogin/', auth_views.logout_then_login, name='relogin'),
|
||||||
|
]
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
from django.contrib.auth.views import LoginView
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.views.generic import TemplateView, FormView
|
||||||
|
|
||||||
|
from accounts.forms import LoginForm
|
||||||
|
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
class DepotLoginView(LoginView):
|
||||||
|
template_name = 'registration/login.html'
|
||||||
|
# success_url = reverse_lazy('dashboard')
|
||||||
|
form_class = LoginForm
|
||||||
|
next_page = reverse_lazy('dashboard')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
# Generated by Django 5.2.3 on 2025-06-25 12:33
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("common", "0001_initial"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="BookingModel",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.BigAutoField(
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
verbose_name="ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("number", models.CharField(max_length=50, unique=True)),
|
|
||||||
("vehicles", models.CharField(blank=True, null=True)),
|
|
||||||
("container_count", models.IntegerField()),
|
|
||||||
("carrier", models.CharField(blank=True, max_length=100, null=True)),
|
|
||||||
("visible", models.BooleanField(default=True)),
|
|
||||||
("is_new", models.BooleanField(default=True)),
|
|
||||||
(
|
|
||||||
"container_number",
|
|
||||||
models.CharField(blank=True, max_length=11, null=True),
|
|
||||||
),
|
|
||||||
("vehicles_left", models.IntegerField(blank=True, null=True)),
|
|
||||||
("created_on", models.DateTimeField(auto_now_add=True)),
|
|
||||||
("created_by", models.IntegerField()),
|
|
||||||
("updated_on", models.DateTimeField(auto_now=True)),
|
|
||||||
("updated_by", models.IntegerField()),
|
|
||||||
(
|
|
||||||
"container_type",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="booking_container_types",
|
|
||||||
to="common.containertypemodel",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"line",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="booking_lines",
|
|
||||||
to="common.linesmodel",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,3 +1,14 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from django.views.generic import CreateView
|
||||||
|
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
class CreateBookingView(CreateView):
|
||||||
|
template_name = 'create-booking.html'
|
||||||
|
extra_context = {
|
||||||
|
'title': 'Create Booking',
|
||||||
|
'description': 'This is the create booking page.',
|
||||||
|
}
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
return render(request, self.template_name, self.extra_context)
|
||||||
@ -1,132 +0,0 @@
|
|||||||
# Generated by Django 5.2.3 on 2025-06-25 12:33
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = []
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="ContainerKindModel",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.BigAutoField(
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
verbose_name="ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("name", models.CharField(max_length=100, unique=True)),
|
|
||||||
("short_name", models.CharField(max_length=5, unique=True)),
|
|
||||||
("description", models.TextField(blank=True, null=True)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
"ordering": ["name"],
|
|
||||||
"abstract": False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="OperationModel",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.BigAutoField(
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
verbose_name="ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("name", models.CharField(max_length=100, unique=True)),
|
|
||||||
("short_name", models.CharField(max_length=5, unique=True)),
|
|
||||||
("description", models.TextField(blank=True, null=True)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
"ordering": ["name"],
|
|
||||||
"abstract": False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="PayerModel",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.BigAutoField(
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
verbose_name="ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("name", models.CharField(max_length=100, unique=True)),
|
|
||||||
("short_name", models.CharField(max_length=5, unique=True)),
|
|
||||||
("description", models.TextField(blank=True, null=True)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
"ordering": ["name"],
|
|
||||||
"abstract": False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="ContainerTypeModel",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.BigAutoField(
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
verbose_name="ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("name", models.CharField(max_length=6, unique=True)),
|
|
||||||
("length", models.IntegerField()),
|
|
||||||
("height", models.BooleanField()),
|
|
||||||
("deleted", models.BooleanField(default=False)),
|
|
||||||
(
|
|
||||||
"container_type",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="container_type_container_kinds",
|
|
||||||
to="common.containerkindmodel",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="LinesModel",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.BigAutoField(
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
verbose_name="ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("name", models.CharField(max_length=100, unique=True)),
|
|
||||||
("short_name", models.CharField(max_length=5, unique=True)),
|
|
||||||
("description", models.TextField(blank=True, null=True)),
|
|
||||||
(
|
|
||||||
"payer",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="line_payers",
|
|
||||||
to="common.payermodel",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
"ordering": ["name"],
|
|
||||||
"abstract": False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
from containers.models import Container
|
||||||
|
from preinfo.models import Preinfo
|
||||||
|
|
||||||
|
|
||||||
|
def filter_queryset_by_user(queryset, user):
|
||||||
|
"""
|
||||||
|
Filters the queryset based on the user's line or company.
|
||||||
|
If the user has a line, it filters by that line.
|
||||||
|
If the user has a company, it filters by all lines associated with that company.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if user.line:
|
||||||
|
return queryset.filter(line=user.line)
|
||||||
|
elif user.company:
|
||||||
|
company_lines = user.company.line_company.all()
|
||||||
|
return queryset.filter(line__in=company_lines)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
def get_preinfo_by_number(number):
|
||||||
|
"""
|
||||||
|
Retrieves a PreinfoModel instance by its container number.
|
||||||
|
Returns None if no matching PreinfoModel is found.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return Preinfo.objects.get(container_number=number, received=False)
|
||||||
|
except Preinfo.DoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_container_by_number(number):
|
||||||
|
"""
|
||||||
|
Retrieves a Container instance by its number.
|
||||||
|
Returns None if no matching Container is found.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return Container.objects.get(number=number, expedited=False)
|
||||||
|
except Container.DoesNotExist:
|
||||||
|
return None
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
from django.forms import ModelForm
|
||||||
|
|
||||||
|
from containers.models import Container
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerBaseForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Container
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class ContainerReceiveForm(ContainerBaseForm):
|
||||||
|
"""
|
||||||
|
Form for creating a new Container instance.
|
||||||
|
Inherits from ContainerBaseForm.
|
||||||
|
"""
|
||||||
|
class Meta(ContainerBaseForm.Meta):
|
||||||
|
fields = ['number', 'receive_vehicle', 'damages', 'heavy_damaged', 'position',]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerExpeditionForm(ContainerBaseForm):
|
||||||
|
"""
|
||||||
|
Form for updating an existing Container instance.
|
||||||
|
Inherits from ContainerBaseForm.
|
||||||
|
"""
|
||||||
|
class Meta(ContainerBaseForm.Meta):
|
||||||
|
exclude = ['created_on',
|
||||||
|
'created_by',
|
||||||
|
'deleted',
|
||||||
|
'deleted_on',
|
||||||
|
'deleted_by',
|
||||||
|
'received'] # Exclude fields that should not be set by the user
|
||||||
@ -1,112 +0,0 @@
|
|||||||
# Generated by Django 5.2.3 on 2025-06-25 12:33
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("booking", "0001_initial"),
|
|
||||||
("common", "0001_initial"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="Container",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.BigAutoField(
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
verbose_name="ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("number", models.CharField(max_length=11)),
|
|
||||||
("received_on", models.DateTimeField(auto_now_add=True)),
|
|
||||||
("received_by", models.IntegerField()),
|
|
||||||
(
|
|
||||||
"receive_vehicles",
|
|
||||||
models.CharField(blank=True, max_length=100, null=True),
|
|
||||||
),
|
|
||||||
("damages", models.TextField(blank=True, null=True)),
|
|
||||||
("heavy_damaged", models.BooleanField(default=False)),
|
|
||||||
("position", models.CharField(blank=True, max_length=100, null=True)),
|
|
||||||
("swept", models.BooleanField(default=False)),
|
|
||||||
("swept_on", models.DateTimeField(blank=True, null=True)),
|
|
||||||
("swept_by", models.IntegerField(blank=True, null=True)),
|
|
||||||
("washed", models.BooleanField(default=False)),
|
|
||||||
("washed_on", models.DateTimeField(blank=True, null=True)),
|
|
||||||
("washed_by", models.IntegerField(blank=True, null=True)),
|
|
||||||
("expedited", models.BooleanField(default=False)),
|
|
||||||
("expedited_on", models.DateTimeField(blank=True, null=True)),
|
|
||||||
("expedited_by", models.IntegerField(blank=True, null=True)),
|
|
||||||
(
|
|
||||||
"expedition_vehicle",
|
|
||||||
models.CharField(blank=True, max_length=100, null=True),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"booking",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="container_bookings",
|
|
||||||
to="booking.bookingmodel",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"container_type",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="container_container_types",
|
|
||||||
to="common.containertypemodel",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"line_id",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="container_lines",
|
|
||||||
to="common.linesmodel",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="ContainerHistory",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"container_ptr",
|
|
||||||
models.OneToOneField(
|
|
||||||
auto_created=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
parent_link=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
to="containers.container",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("operation_date", models.DateTimeField(auto_now_add=True)),
|
|
||||||
(
|
|
||||||
"container",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="history_containers",
|
|
||||||
to="containers.container",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"operation",
|
|
||||||
models.ForeignKey(
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="history_operations",
|
|
||||||
to="common.operationmodel",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
bases=("containers.container",),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
from django.urls import include, path
|
||||||
|
|
||||||
|
from containers.views import ContainerReceive, ContainerExpedition, ContainerSearchView
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('search', ContainerSearchView.as_view(), name='container_search'),
|
||||||
|
path('<int:pk>/', include([
|
||||||
|
path('receive', ContainerReceive.as_view(), name='container_receive'),
|
||||||
|
path('expedition', ContainerExpedition.as_view(), name='container_expedition'),
|
||||||
|
])),
|
||||||
|
]
|
||||||
@ -1,3 +1,98 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render, redirect
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.views import View
|
||||||
|
from django.views.generic import CreateView, UpdateView, FormView
|
||||||
|
|
||||||
|
from common.utils import get_container_by_number
|
||||||
|
from containers.forms import ContainerReceiveForm, ContainerExpeditionForm
|
||||||
|
from containers.models import Container
|
||||||
|
from preinfo.models import Preinfo
|
||||||
|
|
||||||
|
|
||||||
# Create your views here.
|
# 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):
|
||||||
|
number = request.POST.get('number')
|
||||||
|
container = get_container_by_number(number)
|
||||||
|
if container:
|
||||||
|
if container.booking:
|
||||||
|
next_url = request.POST.get('param')
|
||||||
|
return redirect(next_url, pk=container.booking.pk)
|
||||||
|
else:
|
||||||
|
return render(request, self.template_name, {'error': 'Booking not found'})
|
||||||
|
|
||||||
|
return render(request, self.template_name, {'error': 'Not found'})
|
||||||
|
|
||||||
|
|
||||||
|
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')
|
||||||
|
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'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
from django.forms import ModelForm
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
|
||||||
|
from preinfo.models import Preinfo
|
||||||
|
|
||||||
|
|
||||||
|
class PreinfoBaseForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Preinfo
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
# success_url = reverse_lazy('client_preinfo')
|
||||||
|
class PreinfoCreateForm(PreinfoBaseForm):
|
||||||
|
"""
|
||||||
|
Form for creating a new PreinfoModel instance.
|
||||||
|
Inherits from PreinfoBaseForm.
|
||||||
|
"""
|
||||||
|
class Meta(PreinfoBaseForm.Meta):
|
||||||
|
exclude = ['created_on',
|
||||||
|
'created_by',
|
||||||
|
'deleted',
|
||||||
|
'deleted_on',
|
||||||
|
'deleted_by',
|
||||||
|
'received'] # Exclude fields that should not be set by the user
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from preinfo.views import ClientPreinfoView, check_preinfo, PreinfoSearchView
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('client/', ClientPreinfoView.as_view(), name='client_preinfo'),
|
||||||
|
path('check-preinfo/', check_preinfo, name='check_preinfo'),
|
||||||
|
path('preinfo-search/', PreinfoSearchView.as_view(), name='preinfo_search'),
|
||||||
|
]
|
||||||
@ -1,3 +1,82 @@
|
|||||||
from django.shortcuts import render
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.forms import forms
|
||||||
|
from django.forms.widgets import HiddenInput
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from django.shortcuts import render, redirect
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.views import View
|
||||||
|
from django.views.generic import TemplateView, FormView, CreateView
|
||||||
|
|
||||||
# Create your views here.
|
from common.utils import filter_queryset_by_user, get_preinfo_by_number
|
||||||
|
from preinfo.forms import PreinfoBaseForm, PreinfoCreateForm
|
||||||
|
from preinfo.models import Preinfo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ClientPreinfoView(LoginRequiredMixin, CreateView):
|
||||||
|
template_name = 'client-preinfo-content.html'
|
||||||
|
form_class = PreinfoCreateForm
|
||||||
|
success_url = reverse_lazy('client_preinfo')
|
||||||
|
model = Preinfo
|
||||||
|
|
||||||
|
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
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_form(self, form_class=None):
|
||||||
|
form = super().get_form(form_class)
|
||||||
|
user = self.request.user
|
||||||
|
|
||||||
|
# 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.attrs['disabled'] = True
|
||||||
|
# Keep the value when form is submitted
|
||||||
|
# form.fields['line'].widget = HiddenInput()
|
||||||
|
|
||||||
|
return form
|
||||||
|
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.instance.created_by = self.request.user
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
# Check if a preinfo exists for the given container number
|
||||||
|
def check_preinfo(request):
|
||||||
|
number = request.GET.get('number')
|
||||||
|
preinfo = Preinfo.objects.filter(container_number=number, received=False).first()
|
||||||
|
if preinfo:
|
||||||
|
return JsonResponse({
|
||||||
|
'found': True,
|
||||||
|
'line': preinfo.line.name,
|
||||||
|
'container_type': preinfo.container_type.name,
|
||||||
|
'preinfo_id': preinfo.id,
|
||||||
|
})
|
||||||
|
return JsonResponse({'found': False})
|
||||||
|
|
||||||
|
|
||||||
|
class PreinfoSearchView(View):
|
||||||
|
template_name = 'container-search.html'
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
return render(request, self.template_name)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
number = request.POST.get('number')
|
||||||
|
|
||||||
|
preinfo = get_preinfo_by_number(number)
|
||||||
|
if preinfo:
|
||||||
|
next_url = request.POST.get('param')
|
||||||
|
return redirect(next_url, pk=preinfo.pk)
|
||||||
|
|
||||||
|
return render(request, self.template_name, {'error': 'Not found'})
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
/* === Layout === */
|
||||||
|
.layout-root {
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Sidebar === */
|
||||||
|
.sidebar {
|
||||||
|
width: 16rem;
|
||||||
|
height: 100%;
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: linear-gradient(180deg, #0f4c81 0%, #1a6baf 100%);
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
.sidebar-header {
|
||||||
|
padding: 1.25rem;
|
||||||
|
border-bottom: 1px solid #2b6cb0;
|
||||||
|
}
|
||||||
|
.sidebar-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.sidebar-subtitle {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #bfdbfe;
|
||||||
|
}
|
||||||
|
.nav-section {
|
||||||
|
padding: 1rem 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.nav-label {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #93c5fd;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
.nav-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
color: white;
|
||||||
|
transition: all 0.2s;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
.nav-icon {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
}
|
||||||
|
.user-profile {
|
||||||
|
padding: 1rem;
|
||||||
|
border-top: 1px solid #2b6cb0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.user-avatar {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
border-radius: 9999px;
|
||||||
|
background-color: #3b82f6;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: 700;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.user-details {
|
||||||
|
margin-left: 0.75rem;
|
||||||
|
}
|
||||||
|
.user-name {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.user-role {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #bfdbfe;
|
||||||
|
}
|
||||||
|
.logout-button {
|
||||||
|
margin-left: auto;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
@ -0,0 +1,124 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Line Operator Dashboard | 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(-5px);
|
||||||
|
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); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% load static %}
|
||||||
|
<div 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>
|
||||||
|
|
||||||
|
<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 'barrier_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 'preinfo_search' %}?param=container_receive" 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>
|
||||||
|
Receive container
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'container_search' %}?param=container_expedition" 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>
|
||||||
|
Expedite container
|
||||||
|
</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>
|
||||||
|
Photos
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="px-4 py-2 mt-6 text-xs text-blue-300 uppercase tracking-wider">Account</div>
|
||||||
|
{# <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="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#}
|
||||||
|
{# </a>#}
|
||||||
|
</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">Maersk Line</p>
|
||||||
|
<p class="text-xs text-blue-300">Line Operator</p>
|
||||||
|
</div>
|
||||||
|
<button 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>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,232 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Line Operator Dashboard | Container Depot</title>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
.content-area {
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
.nav-item {
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
.card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
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); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="layout-root">
|
||||||
|
<aside class="sidebar">
|
||||||
|
<div class="sidebar-header">
|
||||||
|
<h1 class="sidebar-title">Container Depot</h1>
|
||||||
|
<p class="sidebar-subtitle">Line Operator Portal</p>
|
||||||
|
</div>
|
||||||
|
<nav class="nav-section">
|
||||||
|
<div class="nav-label">Main</div>
|
||||||
|
<a href="#" class="nav-item active">
|
||||||
|
<svg class="nav-icon" xmlns="http://www.w3.org/2000/svg" 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>
|
||||||
|
<!-- Repeat for other nav items like Container Preinfo, Expedition Orders, Reports, etc. -->
|
||||||
|
</nav>
|
||||||
|
<div class="user-profile">
|
||||||
|
<div class="user-avatar">LO</div>
|
||||||
|
<div class="user-details">
|
||||||
|
<p class="user-name">Maersk Line</p>
|
||||||
|
<p class="user-role">Line Operator</p>
|
||||||
|
</div>
|
||||||
|
<button class="logout-button">
|
||||||
|
<svg class="nav-icon" xmlns="http://www.w3.org/2000/svg" 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>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<!-- Main content layout will continue here... -->
|
||||||
|
<main class="content-area">
|
||||||
|
<header class="topbar">
|
||||||
|
<div class="topbar-inner">
|
||||||
|
<div class="topbar-title">
|
||||||
|
<h2>Dashboard</h2>
|
||||||
|
</div>
|
||||||
|
<div class="topbar-actions">
|
||||||
|
<button class="topbar-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="topbar-icon" 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>
|
||||||
|
<button class="topbar-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="topbar-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<!-- More dashboard content will follow -->
|
||||||
|
<section class="dashboard-overview">
|
||||||
|
<div class="dashboard-cards">
|
||||||
|
<div class="card card-blue">
|
||||||
|
<div class="card-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="card-icon-inner" 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="card-details">
|
||||||
|
<h3 class="card-title">Active Containers</h3>
|
||||||
|
<p class="card-number">42</p>
|
||||||
|
<p class="card-note green">+3 since last week</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<div class="card-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="card-icon-inner" 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="card-details">
|
||||||
|
<h3 class="card-title">Preinfo Sent</h3>
|
||||||
|
<p class="card-number">18</p>
|
||||||
|
<p class="card-note green">+5 since last week</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-orange">
|
||||||
|
<div class="card-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="card-icon-inner" 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="card-details">
|
||||||
|
<h3 class="card-title">Pending Orders</h3>
|
||||||
|
<p class="card-number">7</p>
|
||||||
|
<p class="card-note orange">2 require attention</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="dashboard-tables">
|
||||||
|
<div class="table-container">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3>Recent Container Activity</h3>
|
||||||
|
</div>
|
||||||
|
<table class="data-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="badge badge-green">Received</span></td>
|
||||||
|
<td>2023-06-15</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>MSCU7654321</td>
|
||||||
|
<td>20DV</td>
|
||||||
|
<td><span class="badge badge-blue">Preinfo</span></td>
|
||||||
|
<td>2023-06-14</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>MSCU2468135</td>
|
||||||
|
<td>40DV</td>
|
||||||
|
<td><span class="badge badge-orange">Order</span></td>
|
||||||
|
<td>2023-06-13</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>MSCU1357924</td>
|
||||||
|
<td>20RF</td>
|
||||||
|
<td><span class="badge badge-red">Expedited</span></td>
|
||||||
|
<td>2023-06-12</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<div class="table-header">
|
||||||
|
<h3>Payment Status</h3>
|
||||||
|
</div>
|
||||||
|
<table class="data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Invoice</th>
|
||||||
|
<th>Amount</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Due Date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>INV-2023-0042</td>
|
||||||
|
<td>$1,250.00</td>
|
||||||
|
<td><span class="badge badge-green">Paid</span></td>
|
||||||
|
<td>2023-06-10</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>INV-2023-0041</td>
|
||||||
|
<td>$875.50</td>
|
||||||
|
<td><span class="badge badge-yellow">Pending</span></td>
|
||||||
|
<td>2023-06-20</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>INV-2023-0040</td>
|
||||||
|
<td>$2,100.00</td>
|
||||||
|
<td><span class="badge badge-red">Overdue</span></td>
|
||||||
|
<td>2023-06-05</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>INV-2023-0039</td>
|
||||||
|
<td>$950.25</td>
|
||||||
|
<td><span class="badge badge-green">Paid</span></td>
|
||||||
|
<td>2023-05-28</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@ -0,0 +1,194 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Line Operator Dashboard | 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(-5px);
|
||||||
|
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); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="flex h-screen overflow-hidden">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
{% include 'sidebar.html' %}
|
||||||
|
<!-- 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">Dashboard</h2>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<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>
|
||||||
|
<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="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Dashboard Content -->
|
||||||
|
<div class="p-6">
|
||||||
|
{% block content %}
|
||||||
|
{# {% include 'client-dashboard-content.html' %}#}
|
||||||
|
{# {% include 'client-preinfo-content.html' %}#}
|
||||||
|
{# {% include 'client-orders-content.html' %}#}
|
||||||
|
{% endblock content %}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{# <script>#}
|
||||||
|
{# // Tab Navigation#}
|
||||||
|
{# const dashboardContent = document.getElementById('dashboardContent');#}
|
||||||
|
{# const preinfoContent = document.getElementById('preinfoContent');#}
|
||||||
|
{# const ordersContent = document.getElementById('ordersContent');#}
|
||||||
|
{# #}
|
||||||
|
{# const preinfoNav = document.getElementById('preinfoNav');#}
|
||||||
|
{# const ordersNav = document.getElementById('ordersNav');#}
|
||||||
|
{# #}
|
||||||
|
{# preinfoNav.addEventListener('click', function(e) {#}
|
||||||
|
{# e.preventDefault();#}
|
||||||
|
{# #}
|
||||||
|
{# // Update content visibility#}
|
||||||
|
{# dashboardContent.classList.remove('active');#}
|
||||||
|
{# preinfoContent.classList.add('active');#}
|
||||||
|
{# ordersContent.classList.remove('active');#}
|
||||||
|
{# #}
|
||||||
|
{# // Update navigation styling#}
|
||||||
|
{# document.querySelector('.nav-item.active').classList.remove('active');#}
|
||||||
|
{# preinfoNav.classList.add('active');#}
|
||||||
|
{# #}
|
||||||
|
{# // Update header title#}
|
||||||
|
{# document.querySelector('header h2').textContent = 'Container Preinfo';#}
|
||||||
|
{# });#}
|
||||||
|
{# #}
|
||||||
|
{# ordersNav.addEventListener('click', function(e) {#}
|
||||||
|
{# e.preventDefault();#}
|
||||||
|
{# #}
|
||||||
|
{# // Update content visibility#}
|
||||||
|
{# dashboardContent.classList.remove('active');#}
|
||||||
|
{# preinfoContent.classList.remove('active');#}
|
||||||
|
{# ordersContent.classList.add('active');#}
|
||||||
|
{# #}
|
||||||
|
{# // Update navigation styling#}
|
||||||
|
{# document.querySelector('.nav-item.active').classList.remove('active');#}
|
||||||
|
{# ordersNav.classList.add('active');#}
|
||||||
|
{# #}
|
||||||
|
{# // Update header title#}
|
||||||
|
{# document.querySelector('header h2').textContent = 'Expedition Orders';#}
|
||||||
|
{# });#}
|
||||||
|
{# #}
|
||||||
|
{# // Damage checkbox toggle#}
|
||||||
|
{# const damageCheck = document.getElementById('damageCheck');#}
|
||||||
|
{# const damageDetails = document.getElementById('damageDetails');#}
|
||||||
|
{# #}
|
||||||
|
{# damageCheck.addEventListener('change', function() {#}
|
||||||
|
{# if (this.checked) {#}
|
||||||
|
{# damageDetails.classList.remove('hidden');#}
|
||||||
|
{# } else {#}
|
||||||
|
{# damageDetails.classList.add('hidden');#}
|
||||||
|
{# }#}
|
||||||
|
{# });#}
|
||||||
|
{# #}
|
||||||
|
{# // Order type toggle#}
|
||||||
|
{# const specificContainer = document.getElementById('specificContainer');#}
|
||||||
|
{# const anyContainer = document.getElementById('anyContainer');#}
|
||||||
|
{# const specificContainerFields = document.getElementById('specificContainerFields');#}
|
||||||
|
{# const anyContainerFields = document.getElementById('anyContainerFields');#}
|
||||||
|
{# #}
|
||||||
|
{# specificContainer.addEventListener('change', function() {#}
|
||||||
|
{# if (this.checked) {#}
|
||||||
|
{# specificContainerFields.classList.remove('hidden');#}
|
||||||
|
{# anyContainerFields.classList.add('hidden');#}
|
||||||
|
{# }#}
|
||||||
|
{# });#}
|
||||||
|
{# #}
|
||||||
|
{# anyContainer.addEventListener('change', function() {#}
|
||||||
|
{# if (this.checked) {#}
|
||||||
|
{# specificContainerFields.classList.add('hidden');#}
|
||||||
|
{# anyContainerFields.classList.remove('hidden');#}
|
||||||
|
{# }#}
|
||||||
|
{# });#}
|
||||||
|
{# #}
|
||||||
|
{# // Form submissions#}
|
||||||
|
{# document.getElementById('preinfoForm').addEventListener('submit', function(e) {#}
|
||||||
|
{# e.preventDefault();#}
|
||||||
|
{# #}
|
||||||
|
{# // In a real app, this would send data to the server#}
|
||||||
|
{# alert('Preinfo submitted successfully!');#}
|
||||||
|
{# this.reset();#}
|
||||||
|
{# });#}
|
||||||
|
{# #}
|
||||||
|
{# document.getElementById('orderForm').addEventListener('submit', function(e) {#}
|
||||||
|
{# e.preventDefault();#}
|
||||||
|
{# #}
|
||||||
|
{# // In a real app, this would send data to the server#}
|
||||||
|
{# alert('Expedition order submitted successfully!');#}
|
||||||
|
{# this.reset();#}
|
||||||
|
{# });#}
|
||||||
|
{# #}
|
||||||
|
{# // Add more container button#}
|
||||||
|
{# document.getElementById('addMoreContainer').addEventListener('click', function() {#}
|
||||||
|
{# alert('In a real app, this would add fields for another container');#}
|
||||||
|
{# });#}
|
||||||
|
{# </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:'947f86ac452d3130',t:'MTc0ODYyMTY4Mi4wMDAwMDA='};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>#}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
{% extends 'client-base.html' %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="preinfoContent" class="tab-content active">
|
||||||
|
<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">Submit Container Preinfo</h3>
|
||||||
|
<p class="text-sm text-gray-600 mt-1">Provide information about containers that will arrive at the depot</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
<form id="preinfoForm" class="space-y-6" method="POST" >
|
||||||
|
|
||||||
|
{{ form.as_p }}
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<div class="form-section flex justify-end space-x-3">
|
||||||
|
<button type="submit" 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">
|
||||||
|
Submit Preinfo
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 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 Preinfo Submissions</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">Est. Arrival</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">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-gray-200">
|
||||||
|
{% for preinfo in recent %}
|
||||||
|
<tr>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">{{ preinfo.container_number }}</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">{{ preinfo.container_type }}</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">{{ preinfo.created_on }}</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">Pending</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">
|
||||||
|
<button class="text-blue-600 hover:text-blue-800 mr-3">Edit</button>
|
||||||
|
<button class="text-red-600 hover:text-red-800">Cancel</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
||||||
@ -0,0 +1,162 @@
|
|||||||
|
{% 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 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">Pending Orders</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>
|
||||||
|
{% endblock %}
|
||||||
@ -1,689 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Line Operator Dashboard | 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(-5px);
|
|
||||||
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); }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body class="flex h-screen overflow-hidden">
|
|
||||||
<!-- Sidebar -->
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<nav class="flex-grow py-4">
|
|
||||||
<div class="px-4 py-2 text-xs text-blue-300 uppercase tracking-wider">Main</div>
|
|
||||||
<a href="#" 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="#" 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="#" 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>
|
|
||||||
Expedition Orders
|
|
||||||
</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>
|
|
||||||
|
|
||||||
<div class="px-4 py-2 mt-6 text-xs text-blue-300 uppercase tracking-wider">Account</div>
|
|
||||||
<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="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
|
|
||||||
</a>
|
|
||||||
</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">Maersk Line</p>
|
|
||||||
<p class="text-xs text-blue-300">Line Operator</p>
|
|
||||||
</div>
|
|
||||||
<button 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>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<!-- 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">Dashboard</h2>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center space-x-4">
|
|
||||||
<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>
|
|
||||||
<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="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<!-- Dashboard Content -->
|
|
||||||
<div class="p-6">
|
|
||||||
<!-- Dashboard Overview -->
|
|
||||||
<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">Pending Orders</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>
|
|
||||||
|
|
||||||
<!-- Container Preinfo Form -->
|
|
||||||
<div id="preinfoContent" class="tab-content">
|
|
||||||
<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">Submit Container Preinfo</h3>
|
|
||||||
<p class="text-sm text-gray-600 mt-1">Provide information about containers that will arrive at the depot</p>
|
|
||||||
</div>
|
|
||||||
<div class="p-6">
|
|
||||||
<form id="preinfoForm" class="space-y-6">
|
|
||||||
<div class="form-section grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
<div>
|
|
||||||
<label for="containerNumber" class="block text-sm font-medium text-gray-700 mb-1">Container Number</label>
|
|
||||||
<input type="text" id="containerNumber" name="containerNumber" placeholder="e.g. MSCU1234567" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" required>
|
|
||||||
<p class="mt-1 text-xs text-gray-500">Enter the full container number including prefix</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label for="containerType" class="block text-sm font-medium text-gray-700 mb-1">Container Type</label>
|
|
||||||
<select id="containerType" name="containerType" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" required>
|
|
||||||
<option value="">Select container type</option>
|
|
||||||
<option value="20DV">20' Dry Van (20DV)</option>
|
|
||||||
<option value="40DV">40' Dry Van (40DV)</option>
|
|
||||||
<option value="40HC">40' High Cube (40HC)</option>
|
|
||||||
<option value="20RF">20' Reefer (20RF)</option>
|
|
||||||
<option value="40RF">40' Reefer (40RF)</option>
|
|
||||||
<option value="20OT">20' Open Top (20OT)</option>
|
|
||||||
<option value="40OT">40' Open Top (40OT)</option>
|
|
||||||
<option value="20FR">20' Flat Rack (20FR)</option>
|
|
||||||
<option value="40FR">40' Flat Rack (40FR)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-section grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
<div>
|
|
||||||
<label for="estimatedArrival" class="block text-sm font-medium text-gray-700 mb-1">Estimated Arrival Date</label>
|
|
||||||
<input type="date" id="estimatedArrival" name="estimatedArrival" 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>
|
|
||||||
<label for="vesselVoyage" class="block text-sm font-medium text-gray-700 mb-1">Vessel/Voyage</label>
|
|
||||||
<input type="text" id="vesselVoyage" name="vesselVoyage" placeholder="e.g. MAERSK SEVILLE / 123E" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-section">
|
|
||||||
<label for="additionalInfo" class="block text-sm font-medium text-gray-700 mb-1">Additional Information</label>
|
|
||||||
<textarea id="additionalInfo" name="additionalInfo" 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 special handling instructions or notes"></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-section flex items-center">
|
|
||||||
<input type="checkbox" id="damageCheck" name="damageCheck" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
|
||||||
<label for="damageCheck" class="ml-2 block text-sm text-gray-700">Container has visible damage</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="damageDetails" class="form-section hidden">
|
|
||||||
<label for="damageDescription" class="block text-sm font-medium text-gray-700 mb-1">Damage Description</label>
|
|
||||||
<textarea id="damageDescription" name="damageDescription" 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="Describe the damage in detail"></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-section flex justify-end space-x-3">
|
|
||||||
<button type="button" id="addMoreContainer" class="px-4 py-2 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500">
|
|
||||||
Add Another Container
|
|
||||||
</button>
|
|
||||||
<button type="submit" 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">
|
|
||||||
Submit Preinfo
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-6 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 Preinfo Submissions</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">Est. Arrival</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">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">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 text-sm text-gray-700">2023-06-18</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">Pending</span>
|
|
||||||
</td>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">
|
|
||||||
<button class="text-blue-600 hover:text-blue-800 mr-3">Edit</button>
|
|
||||||
<button class="text-red-600 hover:text-red-800">Cancel</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU9876543</td>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">40HC</td>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-17</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">
|
|
||||||
<button class="text-blue-600 hover:text-blue-800 mr-3">View</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU1122334</td>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">20RF</td>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-16</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">Delayed</span>
|
|
||||||
</td>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">
|
|
||||||
<button class="text-blue-600 hover:text-blue-800 mr-3">Edit</button>
|
|
||||||
<button class="text-red-600 hover:text-red-800">Cancel</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Expedition Orders Form -->
|
|
||||||
<div id="ordersContent" class="tab-content">
|
|
||||||
<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">Create Expedition Order</h3>
|
|
||||||
<p class="text-sm text-gray-600 mt-1">Request containers to be expedited from the depot</p>
|
|
||||||
</div>
|
|
||||||
<div class="p-6">
|
|
||||||
<form id="orderForm" class="space-y-6">
|
|
||||||
<div class="form-section">
|
|
||||||
<label for="orderType" class="block text-sm font-medium text-gray-700 mb-1">Order Type</label>
|
|
||||||
<div class="flex space-x-4">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<input type="radio" id="specificContainer" name="orderType" value="specific" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" checked>
|
|
||||||
<label for="specificContainer" class="ml-2 block text-sm text-gray-700">Specific Container</label>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<input type="radio" id="anyContainer" name="orderType" value="any" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300">
|
|
||||||
<label for="anyContainer" class="ml-2 block text-sm text-gray-700">Any Available Container</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="specificContainerFields" class="form-section">
|
|
||||||
<label for="containerNumber" class="block text-sm font-medium text-gray-700 mb-1">Container Number</label>
|
|
||||||
<input type="text" id="orderContainerNumber" name="containerNumber" placeholder="e.g. MSCU1234567" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" required>
|
|
||||||
<p class="mt-1 text-xs text-gray-500">Enter the full container number including prefix</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="anyContainerFields" class="form-section hidden grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
<div>
|
|
||||||
<label for="containerType" class="block text-sm font-medium text-gray-700 mb-1">Container Type</label>
|
|
||||||
<select id="orderContainerType" name="containerType" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
||||||
<option value="">Select container type</option>
|
|
||||||
<option value="20DV">20' Dry Van (20DV)</option>
|
|
||||||
<option value="40DV">40' Dry Van (40DV)</option>
|
|
||||||
<option value="40HC">40' High Cube (40HC)</option>
|
|
||||||
<option value="20RF">20' Reefer (20RF)</option>
|
|
||||||
<option value="40RF">40' Reefer (40RF)</option>
|
|
||||||
<option value="20OT">20' Open Top (20OT)</option>
|
|
||||||
<option value="40OT">40' Open Top (40OT)</option>
|
|
||||||
<option value="20FR">20' Flat Rack (20FR)</option>
|
|
||||||
<option value="40FR">40' Flat Rack (40FR)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label for="containerCondition" class="block text-sm font-medium text-gray-700 mb-1">Container Condition</label>
|
|
||||||
<select id="containerCondition" name="containerCondition" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
||||||
<option value="any">Any Condition</option>
|
|
||||||
<option value="new">New / Like New</option>
|
|
||||||
<option value="good">Good Condition</option>
|
|
||||||
<option value="acceptable">Acceptable</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-section grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
<div>
|
|
||||||
<label for="pickupDate" class="block text-sm font-medium text-gray-700 mb-1">Pickup Date</label>
|
|
||||||
<input type="date" id="pickupDate" name="pickupDate" 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>
|
|
||||||
<label for="transportCompany" class="block text-sm font-medium text-gray-700 mb-1">Transport Company</label>
|
|
||||||
<input type="text" id="transportCompany" name="transportCompany" placeholder="e.g. ABC Trucking" 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="driverInfo" class="block text-sm font-medium text-gray-700 mb-1">Driver Information</label>
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
<input type="text" id="driverName" name="driverName" placeholder="Driver Name" class="px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
||||||
<input type="text" id="driverPhone" name="driverPhone" placeholder="Driver Phone" class="px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-section">
|
|
||||||
<label for="orderNotes" class="block text-sm font-medium text-gray-700 mb-1">Special Instructions</label>
|
|
||||||
<textarea id="orderNotes" name="orderNotes" 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 special handling instructions or notes"></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-section flex justify-end">
|
|
||||||
<button type="submit" 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">
|
|
||||||
Submit Order
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-6 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">Active 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">Pickup Date</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">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">2023-06-20</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">Approved</span>
|
|
||||||
</td>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">
|
|
||||||
<button class="text-blue-600 hover:text-blue-800 mr-3">View</button>
|
|
||||||
<button class="text-red-600 hover:text-red-800">Cancel</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">2023-06-19</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">
|
|
||||||
<button class="text-blue-600 hover:text-blue-800 mr-3">View</button>
|
|
||||||
<button class="text-red-600 hover:text-red-800">Cancel</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">ORD-2023-0013</td>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">MSCU1357924</td>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-15</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">Completed</span>
|
|
||||||
</td>
|
|
||||||
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">
|
|
||||||
<button class="text-blue-600 hover:text-blue-800">View</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Tab Navigation
|
|
||||||
const dashboardContent = document.getElementById('dashboardContent');
|
|
||||||
const preinfoContent = document.getElementById('preinfoContent');
|
|
||||||
const ordersContent = document.getElementById('ordersContent');
|
|
||||||
|
|
||||||
const preinfoNav = document.getElementById('preinfoNav');
|
|
||||||
const ordersNav = document.getElementById('ordersNav');
|
|
||||||
|
|
||||||
preinfoNav.addEventListener('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// Update content visibility
|
|
||||||
dashboardContent.classList.remove('active');
|
|
||||||
preinfoContent.classList.add('active');
|
|
||||||
ordersContent.classList.remove('active');
|
|
||||||
|
|
||||||
// Update navigation styling
|
|
||||||
document.querySelector('.nav-item.active').classList.remove('active');
|
|
||||||
preinfoNav.classList.add('active');
|
|
||||||
|
|
||||||
// Update header title
|
|
||||||
document.querySelector('header h2').textContent = 'Container Preinfo';
|
|
||||||
});
|
|
||||||
|
|
||||||
ordersNav.addEventListener('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// Update content visibility
|
|
||||||
dashboardContent.classList.remove('active');
|
|
||||||
preinfoContent.classList.remove('active');
|
|
||||||
ordersContent.classList.add('active');
|
|
||||||
|
|
||||||
// Update navigation styling
|
|
||||||
document.querySelector('.nav-item.active').classList.remove('active');
|
|
||||||
ordersNav.classList.add('active');
|
|
||||||
|
|
||||||
// Update header title
|
|
||||||
document.querySelector('header h2').textContent = 'Expedition Orders';
|
|
||||||
});
|
|
||||||
|
|
||||||
// Damage checkbox toggle
|
|
||||||
const damageCheck = document.getElementById('damageCheck');
|
|
||||||
const damageDetails = document.getElementById('damageDetails');
|
|
||||||
|
|
||||||
damageCheck.addEventListener('change', function() {
|
|
||||||
if (this.checked) {
|
|
||||||
damageDetails.classList.remove('hidden');
|
|
||||||
} else {
|
|
||||||
damageDetails.classList.add('hidden');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Order type toggle
|
|
||||||
const specificContainer = document.getElementById('specificContainer');
|
|
||||||
const anyContainer = document.getElementById('anyContainer');
|
|
||||||
const specificContainerFields = document.getElementById('specificContainerFields');
|
|
||||||
const anyContainerFields = document.getElementById('anyContainerFields');
|
|
||||||
|
|
||||||
specificContainer.addEventListener('change', function() {
|
|
||||||
if (this.checked) {
|
|
||||||
specificContainerFields.classList.remove('hidden');
|
|
||||||
anyContainerFields.classList.add('hidden');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
anyContainer.addEventListener('change', function() {
|
|
||||||
if (this.checked) {
|
|
||||||
specificContainerFields.classList.add('hidden');
|
|
||||||
anyContainerFields.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Form submissions
|
|
||||||
document.getElementById('preinfoForm').addEventListener('submit', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// In a real app, this would send data to the server
|
|
||||||
alert('Preinfo submitted successfully!');
|
|
||||||
this.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('orderForm').addEventListener('submit', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// In a real app, this would send data to the server
|
|
||||||
alert('Expedition order submitted successfully!');
|
|
||||||
this.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add more container button
|
|
||||||
document.getElementById('addMoreContainer').addEventListener('click', function() {
|
|
||||||
alert('In a real app, this would add fields for another container');
|
|
||||||
});
|
|
||||||
</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:'947f86ac452d3130',t:'MTc0ODYyMTY4Mi4wMDAwMDA='};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>
|
|
||||||
@ -0,0 +1,151 @@
|
|||||||
|
{% extends 'client-base.html' %}
|
||||||
|
{% block content %}
|
||||||
|
<div id="ordersContent" class="tab-content">
|
||||||
|
<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">Create Expedition Order</h3>
|
||||||
|
<p class="text-sm text-gray-600 mt-1">Request containers to be expedited from the depot</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
<form id="orderForm" class="space-y-6">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="orderType" class="block text-sm font-medium text-gray-700 mb-1">Order Type</label>
|
||||||
|
<div class="flex space-x-4">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<input type="radio" id="specificContainer" name="orderType" value="specific" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" checked>
|
||||||
|
<label for="specificContainer" class="ml-2 block text-sm text-gray-700">Specific Container</label>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<input type="radio" id="anyContainer" name="orderType" value="any" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300">
|
||||||
|
<label for="anyContainer" class="ml-2 block text-sm text-gray-700">Any Available Container</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="specificContainerFields" class="form-section">
|
||||||
|
<label for="containerNumber" class="block text-sm font-medium text-gray-700 mb-1">Container Number</label>
|
||||||
|
<input type="text" id="orderContainerNumber" name="containerNumber" placeholder="e.g. MSCU1234567" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" required>
|
||||||
|
<p class="mt-1 text-xs text-gray-500">Enter the full container number including prefix</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="anyContainerFields" class="form-section hidden grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label for="containerType" class="block text-sm font-medium text-gray-700 mb-1">Container Type</label>
|
||||||
|
<select id="orderContainerType" name="containerType" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||||
|
<option value="">Select container type</option>
|
||||||
|
<option value="20DV">20' Dry Van (20DV)</option>
|
||||||
|
<option value="40DV">40' Dry Van (40DV)</option>
|
||||||
|
<option value="40HC">40' High Cube (40HC)</option>
|
||||||
|
<option value="20RF">20' Reefer (20RF)</option>
|
||||||
|
<option value="40RF">40' Reefer (40RF)</option>
|
||||||
|
<option value="20OT">20' Open Top (20OT)</option>
|
||||||
|
<option value="40OT">40' Open Top (40OT)</option>
|
||||||
|
<option value="20FR">20' Flat Rack (20FR)</option>
|
||||||
|
<option value="40FR">40' Flat Rack (40FR)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="containerCondition" class="block text-sm font-medium text-gray-700 mb-1">Container Condition</label>
|
||||||
|
<select id="containerCondition" name="containerCondition" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||||
|
<option value="any">Any Condition</option>
|
||||||
|
<option value="new">New / Like New</option>
|
||||||
|
<option value="good">Good Condition</option>
|
||||||
|
<option value="acceptable">Acceptable</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label for="pickupDate" class="block text-sm font-medium text-gray-700 mb-1">Pickup Date</label>
|
||||||
|
<input type="date" id="pickupDate" name="pickupDate" 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>
|
||||||
|
<label for="transportCompany" class="block text-sm font-medium text-gray-700 mb-1">Transport Company</label>
|
||||||
|
<input type="text" id="transportCompany" name="transportCompany" placeholder="e.g. ABC Trucking" 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="driverInfo" class="block text-sm font-medium text-gray-700 mb-1">Driver Information</label>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<input type="text" id="driverName" name="driverName" placeholder="Driver Name" class="px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||||
|
<input type="text" id="driverPhone" name="driverPhone" placeholder="Driver Phone" class="px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="orderNotes" class="block text-sm font-medium text-gray-700 mb-1">Special Instructions</label>
|
||||||
|
<textarea id="orderNotes" name="orderNotes" 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 special handling instructions or notes"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section flex justify-end">
|
||||||
|
<button type="submit" 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">
|
||||||
|
Submit Order
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 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">Active 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">Pickup Date</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">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">2023-06-20</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">Approved</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">
|
||||||
|
<button class="text-blue-600 hover:text-blue-800 mr-3">View</button>
|
||||||
|
<button class="text-red-600 hover:text-red-800">Cancel</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">2023-06-19</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">
|
||||||
|
<button class="text-blue-600 hover:text-blue-800 mr-3">View</button>
|
||||||
|
<button class="text-red-600 hover:text-red-800">Cancel</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">ORD-2023-0013</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">MSCU1357924</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-15</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">Completed</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">
|
||||||
|
<button class="text-blue-600 hover:text-blue-800">View</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
||||||
@ -0,0 +1,139 @@
|
|||||||
|
{% extends 'client-base.html' %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="preinfoContent" class="tab-content active">
|
||||||
|
<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">Submit Container Preinfo</h3>
|
||||||
|
<p class="text-sm text-gray-600 mt-1">Provide information about containers that will arrive at the depot</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-6">
|
||||||
|
<form id="preinfoForm" class="space-y-6" method="POST" >
|
||||||
|
|
||||||
|
{{ form.as_p }}
|
||||||
|
{% csrf_token %}
|
||||||
|
{# <div class="form-section grid grid-cols-1 md:grid-cols-2 gap-6">#}
|
||||||
|
{# <div>#}
|
||||||
|
{# <label for="containerNumber" class="block text-sm font-medium text-gray-700 mb-1">Container Number</label>#}
|
||||||
|
{# <input type="text" id="containerNumber" name="containerNumber" placeholder="e.g. MSCU1234567" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" required>#}
|
||||||
|
{# <p class="mt-1 text-xs text-gray-500">Enter the full container number including prefix</p>#}
|
||||||
|
{# </div>#}
|
||||||
|
{##}
|
||||||
|
{# <div>#}
|
||||||
|
{# <label for="containerType" class="block text-sm font-medium text-gray-700 mb-1">Container Type</label>#}
|
||||||
|
{# <select id="containerType" name="containerType" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" required>#}
|
||||||
|
{# <option value="">Select container type</option>#}
|
||||||
|
{# <option value="20DV">20' Dry Van (20DV)</option>#}
|
||||||
|
{# <option value="40DV">40' Dry Van (40DV)</option>#}
|
||||||
|
{# <option value="40HC">40' High Cube (40HC)</option>#}
|
||||||
|
{# <option value="20RF">20' Reefer (20RF)</option>#}
|
||||||
|
{# <option value="40RF">40' Reefer (40RF)</option>#}
|
||||||
|
{# <option value="20OT">20' Open Top (20OT)</option>#}
|
||||||
|
{# <option value="40OT">40' Open Top (40OT)</option>#}
|
||||||
|
{# <option value="20FR">20' Flat Rack (20FR)</option>#}
|
||||||
|
{# <option value="40FR">40' Flat Rack (40FR)</option>#}
|
||||||
|
{# </select>#}
|
||||||
|
{# </div>#}
|
||||||
|
{# </div>#}
|
||||||
|
{##}
|
||||||
|
{# <div class="form-section grid grid-cols-1 md:grid-cols-2 gap-6">#}
|
||||||
|
{# <div>#}
|
||||||
|
{# <label for="estimatedArrival" class="block text-sm font-medium text-gray-700 mb-1">Estimated Arrival Date</label>#}
|
||||||
|
{# <input type="date" id="estimatedArrival" name="estimatedArrival" 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>#}
|
||||||
|
{# <label for="vesselVoyage" class="block text-sm font-medium text-gray-700 mb-1">Vessel/Voyage</label>#}
|
||||||
|
{# <input type="text" id="vesselVoyage" name="vesselVoyage" placeholder="e.g. MAERSK SEVILLE / 123E" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">#}
|
||||||
|
{# </div>#}
|
||||||
|
{# </div>#}
|
||||||
|
{##}
|
||||||
|
{# <div class="form-section">#}
|
||||||
|
{# <label for="additionalInfo" class="block text-sm font-medium text-gray-700 mb-1">Additional Information</label>#}
|
||||||
|
{# <textarea id="additionalInfo" name="additionalInfo" 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 special handling instructions or notes"></textarea>#}
|
||||||
|
{# </div>#}
|
||||||
|
{##}
|
||||||
|
{# <div class="form-section flex items-center">#}
|
||||||
|
{# <input type="checkbox" id="damageCheck" name="damageCheck" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">#}
|
||||||
|
{# <label for="damageCheck" class="ml-2 block text-sm text-gray-700">Container has visible damage</label>#}
|
||||||
|
{# </div>#}
|
||||||
|
{##}
|
||||||
|
{# <div id="damageDetails" class="form-section hidden">#}
|
||||||
|
{# <label for="damageDescription" class="block text-sm font-medium text-gray-700 mb-1">Damage Description</label>#}
|
||||||
|
{# <textarea id="damageDescription" name="damageDescription" 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="Describe the damage in detail"></textarea>#}
|
||||||
|
{# </div>#}
|
||||||
|
|
||||||
|
<div class="form-section flex justify-end space-x-3">
|
||||||
|
<button type="button" id="addMoreContainer" class="px-4 py-2 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500">
|
||||||
|
Add Another Container
|
||||||
|
</button>
|
||||||
|
<button type="submit" 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">
|
||||||
|
Submit Preinfo
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 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 Preinfo Submissions</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">Est. Arrival</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">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-gray-200">
|
||||||
|
{% for preinfo in recent %}
|
||||||
|
<tr>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">{{ preinfo.container_number }}</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">{{ preinfo.container_type }}</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">{{ preinfo.created_on }}</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">Pending</span>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">
|
||||||
|
<button class="text-blue-600 hover:text-blue-800 mr-3">Edit</button>
|
||||||
|
<button class="text-red-600 hover:text-red-800">Cancel</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{# <tr>#}
|
||||||
|
{# <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU9876543</td>#}
|
||||||
|
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">40HC</td>#}
|
||||||
|
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-17</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">#}
|
||||||
|
{# <button class="text-blue-600 hover:text-blue-800 mr-3">View</button>#}
|
||||||
|
{# </td>#}
|
||||||
|
{# </tr>#}
|
||||||
|
{# <tr>#}
|
||||||
|
{# <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">MSCU1122334</td>#}
|
||||||
|
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">20RF</td>#}
|
||||||
|
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">2023-06-16</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">Delayed</span>#}
|
||||||
|
{# </td>#}
|
||||||
|
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">#}
|
||||||
|
{# <button class="text-blue-600 hover:text-blue-800 mr-3">Edit</button>#}
|
||||||
|
{# <button class="text-red-600 hover:text-red-800">Cancel</button>#}
|
||||||
|
{# </td>#}
|
||||||
|
{# </tr>#}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
||||||
@ -0,0 +1,593 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<!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 Reception Content -->
|
||||||
|
<div id="receiveContent" class="tab-content active 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 Reception</h3>
|
||||||
|
<p class="text-sm text-gray-600 mt-1">Process incoming 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>
|
||||||
|
<form id="receiveForm" class="space-y-6" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="p-6">
|
||||||
|
<label for="id_number" class="block text-sm font-medium text-gray-700 mb-1">Container Number</label>
|
||||||
|
{# <label for="id_number" >Container Number</label>#}
|
||||||
|
{# <input type="text" id="id_number" name="number" placeholder="Enter Container Number" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 mb-4">#}
|
||||||
|
{# <button type="button" id="check-preinfo-btn">Check Preinfo</button>#}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{# <label for="containerNumber" class="block text-sm font-medium text-gray-700 mb-1">Container Number</label>#}
|
||||||
|
{# <div class="flex">#}
|
||||||
|
{# <input type="text" id="id_number" name="number" 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="check-preinfo-btn" 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>#}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="preinfoData">
|
||||||
|
<div class="bg-blue-50 border border-blue-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-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 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-blue-800">Preinfo Found</h3>
|
||||||
|
<div class="mt-2 text-sm text-blue-700">
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<span class="font-medium">Container Number:</span>
|
||||||
|
<input type="text" id="id_number" name="number" value="{{ preinfo.container_number }}" readonly>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="font-medium">Container Type:</span>
|
||||||
|
<input type="text" id="id_container_type" name="id_container_type" value="{{ preinfo.container_type }}" readonly>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="font-medium">Line Operator:</span>
|
||||||
|
<input type="text" id="id_line" name="id_line" value="{{ preinfo.line.name }}" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{# <p class="mt-2"><span class="font-medium">Notes:</span> <span id="preinfoNotes">Container reported with minor damage on right side panel.</span></p>#}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="id_receive_vehicle">Receive vehicle:</label>
|
||||||
|
<input type="text" name="receive_vehicle" maxlength="100" id="id_receive_vehicle">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="id_damages">Damages:</label>
|
||||||
|
<textarea name="damages" cols="40" rows="10" id="id_damages"></textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="id_heavy_damaged">Heavy damaged:</label>
|
||||||
|
<input type="checkbox" name="heavy_damaged" id="id_heavy_damaged">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="id_position">Position:</label>
|
||||||
|
<input type="text" name="position" maxlength="100" id="id_position">
|
||||||
|
</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 Reception
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="hidden" name="preinfo_id" id="preinfo_id" value="{{ preinfo.pk }}">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</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">Recent Container Movements</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">Line</th>
|
||||||
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Time</th>
|
||||||
|
{# <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Direction</th>#}
|
||||||
|
{# <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>#}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-gray-200">
|
||||||
|
{% for container in containers %}
|
||||||
|
<tr>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||||
|
{{ container.number }}</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">{{ container.container_type }}</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">{{ container.line.name }}</td>
|
||||||
|
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">{{ container.received_on }}</td>
|
||||||
|
{# <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700">IN</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">Complete</span>#}
|
||||||
|
{# </td>#}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Container Expedition Content -->
|
||||||
|
</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>#}
|
||||||
|
{##}
|
||||||
|
{#<script>#}
|
||||||
|
{#document.getElementById('check-preinfo-btn').onclick = function() {#}
|
||||||
|
{# const number = document.getElementById('id_number').value;#}
|
||||||
|
{# fetch(`/preinfo/check-preinfo/?number=${encodeURIComponent(number)}`)#}
|
||||||
|
{# .then(response => response.json())#}
|
||||||
|
{# .then(data => {#}
|
||||||
|
{# if (data.found) {#}
|
||||||
|
{# alert('Preinfo found ' + data.container_type);#}
|
||||||
|
{# document.getElementById('id_line').value = data.line;#}
|
||||||
|
{# document.getElementById('id_container_type').value = data.container_type;#}
|
||||||
|
{# document.getElementById('preinfo_id').value = data.preinfo_id;#}
|
||||||
|
{# } else {#}
|
||||||
|
{# alert('No preinfo found or already received.');#}
|
||||||
|
{# }#}
|
||||||
|
{# });#}
|
||||||
|
{#;#}
|
||||||
|
{#</script>#}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,131 @@
|
|||||||
|
<!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>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="p-6">
|
||||||
|
<label for="id_number" class="block text-sm font-medium text-gray-700 mb-1">Container Number</label>
|
||||||
|
<div class="flex">
|
||||||
|
<input type="text" id="id_number" name="number" 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="submit" id="check-preinfo-btn" 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>
|
||||||
|
<input type="hidden" name="param" value="{{ request.GET.param }}">
|
||||||
|
{% if error %}
|
||||||
|
<div class="ml-4 text-red-600">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,126 @@
|
|||||||
|
<!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>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="p-6">
|
||||||
|
<label for="id_number" class="block text-sm font-medium text-gray-700 mb-1">Container Number</label>
|
||||||
|
<div class="flex">
|
||||||
|
<input type="text" id="id_number" name="number" 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="submit" id="check-preinfo-btn" 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>
|
||||||
|
<input type="hidden" name="param" value="{{ request.GET.param }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Container Depot Management System</title>
|
||||||
|
<link rel="stylesheet" href="{% static '/css/styles.css' %}"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{# {{ title|default:"Container Depot Management System" }} {{ description|default:"Login to the Container Depot Management System" }}#}
|
||||||
|
<div class="background-image"></div>
|
||||||
|
<div class="wave"></div>
|
||||||
|
|
||||||
|
<div class="login-container">
|
||||||
|
<!-- Left side - Login Form -->
|
||||||
|
<div class="login-form-section">
|
||||||
|
<div>
|
||||||
|
<h1>Container Depot</h1>
|
||||||
|
<p class="subtitle">Sign in to access the management system</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="loginForm" method="post" action="{% url 'login' %}"
|
||||||
|
>
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form }}
|
||||||
|
<div>
|
||||||
|
<button type="submit" class="submit-button" >
|
||||||
|
Sign In
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p>Need help? Contact system administrator</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right side - Info -->
|
||||||
|
<div class="info-section">
|
||||||
|
<div>
|
||||||
|
<h2>Container Depot Management System</h2>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="feature-item">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<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="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3>Line Operators</h3>
|
||||||
|
<p class="feature-text">Manage container preinfo and expedition orders</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-item">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<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 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3>Barrier Staff</h3>
|
||||||
|
<p class="feature-text">Process container arrivals and departures</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="feature-item">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<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 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"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3>Depot Management</h3>
|
||||||
|
<p class="feature-text">View inventory and generate reports</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="version-info">
|
||||||
|
<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="M5 12h.01M12 12h.01M19 12h.01M6 12a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0z"></path>
|
||||||
|
</svg>
|
||||||
|
<span>Version 1.0.0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
{% 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>
|
||||||
|
|
||||||
|
<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 'container_search' %}?param=container_receive" 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>
|
||||||
|
Expedition Orders
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<div class="px-4 py-2 mt-6 text-xs text-blue-300 uppercase tracking-wider">Account</div>
|
||||||
|
<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="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
|
||||||
|
</a>
|
||||||
|
</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>
|
||||||
|
<button class="ml-auto text-white" onclick="{% url 'relogin' %}">
|
||||||
|
<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>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
# Register your models here.
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
from django.forms import forms
|
|
||||||
|
|
||||||
|
|
||||||
class LoginForm(forms.Form):
|
|
||||||
field_order = ['username', 'password']
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
from django.contrib.auth.models import AbstractUser
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
class DepotUser(AbstractUser):
|
|
||||||
USER_TYPES = (
|
|
||||||
('management', 'Management'),
|
|
||||||
('barrier', 'Barrier Staff'),
|
|
||||||
('client', 'Client'),
|
|
||||||
)
|
|
||||||
user_type = models.CharField(max_length=20, choices=USER_TYPES, default='client')
|
|
||||||
|
|
||||||
# Add any other fields you want
|
|
||||||
phone_number = models.CharField(max_length=15, blank=True, null=True)
|
|
||||||
email = models.EmailField(unique=True, blank=False, null=False)
|
|
||||||
line = models.ForeignKey(
|
|
||||||
'common.LinesModel',
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='user_lines',
|
|
||||||
blank=True,
|
|
||||||
null=True
|
|
||||||
)
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
from django.urls import path
|
|
||||||
|
|
||||||
from users import views
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('login/', views.LoginView.as_view(), name='login'),
|
|
||||||
]
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
from django.shortcuts import render
|
|
||||||
from django.urls import reverse_lazy
|
|
||||||
from django.views.generic import TemplateView, FormView
|
|
||||||
|
|
||||||
from users.forms import LoginForm
|
|
||||||
|
|
||||||
|
|
||||||
# Create your views here.
|
|
||||||
class LoginView(FormView):
|
|
||||||
template_name = 'logintest.html'
|
|
||||||
success_url = reverse_lazy('dashboard')
|
|
||||||
form_class = LoginForm
|
|
||||||
Loading…
Reference in New Issue