diff --git a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchBoardPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchBoardPage.tsx index 4ee2a7d96..32ef990d9 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchBoardPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchBoardPage.tsx @@ -1,12 +1,13 @@ import { useMemo, useState } from "react"; import { useNavigate } from "react-router-dom"; import { + ActionIcon, Alert, Box, Button, Card, - Container, Group, + Menu, Paper, RingProgress, Select, @@ -21,7 +22,9 @@ import { ArrowRight, CalendarClock, CalendarDays, + Eye, Inbox, + MoreHorizontal, Package, Ruler, Train, @@ -31,7 +34,7 @@ import { import type { ColumnDef } from "@edr/ui-common"; import { DataTable, DataTableFooter, usePagination } from "@edr/ui-common"; -import Breadcrumbs from "@/components/ui/Breadcrumbs"; +import { KpiStrip, PageContainer, PageHeader } from "@/components/page"; import FleetToolbar from "@/components/fleet/FleetToolbar"; import { useFleetViewMode } from "@/components/fleet/useFleetViewMode"; import { @@ -40,7 +43,7 @@ import { totalBookingCount, WindowStatusPill, } from "@/components/trainScheduling/batchVisuals"; -import { RouteCorridor, StatTile } from "@/components/trainScheduling/scheduleVisuals"; +import { RouteCorridor } from "@/components/trainScheduling/scheduleVisuals"; import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles"; import { FREIGHT_BRAND, FREIGHT_BRAND_DARK } from "@/theme/freight-brand"; import { useBatchBoard } from "@/hooks/trainScheduling/useTrainScheduling"; @@ -365,7 +368,7 @@ function CardSkeleton() { export default function BatchBoardPage() { const navigate = useNavigate(); - const { data, isLoading, isFetching, refetch } = useBatchBoard(); + const { data, isLoading, isError, isFetching, refetch } = useBatchBoard(); const { viewMode, setViewMode } = useFleetViewMode("batch-board"); const { pagination, setPagination } = usePagination({ pageSize: 10 }); const [search, setSearch] = useState(""); @@ -549,72 +552,69 @@ export default function BatchBoardPage() { header: "", meta: { headerClassName, cellClassName: `${cellClassName} whitespace-nowrap` }, cell: ({ row }) => ( - - + e.stopPropagation()}> + + + + + + + + } + onClick={() => + navigate(`/dashboard/operations/batch-board/${row.original.scheduleId}`) + } + > + View windows + + + ), }, ]; }, [navigate]); - const tableStatus = isLoading ? "loading" : "success"; + const tableStatus = isLoading ? "loading" : isError ? "error" : "success"; return ( - - + + void refetch()}> + Refresh + + } + /> - - - - - + - + + navigate(`/dashboard/operations/batch-board/${schedule.scheduleId}`) + } + error={ + isError + ? { + message: "Failed to load the batch board.", + onRetry: () => void refetch(), + } + : undefined + } emptyMessage="No active schedules" pagination={{ pageIndex: pagination.pageIndex, @@ -688,7 +699,6 @@ export default function BatchBoardPage() { borderRadius: 20, background: "white", border: "1px solid var(--mantine-color-gray-2)", - boxShadow: "0 4px 12px rgba(15,23,42,0.06)", }} > @@ -711,18 +721,6 @@ export default function BatchBoardPage() { )} - - - - - + ); } diff --git a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2ListPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2ListPage.tsx index 6a2e77287..7d5c49270 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2ListPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2ListPage.tsx @@ -3,10 +3,12 @@ import { useNavigate } from "react-router-dom"; import { isAxiosError } from "axios"; import type { ColumnDef } from "@edr/ui-common"; import { + ActionIcon, Box, Button, Card, Group, + Menu, Modal, Select, SimpleGrid, @@ -15,16 +17,25 @@ import { TextInput, ThemeIcon, } from "@mantine/core"; -import { ArrowRight, CalendarClock, Navigation, Send, Train, Weight } from "lucide-react"; +import { + ArrowRight, + Ban, + CalendarClock, + Eye, + MoreHorizontal, + Navigation, + Send, + Train, + Weight, +} from "lucide-react"; +import { KpiStrip, PageContainer, PageHeader } from "@/components/page"; import FleetToolbar from "@/components/fleet/FleetToolbar"; import { useFleetViewMode } from "@/components/fleet/useFleetViewMode"; import { FreightTypeBadge } from "@/components/trainScheduling/ScheduleStatusBadge"; import { RouteCorridor, - StatTile, StatusPill, - scheduleBrand, } from "@/components/trainScheduling/scheduleVisuals"; import RuleEngineListFooter from "@/components/ruleEngine/RuleEngineListFooter"; import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles"; @@ -253,61 +264,65 @@ export default function TrainScheduleV2ListPage() { id: "actions", header: "Actions", meta: { headerClassName, cellClassName: `${cellClassName} whitespace-nowrap` }, - cell: ({ row }) => ( - - - {["DISPATCHED", "ARRIVED"].includes(row.original.status) ? ( - - ) : null} - {["DRAFT", "SCHEDULED"].includes(row.original.status) ? ( - - ) : null} - - ), + cell: ({ row }) => { + const schedule = row.original; + return ( + e.stopPropagation()}> + + + + + + + + } + onClick={() => + navigate(`/dashboard/operations/train-scheduling-v2/${schedule.id}`) + } + > + View detail + + {["DISPATCHED", "ARRIVED"].includes(schedule.status) ? ( + } + onClick={() => + navigate( + `/dashboard/operations/train-scheduling-v2/${schedule.id}/track`, + ) + } + > + Track + + ) : null} + {["DRAFT", "SCHEDULED"].includes(schedule.status) ? ( + } + onClick={async () => { + try { + await cancel.mutateAsync({ + id: schedule.id, + freightType: schedule.freightType ?? "CONTAINER", + }); + toast({ title: "Schedule cancelled" }); + } catch (err) { + toast({ + title: "Cancel failed", + description: parseError(err, "Could not cancel"), + variant: "destructive", + }); + } + }} + > + Cancel schedule + + ) : null} + + + + ); + }, }, ]; }, [navigate, cancel.isPending, cancel, toast]); @@ -340,65 +355,33 @@ export default function TrainScheduleV2ListPage() { : "success"; return ( - - - - + + } onClick={() => setCreateOpen(true)}> + New schedule + + } + /> - - - - - - + - + setCreateOpen(true)} viewMode={viewMode} onViewModeChange={setViewMode} filters={ @@ -442,6 +425,17 @@ export default function TrainScheduleV2ListPage() { columns={columns} data={paged} status={tableStatus} + onRowClick={(schedule) => + navigate(`/dashboard/operations/train-scheduling-v2/${schedule.id}`) + } + error={ + schedulesQuery.isError + ? { + message: "Failed to load train schedules.", + onRetry: () => void schedulesQuery.refetch(), + } + : undefined + } emptyMessage="No train schedules found" pagination={{ pageIndex: pagination.pageIndex, @@ -554,13 +548,13 @@ export default function TrainScheduleV2ListPage() { - - + ); } @@ -613,31 +607,15 @@ function ScheduleCard({ const canTrack = ["DISPATCHED", "ARRIVED"].includes(schedule.status); return ( { - e.currentTarget.style.boxShadow = scheduleBrand.shadowSm; - e.currentTarget.style.transform = "translateY(-2px)"; - }} - onMouseLeave={(e) => { - e.currentTarget.style.boxShadow = ""; - e.currentTarget.style.transform = ""; - }} + className="cursor-pointer overflow-hidden transition-[transform,border-color] duration-150 hover:-translate-y-0.5 hover:border-edr-primary!" > - {/* accent strip */} - - + diff --git a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainSchedulingGlobalRulesPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainSchedulingGlobalRulesPage.tsx index 65d3e4c21..e72dc5d41 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainSchedulingGlobalRulesPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainSchedulingGlobalRulesPage.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from "react"; -import { Button, Card, Group, NumberInput, Stack, Text, Title } from "@mantine/core"; +import { Button, Card, Group, NumberInput, Stack } from "@mantine/core"; +import { PageContainer, PageHeader } from "@/components/page"; import { trainSchedulingService } from "@/services/trainScheduling.service"; import { useToast } from "@/hooks/use-toast"; import type { TrainSchedulingGlobalRules } from "@/types/trainScheduling"; @@ -44,15 +45,13 @@ export default function TrainSchedulingGlobalRulesPage() { }; return ( - - - Train scheduling rules - - Global limits applied when previewing and assigning bookings to trains. - - + + - + - - + ); }