mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-01 13:33:26 +00:00
Multi-truck self-haul had no list anywhere on the warehouse side. The ops dashboard counted trucks on site and offered no way to open the list, and the inventory table showed a blank plate on exactly the bookings that have several trucks: it read booking.customer_truck_plate_number, which multi-truck self-haul leaves null because plates live in customer_truck_assignments. Booking BK-2026-000033 has a truck and a driver on file and displayed neither. Adds a Trucks on Site page listing every truck that has arrived and not yet departed, across bookings, with plate, driver, booking, customer, containers and dwell time. It covers both haulage paths because the gate does — a customer's own truck and an EDR last-mile truck reach the same barrier — and flags anything sitting over four hours. It lives under Warehouse Management rather than Imports or Exports, since the yard is not per-direction. The inventory queries now read plates and drivers from the assignments and keep the booking columns as the fallback for single-truck bookings written before that table existed. Both new statements were EXPLAIN-validated against the live schema; the plate fix returns the data that was previously null. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1478 lines
47 KiB
TypeScript
1478 lines
47 KiB
TypeScript
import {
|
|
Boxes,
|
|
Building2,
|
|
Container,
|
|
FileSignature,
|
|
FileText,
|
|
Hammer,
|
|
LayoutDashboard,
|
|
LayoutGrid,
|
|
MapPin,
|
|
Network,
|
|
Package,
|
|
PackageCheck,
|
|
PackageOpen,
|
|
Paperclip,
|
|
Receipt,
|
|
ScrollText,
|
|
Send,
|
|
Settings,
|
|
ShieldCheck,
|
|
Ship,
|
|
SlidersHorizontal,
|
|
Train,
|
|
Truck,
|
|
Users,
|
|
Wallet,
|
|
LifeBuoy,
|
|
TrainFront,
|
|
} from "lucide-react";
|
|
import { useEffect } from "react";
|
|
import {
|
|
Navigate,
|
|
Outlet,
|
|
Route,
|
|
Routes,
|
|
useLocation,
|
|
useNavigate,
|
|
useParams,
|
|
} from "react-router-dom";
|
|
|
|
import {
|
|
FreightDashboardLayout,
|
|
type SidebarItem,
|
|
type SidebarSection,
|
|
} from "@/components/layout";
|
|
import { useAuth } from "./auth/useAuth";
|
|
import LoadingScreen from "./components/LoadingScreen";
|
|
import LoginPage from "./pages/auth/LoginPage";
|
|
import ForgotPasswordPage from "./pages/auth/ForgotPasswordPage";
|
|
import BookingContractPage from "./pages/bookings/BookingContractPage";
|
|
import BookingRequestDetailPage from "./pages/bookings/BookingRequestDetailPage";
|
|
import BookingRequestsPage from "./pages/bookings/BookingRequestsPage";
|
|
import NewBookingPage from "./pages/bookings/NewBookingPage";
|
|
import ContractRequestsPage from "./pages/contracts/ContractRequestsPage";
|
|
import ContractRequestDetailPage from "./pages/contracts/ContractRequestDetailPage";
|
|
import ContractViewPage from "./pages/contracts/ContractViewPage";
|
|
import ContractClearanceListPage from "./pages/contracts/ContractClearanceListPage";
|
|
import ContractClearanceDetailPage from "./pages/contracts/ContractClearanceDetailPage";
|
|
import ClearanceDocumentsPage from "./pages/contracts/ClearanceDocumentsPage";
|
|
import GlDjiboutiClearanceListPage from "./pages/contracts/GlDjiboutiClearanceListPage";
|
|
import GlClearanceDetailPage from "./pages/contracts/GlClearanceDetailPage";
|
|
import ShipmentRequestsPage from "./pages/contracts/ShipmentRequestsPage";
|
|
import ShipmentRequestDetailPage from "./pages/contracts/ShipmentRequestDetailPage";
|
|
import GlCreateBookingForm from "./components/contracts/GlCreateBookingForm";
|
|
import DocumentClearanceDetailPage from "./pages/bookings/DocumentClearanceDetailPage";
|
|
import DocumentClearanceListPage from "./pages/bookings/DocumentClearanceListPage";
|
|
import CustomerDetailPage from "./pages/customers/CustomerDetailPage";
|
|
import CustomersPage from "./pages/customers/CustomersPage";
|
|
import InvoiceDetailPage from "./pages/invoices/InvoiceDetailPage";
|
|
import InvoicesPage from "./pages/invoices/InvoicesPage";
|
|
import MyProfilePage from "./pages/dashboard/MyProfilePage";
|
|
import OverviewPage from "./pages/dashboard/OverviewPage";
|
|
import AiBookingMockTestPage from "./pages/ai/AiBookingMockTestPage";
|
|
import PaymentsPage from "./pages/payments/PaymentsPage";
|
|
//import EmployeesPage from "./pages/dashboard/user-management/EmployeesPage";
|
|
import { RequirePermission } from "./components/auth/RequirePermission";
|
|
import {
|
|
FREIGHT_PERMS,
|
|
hasPermission as hasFreightPermission,
|
|
isDjiboutiGl,
|
|
isEthiopianGl,
|
|
isSuperAdmin,
|
|
} from "./lib/permissions";
|
|
import FileUploadSettingsPage from "./pages/documents/FileUploadSettingsPage";
|
|
import DropdownSettingsPage from "./pages/dropdown_settings/DropdownSettingsPage";
|
|
import ContractTemplatesPage from "./pages/contract_templates/ContractTemplatesPage";
|
|
import ContractTemplateEditorPage from "./pages/contract_templates/ContractTemplateEditorPage";
|
|
import FleetResourcePage from "./pages/fleet/FleetResourcePage";
|
|
import VehicleDetailPage from "./pages/fleet/VehicleDetailPage";
|
|
import DriverDetailPage from "./pages/fleet/DriverDetailPage";
|
|
import RoutesPage from "./pages/fleet/RoutesPage";
|
|
import FuelPurchasePage from "./pages/fleet/FuelPurchasePage";
|
|
import FuelStatsPage from "./pages/fleet/FuelStatsPage";
|
|
import { MaintenancePage } from "./pages/fleet/MaintenancePage";
|
|
import { FinancialReportsPage } from "./pages/fleet/FinancialReportsPage";
|
|
import { FleetDashboard } from "./pages/fleet/FleetDashboard";
|
|
import { TrackingPage } from "./pages/fleet/TrackingPage";
|
|
import CompliancePage from "./pages/fleet/CompliancePage";
|
|
import IncidentsPage from "./pages/fleet/IncidentsPage";
|
|
import WorkOrdersPage from "./pages/fleet/WorkOrdersPage";
|
|
import ProcurementPage from "./pages/fleet/ProcurementPage";
|
|
import { getCategorySidebarChildren } from "./pages/ruleEngine/config/resources";
|
|
import RuleEngineLegacyRedirect from "./pages/ruleEngine/RuleEngineLegacyRedirect";
|
|
import RuleEngineResourcePage from "./pages/ruleEngine/RuleEngineResourcePage";
|
|
import CargoTypesPage from "./pages/ruleEngine/CargoTypesPage";
|
|
import TrainScheduleV2ListPage from "./pages/trainScheduling/TrainScheduleV2ListPage";
|
|
import BatchBoardPage from "./pages/trainScheduling/BatchBoardPage";
|
|
import BatchScheduleDetailPage from "./pages/trainScheduling/BatchScheduleDetailPage";
|
|
import TrainScheduleV2DetailPage from "./pages/trainScheduling/TrainScheduleV2DetailPage";
|
|
import TrainScheduleTrackPage from "./pages/trainScheduling/TrainScheduleTrackPage";
|
|
import TrainSchedulingGlobalRulesPage from "./pages/trainScheduling/TrainSchedulingGlobalRulesPage";
|
|
import ContractValidityPeriodsPage from "./pages/configuration/ContractValidityPeriodsPage";
|
|
import FirstMilePage from "./pages/operations/FirstMilePage";
|
|
import LastMilePage from "./pages/operations/LastMilePage";
|
|
import TrainDetailPage from "./pages/trains/TrainDetailPage";
|
|
import TrainBuilderDetailPage from "./pages/trainBuilder/TrainBuilderDetailPage";
|
|
import TrainBuilderListPage from "./pages/trainBuilder/TrainBuilderListPage";
|
|
import ArrivalQueuePage from "./pages/warehouses/ArrivalQueuePage";
|
|
import DispatchQueuePage from "./pages/warehouses/DispatchQueuePage";
|
|
import ExportDjiboutiUnloadingQueuePage from "./pages/warehouses/ExportDjiboutiUnloadingQueuePage";
|
|
import ExportWarehouseFlowPage from "./pages/warehouses/ExportWarehouseFlowPage";
|
|
import ImportWarehouseFlowPage from "./pages/warehouses/ImportWarehouseFlowPage";
|
|
import InterchangeDocumentsPage from "./pages/warehouses/InterchangeDocumentsPage";
|
|
import InventoryInquiryPage from "./pages/warehouses/InventoryInquiryPage";
|
|
import LoadedInventoryPage from "./pages/warehouses/LoadedInventoryPage";
|
|
import LoadingQueuePage from "./pages/warehouses/LoadingQueuePage";
|
|
import IntercityPage from "./pages/warehouses/IntercityPage";
|
|
import TrucksOnSitePage from "./pages/warehouses/TrucksOnSitePage";
|
|
import WarehouseDashboardPage from "./pages/warehouses/WarehouseDashboardPage";
|
|
import WarehouseDetailPage from "./pages/warehouses/WarehouseDetailPage";
|
|
import WarehouseInventoryPage from "./pages/warehouses/WarehouseInventoryPage";
|
|
import WarehouseInvoicesPage from "./pages/warehouses/WarehouseInvoicesPage";
|
|
import WarehouseListPage from "./pages/warehouses/WarehouseListPage";
|
|
import WarehouseRulesPage from "./pages/warehouses/WarehouseRulesPage";
|
|
import { HealthCheck } from "./features/health/HealthCheck";
|
|
import FaydaCallbackPage from "./pages/FaydaCallbackPage";
|
|
import { UserManagementRoutes } from "./user-management/route";
|
|
import SetPassword from "./shared/components/SetPassword";
|
|
import SupportInboxPage from "./pages/support/SupportInboxPage";
|
|
|
|
const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
|
|
{
|
|
title: "Main menu",
|
|
items: [
|
|
{
|
|
label: "Overview",
|
|
href: "/dashboard/overview",
|
|
icon: <LayoutDashboard />,
|
|
},
|
|
{
|
|
label: "Customers",
|
|
href: "/dashboard/customers",
|
|
icon: <Building2 />,
|
|
},
|
|
{
|
|
label: "Contracts",
|
|
href: "/dashboard/contract-requests",
|
|
icon: <FileSignature />,
|
|
permission: FREIGHT_PERMS.contracts.view,
|
|
},
|
|
{
|
|
label: "Bookings",
|
|
href: "/dashboard/booking-requests",
|
|
icon: <FileText />,
|
|
},
|
|
// Operations hub: clearance-document review for contracts WITHOUT
|
|
// customs clearing (contract-level for one-time, per-booking for general).
|
|
{
|
|
label: "Clearance Documents",
|
|
href: "/dashboard/contracts/clearance-documents",
|
|
icon: <ShieldCheck />,
|
|
permission: FREIGHT_PERMS.contracts.opsClearanceReview,
|
|
},
|
|
{
|
|
label: "Payments",
|
|
href: "/dashboard/payments",
|
|
icon: <Wallet />,
|
|
permission: FREIGHT_PERMS.bookings.view,
|
|
},
|
|
{
|
|
label: "Invoices",
|
|
href: "/dashboard/invoices",
|
|
icon: <Receipt />,
|
|
permission: FREIGHT_PERMS.bookings.view,
|
|
},
|
|
{
|
|
label: "Support",
|
|
href: "/dashboard/support",
|
|
icon: <LifeBuoy />,
|
|
},
|
|
...demoItems,
|
|
],
|
|
},
|
|
{
|
|
// title: "Port & Terminal",
|
|
items: [
|
|
{
|
|
label: "Operations",
|
|
icon: <Settings />,
|
|
children: [
|
|
{
|
|
label: "Clearance",
|
|
href: "/dashboard/contracts/clearance",
|
|
icon: <ShieldCheck />,
|
|
permission: [
|
|
FREIGHT_PERMS.contracts.clearanceReview,
|
|
FREIGHT_PERMS.contracts.clearanceEtActions,
|
|
],
|
|
},
|
|
// {
|
|
// label: "Shipment Requests",
|
|
// href: "/dashboard/shipment-requests",
|
|
// icon: <Send />,
|
|
// permission: FREIGHT_PERMS.contracts.createBooking,
|
|
// },
|
|
// Operations Path A queue: per-booking self-clearance review for
|
|
// GENERAL non-customs booking instances (and legacy self-clear bookings).
|
|
// {
|
|
// label: "Self-Clearance Review",
|
|
// href: "/dashboard/contracts/ops-clearance",
|
|
// icon: <ShieldCheck />,
|
|
// permission: FREIGHT_PERMS.contracts.opsClearanceReview,
|
|
// },
|
|
{
|
|
label: "GL Djibouti Clearance",
|
|
href: "/dashboard/gl-djibouti/clearance",
|
|
icon: <Ship />,
|
|
permission: FREIGHT_PERMS.contracts.clearanceDjActions,
|
|
},
|
|
{
|
|
label: "Train Schedules",
|
|
href: "/dashboard/operations/train-scheduling-v2",
|
|
icon: <Train />,
|
|
permission: FREIGHT_PERMS.trainScheduling.view,
|
|
},
|
|
{
|
|
label: "Batch Board",
|
|
href: "/dashboard/operations/batch-board",
|
|
icon: <LayoutGrid />,
|
|
permission: FREIGHT_PERMS.trainScheduling.view,
|
|
},
|
|
{
|
|
label: "First Mile",
|
|
href: "/dashboard/operations/first-mile",
|
|
icon: <Truck />,
|
|
permission: FREIGHT_PERMS.firstMile.view,
|
|
},
|
|
{
|
|
label: "Last Mile",
|
|
href: "/dashboard/operations/last-mile",
|
|
icon: <Truck />,
|
|
permission: FREIGHT_PERMS.lastMile.view,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "Fleet Management",
|
|
icon: <Truck />,
|
|
children: [
|
|
{
|
|
label: "Fleet Dashboard",
|
|
href: "/dashboard/fleet-dashboard",
|
|
icon: <LayoutDashboard />,
|
|
permission: FREIGHT_PERMS.fleetDashboard.view,
|
|
},
|
|
{
|
|
label: "Routes",
|
|
href: "/dashboard/routes",
|
|
icon: <Network />,
|
|
permission: FREIGHT_PERMS.fleet.view,
|
|
},
|
|
{
|
|
label: "Locomotives",
|
|
href: "/dashboard/locomotives",
|
|
icon: <Train />,
|
|
permission: FREIGHT_PERMS.fleet.view,
|
|
},
|
|
{
|
|
label: "Train Builder",
|
|
href: "/dashboard/train-builder",
|
|
icon: <Hammer />,
|
|
permission: FREIGHT_PERMS.fleet.view,
|
|
},
|
|
|
|
// {
|
|
// label: "Wagon types",
|
|
// href: "/dashboard/wagon-types",
|
|
// icon: <Boxes />,
|
|
// },
|
|
{
|
|
label: "Wagons",
|
|
href: "/dashboard/wagons",
|
|
icon: <Truck />,
|
|
permission: FREIGHT_PERMS.fleet.view,
|
|
},
|
|
{
|
|
label: "Vehicles",
|
|
href: "/dashboard/vehicles",
|
|
icon: <Truck />,
|
|
permission: FREIGHT_PERMS.vehicles.view,
|
|
},
|
|
{
|
|
label: "Drivers",
|
|
href: "/dashboard/drivers",
|
|
icon: <Users />,
|
|
permission: FREIGHT_PERMS.drivers.view,
|
|
},
|
|
{
|
|
label: "Track Vehicles",
|
|
href: "/dashboard/tracking",
|
|
icon: <MapPin />,
|
|
permission: FREIGHT_PERMS.tracking.view,
|
|
},
|
|
{
|
|
label: "Fuel Purchases",
|
|
href: "/dashboard/fuel-purchases",
|
|
icon: <Truck />,
|
|
permission: FREIGHT_PERMS.fuel.view,
|
|
},
|
|
{
|
|
label: "Fuel Analytics",
|
|
href: "/dashboard/fuel-stats",
|
|
icon: <Truck />,
|
|
permission: FREIGHT_PERMS.fuel.view,
|
|
},
|
|
{
|
|
label: "Maintenance",
|
|
href: "/dashboard/maintenance",
|
|
icon: <Truck />,
|
|
permission: FREIGHT_PERMS.maintenance.view,
|
|
},
|
|
{
|
|
label: "Work Orders",
|
|
href: "/dashboard/work-orders",
|
|
icon: <SlidersHorizontal />,
|
|
permission: FREIGHT_PERMS.maintenance.view,
|
|
},
|
|
{
|
|
label: "Compliance & Alerts",
|
|
href: "/dashboard/compliance",
|
|
icon: <ShieldCheck />,
|
|
permission: FREIGHT_PERMS.fleet.view,
|
|
},
|
|
{
|
|
label: "Incidents",
|
|
href: "/dashboard/incidents",
|
|
icon: <FileText />,
|
|
permission: FREIGHT_PERMS.fleet.view,
|
|
},
|
|
{
|
|
label: "Procurement",
|
|
href: "/dashboard/procurement",
|
|
icon: <Package />,
|
|
permission: FREIGHT_PERMS.fleet.view,
|
|
},
|
|
{
|
|
label: "Financial Reports",
|
|
href: "/dashboard/financial-reports",
|
|
icon: <Wallet />,
|
|
permission: FREIGHT_PERMS.fleetReports.view,
|
|
},
|
|
// {
|
|
// label: "Containers",
|
|
// href: "/dashboard/containers",
|
|
// icon: <Container />,
|
|
// },
|
|
// {
|
|
// label: "Cargoes",
|
|
// href: "/dashboard/cargoes",
|
|
// icon: <Package />,
|
|
// },
|
|
],
|
|
},
|
|
{
|
|
label: "Imports",
|
|
href: "/dashboard/import-warehouse",
|
|
icon: <PackageOpen />,
|
|
children: [
|
|
{
|
|
label: "Import Overview",
|
|
href: "/dashboard/import-warehouse",
|
|
icon: <PackageOpen />,
|
|
},
|
|
{
|
|
label: "Arrival Queue",
|
|
href: "/dashboard/arrival-queue",
|
|
icon: <PackageOpen />,
|
|
},
|
|
{
|
|
label: "Dispatch Queue",
|
|
href: "/dashboard/dispatch-queue",
|
|
icon: <Send />,
|
|
},
|
|
{
|
|
label: "Terminal Inventory",
|
|
href: "/dashboard/warehouse-inventory?direction=IMPORT",
|
|
icon: <Package />,
|
|
},
|
|
{
|
|
label: "Inventory Inquiry",
|
|
href: "/dashboard/inventory-inquiry",
|
|
icon: <Boxes />,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "Exports",
|
|
href: "/dashboard/export-warehouse",
|
|
icon: <Truck />,
|
|
children: [
|
|
{
|
|
label: "Export Overview",
|
|
href: "/dashboard/export-warehouse",
|
|
icon: <Truck />,
|
|
},
|
|
{
|
|
label: "Loading Queue",
|
|
href: "/dashboard/loading-queue",
|
|
icon: <Truck />,
|
|
},
|
|
{
|
|
label: "Loaded Inventory",
|
|
href: "/dashboard/loaded-inventory",
|
|
icon: <PackageCheck />,
|
|
},
|
|
{
|
|
label: "Dispatch Queue",
|
|
href: "/dashboard/dispatch-queue",
|
|
icon: <Send />,
|
|
},
|
|
{
|
|
label: "Djibouti Unloading",
|
|
href: "/dashboard/export-djibouti-unloading",
|
|
icon: <PackageOpen />,
|
|
},
|
|
{
|
|
label: "Interchange Documents",
|
|
href: "/dashboard/interchange-documents",
|
|
icon: <FileText />,
|
|
},
|
|
{
|
|
label: "Terminal Inventory",
|
|
href: "/dashboard/warehouse-inventory?direction=EXPORT",
|
|
icon: <Package />,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "Intercity",
|
|
href: "/dashboard/intercity",
|
|
icon: <TrainFront />,
|
|
children: [
|
|
{
|
|
label: "Intercity Cargo",
|
|
href: "/dashboard/intercity",
|
|
icon: <TrainFront />,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "Warehouse Management",
|
|
icon: <Container />,
|
|
children: [
|
|
{
|
|
label: "Warehouse Dashboard",
|
|
href: "/dashboard/warehouse-dashboard",
|
|
icon: <LayoutDashboard />,
|
|
},
|
|
{
|
|
// Yard-wide, not per-direction: the gate sees import and export
|
|
// trucks at the same barrier.
|
|
label: "Trucks on Site",
|
|
href: "/dashboard/trucks-on-site",
|
|
icon: <Truck />,
|
|
},
|
|
{
|
|
label: "Warehouses",
|
|
href: "/dashboard/warehouses",
|
|
icon: <Container />,
|
|
},
|
|
{
|
|
label: "Allocation & Fees",
|
|
href: "/dashboard/warehouse-rules",
|
|
icon: <SlidersHorizontal />,
|
|
},
|
|
{
|
|
label: "Fee Invoices",
|
|
href: "/dashboard/warehouse-fee-invoices",
|
|
icon: <Wallet />,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: "Freight configuration",
|
|
mutedTitle: true,
|
|
items: [
|
|
{
|
|
label: "File settings",
|
|
href: "/dashboard/file-settings",
|
|
icon: <Paperclip />,
|
|
permission: FREIGHT_PERMS.admin,
|
|
},
|
|
{
|
|
label: "Dropdown settings",
|
|
href: "/dashboard/dropdown-settings",
|
|
icon: <Settings />,
|
|
permission: FREIGHT_PERMS.admin,
|
|
},
|
|
{
|
|
label: "Contract templates",
|
|
href: "/dashboard/contract-templates",
|
|
icon: <ScrollText />,
|
|
permission: FREIGHT_PERMS.admin,
|
|
},
|
|
{
|
|
label: "Configuration",
|
|
href: "/dashboard/configuration",
|
|
icon: <Boxes />,
|
|
children: [
|
|
...getCategorySidebarChildren("configuration"),
|
|
{
|
|
label: "Contract validity",
|
|
href: "/dashboard/configuration/contract-validity-periods",
|
|
},
|
|
{
|
|
label: "Train scheduling rules",
|
|
href: "/dashboard/configuration/train-scheduling-rules",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "Rules",
|
|
href: "/dashboard/rules",
|
|
icon: <SlidersHorizontal />,
|
|
children: getCategorySidebarChildren("rules"),
|
|
},
|
|
|
|
{
|
|
label: "Staff",
|
|
href: "/user-management",
|
|
icon: <Users />,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
/** Hrefs of the two document-clearance menu items (stable identifiers). */
|
|
const ET_CLEARANCE_HREF = "/dashboard/contracts/clearance";
|
|
const DJ_CLEARANCE_HREF = "/dashboard/gl-djibouti/clearance";
|
|
|
|
const isEtClearanceItem = (item: SidebarItem): boolean =>
|
|
item.href === ET_CLEARANCE_HREF;
|
|
const isDjClearanceItem = (item: SidebarItem): boolean =>
|
|
item.href === DJ_CLEARANCE_HREF;
|
|
const isClearanceItem = (item: SidebarItem): boolean =>
|
|
isEtClearanceItem(item) || isDjClearanceItem(item);
|
|
|
|
/**
|
|
* Keep only items the user is permitted to see; drop now-empty sections.
|
|
*
|
|
* Position-scoped visibility (super_admin sees everything):
|
|
* - Super Admin → sees all items (all permissions pass, all tabs visible)
|
|
* - Ethiopian GL → sees ONLY the ET document-clearance page.
|
|
* - Djibouti GL → sees ONLY the DJ clearance page.
|
|
* - Everyone else → sees everything they have permission for, EXCEPT the two
|
|
* clearance pages (those are GL-only).
|
|
*/
|
|
const filterSidebarByPermission = (
|
|
sections: SidebarSection[],
|
|
user: ReturnType<typeof useAuth>["user"],
|
|
): SidebarSection[] => {
|
|
// Superadmin sees every section and item — no permission filtering.
|
|
if (isSuperAdmin(user)) return sections;
|
|
|
|
const etGl = isEthiopianGl(user);
|
|
const djGl = isDjiboutiGl(user);
|
|
|
|
const permissionAllowed = (item: SidebarItem): boolean => {
|
|
if (!item.permission) return true;
|
|
const keys = Array.isArray(item.permission)
|
|
? item.permission
|
|
: [item.permission];
|
|
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);
|
|
};
|
|
|
|
return sections
|
|
.map((section) => ({
|
|
...section,
|
|
items: section.items.filter(itemAllowed),
|
|
}))
|
|
.filter((section) => section.items.length > 0);
|
|
};
|
|
|
|
const APP_TITLE = "EDR Freight Backoffice";
|
|
|
|
/** Flatten sidebar sections (incl. nested children) into {href, label} pairs. */
|
|
const flattenSidebarItems = (
|
|
sections: SidebarSection[],
|
|
): { href: string; label: string }[] =>
|
|
sections.flatMap((section) =>
|
|
section.items.flatMap((item) => [
|
|
...(item.href ? [{ href: item.href, label: item.label }] : []),
|
|
...(item.children ?? [])
|
|
.filter((child): child is SidebarItem & { href: string } =>
|
|
Boolean(child.href),
|
|
)
|
|
.map((child) => ({ href: child.href, label: child.label })),
|
|
]),
|
|
);
|
|
|
|
/** Find the sidebar label whose href matches (exactly or as a prefix of) the current path. */
|
|
const findActiveSidebarLabel = (
|
|
pathname: string,
|
|
sections: SidebarSection[],
|
|
): string | undefined => {
|
|
const path = pathname.toLowerCase();
|
|
const candidates = flattenSidebarItems(sections)
|
|
.map(({ href, label }) => ({
|
|
label,
|
|
href: href.split("?")[0].toLowerCase(),
|
|
}))
|
|
.sort((a, b) => b.href.length - a.href.length);
|
|
|
|
return candidates.find(
|
|
({ href }) => path === href || path.startsWith(`${href}/`),
|
|
)?.label;
|
|
};
|
|
|
|
const DashboardShell = () => {
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const { user, logout } = useAuth();
|
|
|
|
const demoItems: SidebarItem[] = [];
|
|
|
|
const sidebarSections = filterSidebarByPermission(
|
|
buildSidebarSections(demoItems),
|
|
user,
|
|
);
|
|
const displayName = user?.name?.en || user?.username || user?.email || "User";
|
|
|
|
// GL positions are locked to their single clearance page: if they navigate
|
|
// (or deep-link) anywhere else, send them back to their clearance hub.
|
|
// Super admin is exempt. Allow the clearance path + its detail sub-routes.
|
|
const superAdmin = isSuperAdmin(user);
|
|
const glClearanceHome = !superAdmin
|
|
? isEthiopianGl(user)
|
|
? ET_CLEARANCE_HREF
|
|
: isDjiboutiGl(user)
|
|
? DJ_CLEARANCE_HREF
|
|
: null
|
|
: null;
|
|
|
|
useEffect(() => {
|
|
const activeLabel = findActiveSidebarLabel(
|
|
location.pathname,
|
|
sidebarSections,
|
|
);
|
|
document.title = activeLabel ? `${activeLabel} | ${APP_TITLE}` : APP_TITLE;
|
|
}, [location.pathname, sidebarSections]);
|
|
|
|
if (glClearanceHome && !location.pathname.startsWith(glClearanceHome)) {
|
|
return <Navigate to={glClearanceHome} replace />;
|
|
}
|
|
|
|
return (
|
|
<FreightDashboardLayout
|
|
sidebarSections={sidebarSections}
|
|
// GL Ethiopia / GL Djibouti are locked to a single clearance page — no
|
|
// sidebar (or mobile burger) at all; the page renders full width.
|
|
hideSidebar={Boolean(glClearanceHome)}
|
|
activeHref={location.pathname}
|
|
onNavigate={navigate}
|
|
enableThemeToggle
|
|
userName={displayName}
|
|
userEmail={user?.email}
|
|
onLogout={logout}
|
|
>
|
|
<Outlet />
|
|
</FreightDashboardLayout>
|
|
);
|
|
};
|
|
|
|
const App = () => {
|
|
const { user, loading } = useAuth();
|
|
|
|
if (loading) {
|
|
return <LoadingScreen />;
|
|
}
|
|
|
|
if (!user) {
|
|
return (
|
|
<Routes>
|
|
<Route path="/auth" element={<LoginPage />} />
|
|
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
|
|
{/* <Route path="/um/*" element={<UserManagementHostPage />} /> */}
|
|
<Route path="um/set-password" element={<SetPassword />} />
|
|
<Route path="/callback" element={<FaydaCallbackPage />} />
|
|
<Route path="*" element={<Navigate to="/auth" replace />} />
|
|
</Routes>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Routes>
|
|
{UserManagementRoutes()}
|
|
{/* <Route path="/um/*" element={<UserManagementHostPage />} /> */}
|
|
<Route path="/health" element={<HealthCheck />} />
|
|
<Route path="/callback" element={<FaydaCallbackPage />} />
|
|
<Route path="/" element={<Navigate to="/dashboard/overview" replace />} />
|
|
<Route
|
|
path="/dashboard"
|
|
element={<Navigate to="/dashboard/overview" replace />}
|
|
/>
|
|
<Route path="/dashboard" element={<DashboardShell />}>
|
|
<Route path="overview" element={<OverviewPage />} />
|
|
{/* Dev/testing page for the mock AI booking assistant. */}
|
|
<Route
|
|
path="ai-booking-mock-test"
|
|
element={<AiBookingMockTestPage />}
|
|
/>
|
|
<Route path="profile" element={<MyProfilePage />} />
|
|
|
|
<Route path="booking-requests" element={<BookingRequestsPage />} />
|
|
<Route
|
|
path="payments"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.bookings.view}>
|
|
<PaymentsPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route path="support" element={<SupportInboxPage />} />
|
|
<Route path="customers" element={<CustomersPage />} />
|
|
<Route path="customers/:id" element={<CustomerDetailPage />} />
|
|
<Route
|
|
path="invoices"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.bookings.view}>
|
|
<InvoicesPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="invoices/:id"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.bookings.view}>
|
|
<InvoiceDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route path="booking-requests/new" element={<NewBookingPage />} />
|
|
<Route
|
|
path="booking-requests/:id"
|
|
element={<BookingRequestDetailPage />}
|
|
/>
|
|
<Route
|
|
path="booking-requests/:id/contract"
|
|
element={<BookingContractPage />}
|
|
/>
|
|
{/* Legacy booking-based clearance URLs → the contract clearance hub. */}
|
|
<Route
|
|
path="clearance"
|
|
element={<Navigate to="/dashboard/contracts/clearance" replace />}
|
|
/>
|
|
<Route
|
|
path="clearance/:id"
|
|
element={
|
|
<RequirePermission
|
|
permission={FREIGHT_PERMS.bookings.clearanceView}
|
|
>
|
|
<DocumentClearanceDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
|
|
{/* Contracts (Path A/B) */}
|
|
<Route
|
|
path="contract-requests"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.contracts.view}>
|
|
<ContractRequestsPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="contract-requests/:id"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.contracts.view}>
|
|
<ContractRequestDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="contract-requests/:id/view"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.contracts.view}>
|
|
<ContractViewPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="bookings/:bookingId/clearance"
|
|
element={
|
|
<RequirePermission
|
|
permission={FREIGHT_PERMS.bookings.clearanceView}
|
|
>
|
|
<DocumentClearanceDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="shipment-requests"
|
|
element={
|
|
<RequirePermission
|
|
permission={FREIGHT_PERMS.contracts.createBooking}
|
|
>
|
|
<ShipmentRequestsPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="shipment-requests/:id"
|
|
element={
|
|
<RequirePermission
|
|
permission={FREIGHT_PERMS.contracts.createBooking}
|
|
>
|
|
<ShipmentRequestDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
{/* Operations hub: clearance documents for non-customs contracts —
|
|
Contracts tab (contract-level) + General tab (per-booking). */}
|
|
<Route
|
|
path="contracts/clearance-documents"
|
|
element={
|
|
<RequirePermission
|
|
permission={FREIGHT_PERMS.contracts.opsClearanceReview}
|
|
>
|
|
<ClearanceDocumentsPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="contracts/clearance-documents/:id"
|
|
element={
|
|
<RequirePermission
|
|
permission={FREIGHT_PERMS.contracts.opsClearanceReview}
|
|
>
|
|
<ContractClearanceDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
{/* GL (Path B) contract clearance review hub */}
|
|
<Route
|
|
path="contracts/clearance"
|
|
element={
|
|
<RequirePermission
|
|
permission={[
|
|
FREIGHT_PERMS.contracts.clearanceReview,
|
|
FREIGHT_PERMS.contracts.clearanceEtActions,
|
|
]}
|
|
>
|
|
<ContractClearanceListPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="contracts/clearance/:id"
|
|
element={
|
|
<RequirePermission
|
|
permission={[
|
|
FREIGHT_PERMS.contracts.clearanceReview,
|
|
FREIGHT_PERMS.contracts.clearanceEtActions,
|
|
]}
|
|
>
|
|
<ContractClearanceDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="gl-ethiopia/clearance"
|
|
element={<LegacyGlEthiopiaClearanceRedirect />}
|
|
/>
|
|
<Route
|
|
path="gl-ethiopia/clearance/:id"
|
|
element={<LegacyGlEthiopiaClearanceRedirect />}
|
|
/>
|
|
<Route
|
|
path="gl-djibouti/clearance"
|
|
element={
|
|
<RequirePermission
|
|
permission={FREIGHT_PERMS.contracts.clearanceDjActions}
|
|
>
|
|
<GlDjiboutiClearanceListPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="gl-djibouti/clearance/:id"
|
|
element={
|
|
<RequirePermission
|
|
permission={FREIGHT_PERMS.contracts.clearanceDjActions}
|
|
>
|
|
<GlClearanceDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
{/* Path A — Operations reviews per-booking self-clearance documents
|
|
(GENERAL contracts without customs). */}
|
|
<Route
|
|
path="contracts/ops-clearance"
|
|
element={
|
|
<RequirePermission
|
|
permission={FREIGHT_PERMS.contracts.opsClearanceReview}
|
|
>
|
|
<DocumentClearanceListPage opsMode />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="contracts/:id/create-booking"
|
|
element={
|
|
<RequirePermission
|
|
permission={FREIGHT_PERMS.contracts.createBooking}
|
|
>
|
|
<GlCreateBookingForm />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
{/* Completion of an initiated (bare) instance after per-booking
|
|
clearance — same form, submits to the complete endpoint. */}
|
|
<Route
|
|
path="contracts/:id/bookings/:bookingId/complete"
|
|
element={
|
|
<RequirePermission
|
|
permission={FREIGHT_PERMS.contracts.createBooking}
|
|
>
|
|
<GlCreateBookingForm />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="bookings/:id/milestones"
|
|
element={<BookingMilestonesRedirect />}
|
|
/>
|
|
<Route path="warehouses" element={<WarehouseListPage />} />
|
|
<Route path="warehouses/:id" element={<WarehouseDetailPage />} />
|
|
<Route
|
|
path="warehouse-inventory"
|
|
element={<WarehouseInventoryPage />}
|
|
/>
|
|
<Route path="import-warehouse" element={<ImportWarehouseFlowPage />} />
|
|
<Route path="export-warehouse" element={<ExportWarehouseFlowPage />} />
|
|
<Route path="arrival-queue" element={<ArrivalQueuePage />} />
|
|
<Route path="loading-queue" element={<LoadingQueuePage />} />
|
|
<Route path="intercity" element={<IntercityPage />} />
|
|
<Route path="trucks-on-site" element={<TrucksOnSitePage />} />
|
|
<Route path="loaded-inventory" element={<LoadedInventoryPage />} />
|
|
<Route path="dispatch-queue" element={<DispatchQueuePage />} />
|
|
<Route
|
|
path="export-djibouti-unloading"
|
|
element={<ExportDjiboutiUnloadingQueuePage />}
|
|
/>
|
|
<Route
|
|
path="interchange-documents"
|
|
element={<InterchangeDocumentsPage />}
|
|
/>
|
|
<Route path="inventory-inquiry" element={<InventoryInquiryPage />} />
|
|
<Route path="warehouse-rules" element={<WarehouseRulesPage />} />
|
|
<Route
|
|
path="warehouse-fee-invoices"
|
|
element={<WarehouseInvoicesPage />}
|
|
/>
|
|
<Route
|
|
path="warehouse-dashboard"
|
|
element={<WarehouseDashboardPage />}
|
|
/>
|
|
|
|
<Route
|
|
path="operations/train-scheduling"
|
|
element={
|
|
<Navigate to="/dashboard/operations/train-scheduling-v2" replace />
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/batch-board"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.trainScheduling.view}>
|
|
<BatchBoardPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/first-mile"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.firstMile.view}>
|
|
<FirstMilePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/last-mile"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.lastMile.view}>
|
|
<LastMilePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/batch-board/:scheduleId"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.trainScheduling.view}>
|
|
<BatchScheduleDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/train-scheduling-v2"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.trainScheduling.view}>
|
|
<TrainScheduleV2ListPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/train-scheduling-v2/:scheduleId"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.trainScheduling.view}>
|
|
<TrainScheduleV2DetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/train-scheduling-v2/:scheduleId/track"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.trainScheduling.view}>
|
|
<TrainScheduleTrackPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="routes"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<RoutesPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="locomotives"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="trains"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="trains/:id"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<TrainDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="train-builder"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<TrainBuilderListPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="train-builder/:id"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<TrainBuilderDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="wagons"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="containers"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="cargoes"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="vehicles"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.vehicles.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="vehicles/:id"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.vehicles.view}>
|
|
<VehicleDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="drivers"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.drivers.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="drivers/:id"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.drivers.view}>
|
|
<DriverDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/train-scheduling"
|
|
element={
|
|
<Navigate to="/dashboard/operations/train-scheduling-v2" replace />
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/batch-board"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.trainScheduling.view}>
|
|
<BatchBoardPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/batch-board/:scheduleId"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.trainScheduling.view}>
|
|
<BatchScheduleDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/train-scheduling-v2"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.trainScheduling.view}>
|
|
<TrainScheduleV2ListPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/train-scheduling-v2/:scheduleId"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.trainScheduling.view}>
|
|
<TrainScheduleV2DetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="operations/train-scheduling-v2/:scheduleId/track"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.trainScheduling.view}>
|
|
<TrainScheduleTrackPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="routes"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<RoutesPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="fuel-purchases"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fuel.view}>
|
|
<FuelPurchasePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="fuel-stats"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fuel.view}>
|
|
<FuelStatsPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="maintenance"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.maintenance.view}>
|
|
<MaintenancePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="financial-reports"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleetReports.view}>
|
|
<FinancialReportsPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="fleet-dashboard"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleetDashboard.view}>
|
|
<FleetDashboard />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="tracking"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.tracking.view}>
|
|
<TrackingPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="compliance"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<CompliancePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="incidents"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<IncidentsPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="work-orders"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.maintenance.view}>
|
|
<WorkOrdersPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="procurement"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<ProcurementPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="locomotives"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="trains"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="trains/:id"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<TrainDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="train-builder"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<TrainBuilderListPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="train-builder/:id"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<TrainBuilderDetailPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="wagons"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="containers"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="cargoes"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.fleet.view}>
|
|
<FleetResourcePage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
|
|
{/* Legacy embedded user management routes */}
|
|
{/* <Route path="user-management" element={<UserManagementPage />} />
|
|
<Route path="user-management/users" element={<UsersPage />} />
|
|
<Route
|
|
path="user-management/position-types"
|
|
element={<PositionTypesPage />}
|
|
/> */}
|
|
{/* <Route path="user-management/employees" element={<EmployeesPage />} /> */}
|
|
{/* <Route
|
|
path="user-management/permissions"
|
|
element={<PermissionsPage />}
|
|
/>
|
|
<Route path="user-management/roles" element={<RolesPage />} /> */}
|
|
|
|
<Route
|
|
path="file-settings"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.admin}>
|
|
<FileUploadSettingsPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="dropdown-settings"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.admin}>
|
|
<DropdownSettingsPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="contract-templates"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.admin}>
|
|
<ContractTemplatesPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="contract-templates/:code"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.admin}>
|
|
<ContractTemplateEditorPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
|
|
<Route
|
|
path="configuration"
|
|
element={
|
|
<Navigate to="/dashboard/configuration/cargo-types" replace />
|
|
}
|
|
/>
|
|
<Route
|
|
path="configuration/train-scheduling-rules"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.trainScheduling.view}>
|
|
<TrainSchedulingGlobalRulesPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route
|
|
path="configuration/contract-validity-periods"
|
|
element={
|
|
<RequirePermission permission={FREIGHT_PERMS.admin}>
|
|
<ContractValidityPeriodsPage />
|
|
</RequirePermission>
|
|
}
|
|
/>
|
|
<Route path="configuration/cargo-types" element={<CargoTypesPage />} />
|
|
<Route
|
|
path="configuration/cargo-types/:id"
|
|
element={<CargoTypesPage />}
|
|
/>
|
|
<Route
|
|
path="configuration/:resource"
|
|
element={<RuleEngineResourcePage />}
|
|
/>
|
|
|
|
<Route
|
|
path="rules"
|
|
element={<Navigate to="/dashboard/rules/priority-configs" replace />}
|
|
/>
|
|
<Route path="rules/:resource" element={<RuleEngineResourcePage />} />
|
|
|
|
<Route
|
|
path="rule-engine"
|
|
element={
|
|
<Navigate to="/dashboard/configuration/cargo-types" replace />
|
|
}
|
|
/>
|
|
<Route
|
|
path="rule-engine/:resource"
|
|
element={<RuleEngineLegacyRedirect />}
|
|
/>
|
|
<Route path="org-structure" element={<Navigate to="/um" replace />} />
|
|
<Route path="org-structure/*" element={<Navigate to="/um" replace />} />
|
|
</Route>
|
|
|
|
<Route path="*" element={<Navigate to="/dashboard/overview" replace />} />
|
|
</Routes>
|
|
);
|
|
};
|
|
|
|
/** Redirect removed milestones page to document clearance. */
|
|
function BookingMilestonesRedirect() {
|
|
const { id } = useParams();
|
|
return <Navigate to={`/dashboard/bookings/${id}/clearance`} replace />;
|
|
}
|
|
|
|
/** Redirect legacy GL Ethiopia clearance URLs to the unified document clearance hub. */
|
|
function LegacyGlEthiopiaClearanceRedirect() {
|
|
const { id } = useParams();
|
|
if (id) {
|
|
return <Navigate to={`/dashboard/contracts/clearance/${id}`} replace />;
|
|
}
|
|
return <Navigate to="/dashboard/contracts/clearance" replace />;
|
|
}
|
|
|
|
export default App;
|