Files
edr-platform/apps/edr-freight-web/backoffice/src/App.tsx
2026-07-20 11:41:57 +00:00

1469 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 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 />,
},
{
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="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;