fix(operations): prefill booking context on single assign; enforce per-truck container loads

- single-row Assign vehicle uses the full single-record flow (details + containers)
- release() rejects exit containers not assigned to the departing truck
- weighing modal offers only the selected truck's assigned containers
This commit is contained in:
Hagernesh
2026-07-22 07:03:49 +00:00
parent fd3212f326
commit 19906b273e
7 changed files with 213 additions and 57 deletions

View File

@@ -189,6 +189,7 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
label: "Support",
href: "/dashboard/support",
icon: <LifeBuoy />,
permission: FREIGHT_PERMS.bookings.view,
},
...demoItems,
],
@@ -383,26 +384,31 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
label: "Import Overview",
href: "/dashboard/import-warehouse",
icon: <PackageOpen />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
{
label: "Arrival Queue",
href: "/dashboard/arrival-queue",
icon: <PackageOpen />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
{
label: "Dispatch Queue",
href: "/dashboard/dispatch-queue",
icon: <Send />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
{
label: "Terminal Inventory",
href: "/dashboard/warehouse-inventory?direction=IMPORT",
icon: <Package />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
{
label: "Inventory Inquiry",
href: "/dashboard/inventory-inquiry",
icon: <Boxes />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
],
},
@@ -416,36 +422,43 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
label: "Export Overview",
href: "/dashboard/export-warehouse",
icon: <Truck />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
{
label: "Loading Queue",
href: "/dashboard/loading-queue",
icon: <Truck />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
{
label: "Loaded Inventory",
href: "/dashboard/loaded-inventory",
icon: <PackageCheck />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
{
label: "Dispatch Queue",
href: "/dashboard/dispatch-queue",
icon: <Send />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
{
label: "Djibouti Unloading",
href: "/dashboard/export-djibouti-unloading",
icon: <PackageOpen />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
{
label: "Interchange Documents",
href: "/dashboard/interchange-documents",
icon: <FileText />,
permission: FREIGHT_PERMS.interchangeDocuments.view,
},
{
label: "Terminal Inventory",
href: "/dashboard/warehouse-inventory?direction=EXPORT",
icon: <Package />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
],
},
@@ -459,6 +472,7 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
label: "Intercity Cargo",
href: "/dashboard/intercity",
icon: <TrainFront />,
permission: FREIGHT_PERMS.warehouseInventory.view,
},
],
},
@@ -490,7 +504,10 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
label: "Allocation & Fees",
href: "/dashboard/warehouse-rules",
icon: <SlidersHorizontal />,
permission: FREIGHT_PERMS.warehouseAllocationRules.view,
permission: [
FREIGHT_PERMS.warehouseAllocationRules.view,
FREIGHT_PERMS.warehouseFeeRules.view,
],
},
{
label: "Fee Invoices",
@@ -553,7 +570,12 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
label: "Staff",
href: "/user-management",
icon: <Users />,
permission: FREIGHT_PERMS.admin,
permission: [
FREIGHT_PERMS.admin,
FREIGHT_PERMS.staff.roles.view,
FREIGHT_PERMS.staff.employeeRegistration.view,
FREIGHT_PERMS.staff.roleAssignment.view,
],
},
],
},
@@ -598,16 +620,27 @@ const filterSidebarByPermission = (
return keys.some((key) => hasFreightPermission(user, key));
};
const itemAllowed = (item: SidebarItem): boolean => {
// GL positions are locked to their single clearance page.
if (etGl) return isEtClearanceItem(item);
if (djGl) return isDjClearanceItem(item);
// Everyone else: hide the GL-only clearance pages entirely.
if (isClearanceItem(item)) return false;
return permissionAllowed(item);
};
// Recursive: children are filtered first; a group (item with children) stays
// only while it still has visible children — so parents without their own
// permission key never leak a whole subtree the user cannot open.
const filterItems = (items: SidebarItem[]): SidebarItem[] =>
items
.map((item) =>
item.children ? { ...item, children: filterItems(item.children) } : item,
)
.filter((item) => {
if (etGl || djGl) {
// GL positions are locked to their single clearance page (parents
// survive only as the path to that page).
const isTarget = etGl ? isEtClearanceItem : isDjClearanceItem;
return isTarget(item) || (item.children?.length ?? 0) > 0;
}
// Everyone else: hide the GL-only clearance pages entirely.
if (isClearanceItem(item)) return false;
if (!permissionAllowed(item)) return false;
if (item.children) return item.children.length > 0;
return true;
});
// Recursive: a group's own permission gates the whole subtree, leaves are
// checked individually, and a group with no surviving children disappears.