mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 13:28:11 +00:00
206 lines
5.9 KiB
TypeScript
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>
|
|
);
|
|
}
|