Files
edr-platform/apps/edr-freight-web/backoffice/src/components/trainScheduling/TrackStatusCard.tsx
2026-08-27 07:45:32 +00:00

206 lines
5.9 KiB
TypeScript

import { Box, Group, RingProgress, Stack, Text } from "@mantine/core";
import { ArrowRight, CircleDot, Flag, Navigation } from "lucide-react";
import type { LucideIcon } from "lucide-react";
import { statusMeta } from "@/components/trainScheduling/scheduleVisuals";
import { Chip } from "./trackPrimitives";
import { track } from "./trackTheme";
export interface TrackStatValue {
icon: LucideIcon;
label: string;
value: string;
}
/**
* Left-rail identity card: gradient cap (train number, progress ring, status,
* current station) over the route strip and the stat list.
*/
export function TrackStatusCard({
trainNumber,
direction,
status,
progressPct,
reached,
totalStations,
currentStation,
stateLine,
origin,
destination,
stats,
}: {
trainNumber?: string | null;
direction?: string | null;
status: string;
progressPct: number;
reached: number;
totalStations: number;
currentStation: string;
stateLine: string;
origin: string | null;
destination: string | null;
stats: TrackStatValue[];
}) {
return (
<Box
style={{
background: track.surface,
border: `1px solid ${track.border}`,
borderRadius: 16,
overflow: "hidden",
}}
>
<Stack gap={18} p="22px 22px 20px" style={{ background: track.capGradient }}>
<Group gap={12} align="center" wrap="nowrap">
<Box
style={{
width: 44,
height: 44,
borderRadius: 13,
display: "flex",
alignItems: "center",
justifyContent: "center",
background: "rgba(255,255,255,0.18)",
border: "1px solid rgba(255,255,255,0.36)",
color: "white",
flexShrink: 0,
}}
>
<Navigation size={21} />
</Box>
<Stack gap={4} style={{ minWidth: 0, flex: 1 }}>
<Text fw={700} fz={19} c="white" lh={1.2} truncate>
{trainNumber ?? "Train tracking"}
</Text>
<Text
fz={9.5}
fw={700}
tt="uppercase"
style={{ letterSpacing: 1, color: "rgba(255,255,255,0.72)" }}
>
Train tracking
</Text>
</Stack>
{direction ? (
<Chip
bg="rgba(255,255,255,0.16)"
fg="#FFFFFF"
border="rgba(255,255,255,0.36)"
>
{direction}
</Chip>
) : null}
</Group>
<Group gap={18} align="center" wrap="nowrap">
<RingProgress
size={104}
thickness={9}
roundCaps
sections={[{ value: progressPct, color: "white" }]}
rootColor="rgba(255,255,255,0.24)"
label={
<Stack gap={1} align="center">
<Text fw={700} fz={23} lh={1} c="white">
{Math.round(progressPct)}%
</Text>
<Text
fz={8.5}
fw={700}
tt="uppercase"
style={{ letterSpacing: 0.7, color: "rgba(255,255,255,0.78)" }}
>
{reached}/{totalStations} stops
</Text>
</Stack>
}
/>
<Stack gap={9} style={{ minWidth: 0, flex: 1 }}>
<Box
style={{
display: "inline-flex",
alignItems: "center",
gap: 7,
padding: "6px 12px",
borderRadius: 999,
background: "white",
width: "fit-content",
}}
>
<Box
style={{
width: 7,
height: 7,
borderRadius: 999,
background: statusMeta(status).dot,
}}
/>
<Text fz={10.5} fw={700} c={track.brandDark} style={{ letterSpacing: 0.6 }}>
{status}
</Text>
</Box>
<Text
fz={11.5}
fw={600}
style={{ letterSpacing: 0.4, color: "rgba(255,255,255,0.72)" }}
>
{stateLine}
</Text>
<Text fz={16} fw={700} c="white" truncate>
{currentStation}
</Text>
</Stack>
</Group>
</Stack>
<Group
gap={10}
px={20}
py={14}
wrap="nowrap"
align="center"
style={{
background: track.surface2,
borderBottom: `1px solid ${track.borderSoft}`,
}}
>
<CircleDot size={14} color={track.brand} style={{ flexShrink: 0 }} />
<Text size="12.5px" fw={600} c={track.text} truncate>
{origin ?? "—"}
</Text>
<Box style={{ flex: 1 }} />
<ArrowRight size={14} color={track.text3} style={{ flexShrink: 0 }} />
<Box style={{ flex: 1 }} />
<Text size="12.5px" fw={600} c={track.text} truncate>
{destination ?? "—"}
</Text>
<Flag size={13} color={track.muted} style={{ flexShrink: 0 }} />
</Group>
<Stack gap={0} px={20} pt={6} pb={14}>
{stats.map((s, i) => {
const Icon = s.icon;
return (
<Group
key={s.label}
gap={10}
py={11}
wrap="nowrap"
align="center"
style={i ? { borderTop: `1px solid ${track.borderSoft}` } : undefined}
>
<Icon size={15} color={track.muted} style={{ flexShrink: 0 }} />
<Text size="12.5px" c={track.text2} style={{ flex: 1, minWidth: 0 }}>
{s.label}
</Text>
<Text size="12.5px" fw={700} c={track.text} style={{ flexShrink: 0 }}>
{s.value}
</Text>
</Group>
);
})}
</Stack>
</Box>
);
}