style: WIP BatchBoard and train schedule

This commit is contained in:
Nathnael
2026-06-22 06:38:05 +00:00
parent 3dfb8d99ba
commit e6101c5514
3 changed files with 189 additions and 214 deletions

View File

@@ -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 }) => (
<Group justify="flex-end" wrap="nowrap">
<Button
variant="light"
color="green"
size="compact-sm"
rightSection={<ArrowRight size={14} />}
onClick={() =>
navigate(`/dashboard/operations/batch-board/${row.original.scheduleId}`)
}
>
View windows
</Button>
<Group justify="flex-end" wrap="nowrap" onClick={(e) => e.stopPropagation()}>
<Menu position="bottom-end" withinPortal shadow="md" width={180}>
<Menu.Target>
<ActionIcon variant="subtle" color="gray" aria-label="Row actions">
<MoreHorizontal size={16} />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
leftSection={<Eye size={15} />}
onClick={() =>
navigate(`/dashboard/operations/batch-board/${row.original.scheduleId}`)
}
>
View windows
</Menu.Item>
</Menu.Dropdown>
</Menu>
</Group>
),
},
];
}, [navigate]);
const tableStatus = isLoading ? "loading" : "success";
const tableStatus = isLoading ? "loading" : isError ? "error" : "success";
return (
<Container fluid py="lg" px="xl">
<Breadcrumbs items={[{ label: "Operations" }, { label: "Batch board" }]} />
<PageContainer>
<PageHeader
title="Batch Board"
subtitle="Active schedules and their booking windows."
action={
<Button variant="default" loading={isFetching} onClick={() => void refetch()}>
Refresh
</Button>
}
/>
<SimpleGrid cols={{ base: 1, xs: 3 }} spacing="md" mt="md">
<StatTile
icon={Train}
label="Active schedules"
value={isLoading ? "…" : schedules.length}
hint="on the board right now"
accent="#F2A516"
graph="area"
graphAccent="gold"
/>
<StatTile
icon={CalendarDays}
label="Open windows"
value={isLoading ? "…" : summary.openWindows}
hint="accepting bookings"
accent="#FB8C2E"
graph="line"
graphAccent="orange"
/>
<StatTile
icon={Package}
label="Bookings in play"
value={isLoading ? "…" : summary.totalBookings}
hint={`${summary.totalWagons} wagons allocated`}
accent="#F2A516"
graph="ring"
graphAccent="gold"
graphPct={
schedules.length
? Math.min(100, Math.round((summary.openWindows / schedules.length) * 100))
: 0
}
/>
</SimpleGrid>
<KpiStrip
loading={isLoading}
items={[
{
label: "Active schedules",
value: schedules.length,
hint: "on the board right now",
icon: Train,
},
{
label: "Open windows",
value: summary.openWindows,
hint: "accepting bookings",
icon: CalendarDays,
},
{
label: "Bookings in play",
value: summary.totalBookings,
hint: `${summary.totalWagons} wagons allocated`,
icon: Package,
},
]}
/>
<Card
radius="lg"
padding={0}
withBorder
mt="lg"
style={{ borderColor: "var(--mantine-color-gray-2)" }}
>
<Card p={0}>
<Stack gap={0}>
<Box px="md" pt="md" pb="sm" w="100%">
<FleetToolbar
@@ -647,6 +647,17 @@ export default function BatchBoardPage() {
columns={columns}
data={paged}
status={tableStatus}
onRowClick={(schedule) =>
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)",
}}
>
<Inbox size={28} color="var(--mantine-color-gray-5)" />
@@ -711,18 +721,6 @@ export default function BatchBoardPage() {
)}
</Stack>
</Card>
<Group justify="flex-end" mt="md">
<Button
variant="subtle"
color="gray"
size="xs"
loading={isFetching}
onClick={() => void refetch()}
>
Refresh
</Button>
</Group>
</Container>
</PageContainer>
);
}