From 9a224e9baf21172e693a43c13aa7c45b01aee37e Mon Sep 17 00:00:00 2001 From: Nathnael Date: Mon, 22 Jun 2026 07:44:03 +0000 Subject: [PATCH] style: WIP ui updates --- .../bookings/detail/booking-detail.styles.ts | 2 +- .../src/components/fleet/FleetToolbar.tsx | 5 +- .../trainScheduling/ScheduleBookingsStep.tsx | 2 +- .../trainScheduling/ScheduleStatusBadge.tsx | 4 +- .../trainScheduling/scheduleVisuals.tsx | 2 +- .../src/components/ui/Breadcrumbs.tsx | 77 +-- .../bookings/BookingRequestDetailPage.tsx | 33 +- .../pages/bookings/BookingRequestsPage.tsx | 22 +- .../src/pages/fleet/FleetResourcePage.tsx | 7 +- .../backoffice/src/pages/fleet/RoutesPage.tsx | 13 +- .../BatchScheduleDetailPage.tsx | 437 ++++++++---------- .../TrainScheduleTrackPage.tsx | 43 +- .../TrainScheduleV2DetailPage.tsx | 216 ++++----- .../pages/warehouses/WarehouseDetailPage.tsx | 217 +++++---- 14 files changed, 515 insertions(+), 565 deletions(-) diff --git a/apps/edr-freight-web/backoffice/src/components/bookings/detail/booking-detail.styles.ts b/apps/edr-freight-web/backoffice/src/components/bookings/detail/booking-detail.styles.ts index 80fc527cb..ccee1e8cb 100644 --- a/apps/edr-freight-web/backoffice/src/components/bookings/detail/booking-detail.styles.ts +++ b/apps/edr-freight-web/backoffice/src/components/bookings/detail/booking-detail.styles.ts @@ -55,7 +55,7 @@ export const detailStyles = { export function approvalStatusColor(status: string): string { switch (status) { case "APPROVED": - return "green"; + return "edr-green"; case "PENDING": return "yellow"; case "REJECTED": diff --git a/apps/edr-freight-web/backoffice/src/components/fleet/FleetToolbar.tsx b/apps/edr-freight-web/backoffice/src/components/fleet/FleetToolbar.tsx index 80138c024..840146ae4 100644 --- a/apps/edr-freight-web/backoffice/src/components/fleet/FleetToolbar.tsx +++ b/apps/edr-freight-web/backoffice/src/components/fleet/FleetToolbar.tsx @@ -1,6 +1,6 @@ -import type { ReactNode } from "react"; -import { LayoutGrid, Plus, Search, Table2 } from "lucide-react"; import { Box, Button, Group, SegmentedControl, TextInput } from "@mantine/core"; +import { LayoutGrid, Plus, Search, Table2 } from "lucide-react"; +import type { ReactNode } from "react"; import type { FleetViewMode } from "./useFleetViewMode"; @@ -67,7 +67,6 @@ const FleetToolbar = ({ onChange={(value) => onViewModeChange(value as FleetViewMode)} size="sm" radius="lg" - color="green" data={[ { value: "table", diff --git a/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleBookingsStep.tsx b/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleBookingsStep.tsx index 987b58e59..7e29fe8f5 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleBookingsStep.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleBookingsStep.tsx @@ -1,4 +1,3 @@ -import { ArrowRight, Package, Train } from "lucide-react"; import { Badge, Button, @@ -8,6 +7,7 @@ import { Tabs, Text, } from "@mantine/core"; +import { ArrowRight, Package, Train } from "lucide-react"; import type { EligibleContainerBooking, FreightType } from "@/types/trainScheduling"; diff --git a/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleStatusBadge.tsx b/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleStatusBadge.tsx index 3610f1203..565187ac2 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleStatusBadge.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleStatusBadge.tsx @@ -3,7 +3,7 @@ import { Badge } from "@mantine/core"; const STATUS_COLORS: Record = { DRAFT: "gray", SCHEDULED: "blue", - DISPATCHED: "green", + DISPATCHED: "edr-green", ARRIVED: "teal", CANCELLED: "red", }; @@ -34,7 +34,7 @@ export function SchedulingStatusBadge({ status }: { status?: string | null }) { HOLDING: "yellow", ELIGIBLE: "blue", SCHEDULED: "indigo", - DISPATCHED: "green", + DISPATCHED: "edr-green", }; return ( diff --git a/apps/edr-freight-web/backoffice/src/components/trainScheduling/scheduleVisuals.tsx b/apps/edr-freight-web/backoffice/src/components/trainScheduling/scheduleVisuals.tsx index ff7417a94..a495a5529 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainScheduling/scheduleVisuals.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainScheduling/scheduleVisuals.tsx @@ -32,7 +32,7 @@ const STATUS_META: Record< { color: string; dot: string; label?: string } > = { DRAFT: { color: "gray", dot: "var(--mantine-color-gray-5)" }, - SCHEDULED: { color: "green", dot: freightBrand.primary }, + SCHEDULED: { color: "edr-green", dot: freightBrand.primary }, DISPATCHED: { color: "teal", dot: "var(--mantine-color-teal-6)" }, ARRIVED: { color: "blue", dot: "var(--mantine-color-blue-6)" }, CANCELLED: { color: "red", dot: "var(--mantine-color-red-6)" }, diff --git a/apps/edr-freight-web/backoffice/src/components/ui/Breadcrumbs.tsx b/apps/edr-freight-web/backoffice/src/components/ui/Breadcrumbs.tsx index 988381f83..690c67cd8 100644 --- a/apps/edr-freight-web/backoffice/src/components/ui/Breadcrumbs.tsx +++ b/apps/edr-freight-web/backoffice/src/components/ui/Breadcrumbs.tsx @@ -1,6 +1,6 @@ -import { Fragment } from "react"; +import { Anchor, Breadcrumbs as MantineBreadcrumbs, Text } from "@mantine/core"; +import { ChevronRight } from "lucide-react"; import { Link } from "react-router-dom"; -import { ChevronRight, Home } from "lucide-react"; export interface BreadcrumbItem { label: string; @@ -12,45 +12,48 @@ export interface BreadcrumbsProps { } export default function Breadcrumbs({ items }: BreadcrumbsProps) { - return ( - + ); } diff --git a/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestDetailPage.tsx index e1dedc304..2e5b9ee0f 100644 --- a/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestDetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestDetailPage.tsx @@ -12,6 +12,7 @@ import { Box, } from "@mantine/core"; +import { PageContainer } from "@/components/page"; import Breadcrumbs from "@/components/ui/Breadcrumbs"; import { ApprovalStepsCard } from "@/components/bookings/ApprovalStepsCard"; import { BookingActionsToolbar } from "@/components/bookings/BookingActionsToolbar"; @@ -61,7 +62,7 @@ export default function BookingRequestDetailPage() { if (isLoading) { return ( - +
@@ -70,13 +71,13 @@ export default function BookingRequestDetailPage() {
-
+ ); } if (isError || !booking) { return ( - +
@@ -111,7 +112,7 @@ export default function BookingRequestDetailPage() { - + ); } @@ -127,16 +128,15 @@ export default function BookingRequestDetailPage() { booking.status === "APPROVED_PENDING_SIGNATURE"; return ( - - - + + - + } onClick={() => navigate(`/dashboard/booking-requests/${booking.id}/contract`) @@ -206,8 +206,7 @@ export default function BookingRequestDetailPage() { - - - + + ); } diff --git a/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestsPage.tsx b/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestsPage.tsx index 38d1acec6..348ed2085 100644 --- a/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestsPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestsPage.tsx @@ -2,7 +2,6 @@ import { useCallback, useMemo, useRef, useState } from "react"; import { useNavigate } from "react-router-dom"; import { ArrowRight, Calendar, Package, Search, User, X } from "lucide-react"; import { - Container, Stack, Group, Text, @@ -12,6 +11,7 @@ import { Tabs, } from "@mantine/core"; +import { PageContainer } from "@/components/page"; import Breadcrumbs from "@/components/ui/Breadcrumbs"; import { BookingPriorityBadge } from "@/components/bookings/BookingPriorityBadge"; import { @@ -297,11 +297,10 @@ export default function BookingRequestsPage() { ]; return ( -
- - + + - + - + ) : null} - -
+ ); } diff --git a/apps/edr-freight-web/backoffice/src/pages/fleet/FleetResourcePage.tsx b/apps/edr-freight-web/backoffice/src/pages/fleet/FleetResourcePage.tsx index f246ad194..dfaf52fac 100644 --- a/apps/edr-freight-web/backoffice/src/pages/fleet/FleetResourcePage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/fleet/FleetResourcePage.tsx @@ -3,6 +3,7 @@ import { Navigate, useLocation } from "react-router-dom"; import type { ColumnDef } from "@edr/ui-common"; import { Box, Button, Card, Group, Modal, Select, Stack, Text } from "@mantine/core"; +import { PageContainer } from "@/components/page"; import FleetCardGrid from "@/components/fleet/FleetCardGrid"; import FleetFormDialog from "@/components/fleet/FleetFormDialog"; import FleetRecordActions from "@/components/fleet/FleetRecordActions"; @@ -291,8 +292,8 @@ const FleetResourcePage = () => { const itemLabel = config.label.toLowerCase(); return ( - - + + { - + ); }; diff --git a/apps/edr-freight-web/backoffice/src/pages/fleet/RoutesPage.tsx b/apps/edr-freight-web/backoffice/src/pages/fleet/RoutesPage.tsx index bd4a3364c..72ef9c965 100644 --- a/apps/edr-freight-web/backoffice/src/pages/fleet/RoutesPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/fleet/RoutesPage.tsx @@ -17,6 +17,7 @@ import { Tooltip, } from "@mantine/core"; +import { PageContainer } from "@/components/page"; import FleetToolbar from "@/components/fleet/FleetToolbar"; import { useFleetViewMode } from "@/components/fleet/useFleetViewMode"; import RuleEngineListFooter from "@/components/ruleEngine/RuleEngineListFooter"; @@ -241,7 +242,7 @@ export default function RoutesPage() { header: "Status", meta: { headerClassName, cellClassName }, cell: ({ row }) => ( - + {row.original.isActive ? "Active" : "Inactive"} ), @@ -279,8 +280,8 @@ export default function RoutesPage() { }, [deactivateMutation.isPending]); return ( - - + + {route.name} - + {route.isActive ? "Active" : "Inactive"} @@ -431,7 +432,7 @@ export default function RoutesPage() { - @@ -484,6 +485,6 @@ export default function RoutesPage() { ) : null} - + ); } diff --git a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchScheduleDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchScheduleDetailPage.tsx index 311f76124..f78639359 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchScheduleDetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchScheduleDetailPage.tsx @@ -1,5 +1,4 @@ import { useEffect, useMemo, useState } from "react"; -import type { ReactNode } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { Accordion, @@ -8,13 +7,10 @@ import { Badge, Box, Button, - Container, Group, Loader, Paper, - SimpleGrid, Stack, - Table, Tabs, Text, ThemeIcon, @@ -41,8 +37,11 @@ import { Weight, XCircle, } from "lucide-react"; -import type { LucideIcon } from "lucide-react"; +import { DataTable, type ColumnDef } from "@edr/ui-common"; + +import { KpiStrip, PageContainer } from "@/components/page"; +import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles"; import Breadcrumbs from "@/components/ui/Breadcrumbs"; import { TrainCompositionDiagram } from "@/components/trainScheduling/TrainCompositionDiagram"; import { TrainConsistView, CompositionBookingTabs } from "@/components/trainScheduling/compositionEditor"; @@ -52,8 +51,6 @@ import { totalBookingCount, WindowStatusPill, } from "@/components/trainScheduling/batchVisuals"; -import { MiniRing, MiniSparkline } from "@/components/common/MiniGraph"; -import type { OverviewAccent } from "@/components/overview/overview.styles"; import { RouteCorridor } from "@/components/trainScheduling/scheduleVisuals"; import { useBatchBoardDetail, @@ -72,7 +69,7 @@ const STATE_META: Record< BatchBoardBookingState, { label: string; color: string; icon: typeof CheckCircle2 } > = { - ALLOCATED: { label: "Allocated", color: "green", icon: CheckCircle2 }, + ALLOCATED: { label: "Allocated", color: "edr-green", icon: CheckCircle2 }, SELECTED_FOR_BATCH: { label: "Selected for batch", color: "orange", icon: Clock }, READY: { label: "Ready for batch", color: "teal", icon: Hourglass }, WAITING: { label: "Paid · waiting", color: "blue", icon: Hourglass }, @@ -84,7 +81,7 @@ const ALLOC_META: Record< BookingAllocationStatus, { label: string; color: string } > = { - ASSIGNED: { label: "Wagons assigned", color: "green" }, + ASSIGNED: { label: "Wagons assigned", color: "edr-green" }, NOT_ATTEMPTED: { label: "Not allocated", color: "gray" }, DEFERRED: { label: "Deferred", color: "orange" }, FAILED: { label: "Allocation failed", color: "red" }, @@ -151,140 +148,153 @@ function AllocationBadge({ ); } -function BookingTable({ bookings }: { bookings: BatchBoardBookingDetail[] }) { - if (!bookings.length) { - return ( - - No bookings in this batch window. +const bookingCellMeta = { + headerClassName: ruleEngineTable.headerCell, + cellClassName: ruleEngineTable.bodyCell, +}; + +const BOOKING_COLUMNS: ColumnDef[] = [ + { + id: "reference", + header: "Reference", + meta: bookingCellMeta, + cell: ({ row }) => { + const b = row.original; + return ( + + + {b.reference} + + {b.isGovernment ? ( + + Gov + + ) : null} + + ); + }, + }, + { + id: "customer", + header: "Customer", + meta: bookingCellMeta, + cell: ({ row }) => { + const b = row.original; + return ( + + + + {initials(b.company)} + + + + {b.company} + + + ); + }, + }, + { + id: "contractSigned", + header: "Contract signed", + meta: bookingCellMeta, + cell: ({ row }) => ( + + {fmtDateTime(row.original.fullyExecutedAt)} EAT - ); - } - - const th = (label: string) => ( - - {label} - - ); + ), + }, + { + id: "selectedForBatch", + header: "Selected for batch", + meta: bookingCellMeta, + cell: ({ row }) => { + const b = row.original; + if (!b.selectedForBatchAt) { + return ( + + — + + ); + } + return ( + <> + + {fmtDateTime(b.selectedForBatchAt)} EAT + + {b.paymentDeadline ? ( + + Pay by {fmtDateTime(b.paymentDeadline)} EAT + + ) : null} + + ); + }, + }, + { + id: "capacity", + header: "Capacity", + meta: bookingCellMeta, + cell: ({ row }) => { + const b = row.original; + return ( + + + {b.wagons}w + + + {fmtTons(b.weightTons)} + + + ); + }, + }, + { + id: "state", + header: "Batch state", + meta: bookingCellMeta, + cell: ({ row }) => , + }, + { + id: "allocation", + header: "Wagon allocation", + meta: bookingCellMeta, + cell: ({ row }) => ( + + ), + }, +]; +function BookingTable({ bookings }: { bookings: BatchBoardBookingDetail[] }) { return ( - - - - - {th("Reference")} - {th("Customer")} - {th("Contract signed")} - {th("Selected for batch")} - {th("Capacity")} - {th("Batch state")} - {th("Wagon allocation")} - - - - {bookings.map((b) => ( - - - - - {b.reference} - - {b.isGovernment ? ( - - Gov - - ) : null} - - - - - - - {initials(b.company)} - - - - {b.company} - - - - - - {fmtDateTime(b.fullyExecutedAt)} EAT - - - - {b.selectedForBatchAt ? ( - <> - - {fmtDateTime(b.selectedForBatchAt)} EAT - - {b.paymentDeadline ? ( - - Pay by {fmtDateTime(b.paymentDeadline)} EAT - - ) : null} - - ) : ( - - — - - )} - - - - - {b.wagons}w - - - {fmtTons(b.weightTons)} - - - - - - - - - - - ))} - -
-
+ ); } function WindowCountChips({ counts }: { counts: BatchWindowGroup["counts"] }) { const chips: Array<{ value: number; color: string; label: string }> = [ - { value: counts.allocated, color: "green", label: "allocated" }, + { value: counts.allocated, color: "edr-green", label: "allocated" }, { value: counts.selectedForBatch, color: "orange", label: "selected" }, { value: counts.ready, color: "teal", label: "ready" }, { value: counts.waiting, color: "blue", label: "waiting" }, @@ -415,66 +425,6 @@ function WindowAccordionItem({ window }: { window: BatchWindowGroup }) { ); } -/** - * Light stat card matching the overview look — keeps the explicit numbers. - * Capacity cards (pct set) show a ring; count cards show an area/line trend. - */ -function StatCard({ - icon: Icon, - label, - value, - sub, - pct, - variant = "area", -}: { - icon: LucideIcon; - label: string; - value: ReactNode; - sub?: string; - pct?: number | null; - variant?: "area" | "line"; -}) { - const ringAccent: OverviewAccent = - pct == null ? "gold" : pct >= 100 ? "rose" : pct >= 85 ? "orange" : "gold"; - - return ( - - - - - - - - - {value} - - - {label} - {sub ? ` · ${sub}` : ""} - - - {pct != null ? ( - - - {Math.round(pct)}% - - - ) : null} - - - {pct == null ? ( - - ) : null} - - - ); -} - export default function BatchScheduleDetailPage() { const { scheduleId } = useParams<{ scheduleId: string }>(); const navigate = useNavigate(); @@ -621,11 +571,11 @@ export default function BatchScheduleDetailPage() { if (isLoading || !data) { return ( - + - + - + ); } @@ -641,7 +591,7 @@ export default function BatchScheduleDetailPage() { const totalBookings = totalBookingCount(data.counts); return ( - + - + Overview @@ -673,7 +623,7 @@ export default function BatchScheduleDetailPage() { > Back - + <Title order={2} fw={800}> {data.trainNumber ?? data.routeName ?? "Schedule"} @@ -715,7 +665,7 @@ export default function BatchScheduleDetailPage() { Refresh - {yards.length === 0 ? ( - - No yards yet. - - ) : ( - - - - Name - Code - Type - Weight (cur / cap) - Containers (cur / cap) - Status - Actions - - - - {yards.map((yard) => ( - - {yard.name} - {yard.code} - {humanizeEnum(yard.type)} - {formatCapacity(yard.currentWeight, yard.capacityWeight)} - {formatCapacity(yard.currentContainers, yard.capacityContainers)} - - - - - { - setEditingYard(yard); - setYardModalOpen(true); - }} - > - - - - - ))} - -
- )} + void yardsQuery.refetch(), + } + : undefined + } + /> @@ -234,50 +295,28 @@ export default function WarehouseDetailPage() { Select a yard to view its zones. - ) : (zonesQuery.data ?? []).length === 0 ? ( - - No zones in this yard yet. - ) : ( - - - - Name - Code - Type - Weight (cur / cap) - Containers (cur / cap) - Status - Actions - - - - {(zonesQuery.data ?? []).map((zone) => ( - - {zone.name} - {zone.code} - {humanizeEnum(zone.type)} - {formatCapacity(zone.currentWeight, zone.capacityWeight)} - {formatCapacity(zone.currentContainers, zone.capacityContainers)} - - - - - { - setEditingZone(zone); - setZoneModalOpen(true); - }} - > - - - - - ))} - -
+ void zonesQuery.refetch(), + } + : undefined + } + /> )}