From 4a866f5352a51916ddb9849b2d68213856196c9c Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Tue, 23 Dec 2025 21:33:17 +0100 Subject: Full real-time page, coruña real time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/components/arrivals/ReducedArrivalCard.tsx | 198 +++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 src/frontend/app/components/arrivals/ReducedArrivalCard.tsx (limited to 'src/frontend/app/components/arrivals/ReducedArrivalCard.tsx') diff --git a/src/frontend/app/components/arrivals/ReducedArrivalCard.tsx b/src/frontend/app/components/arrivals/ReducedArrivalCard.tsx new file mode 100644 index 0000000..2c1ea20 --- /dev/null +++ b/src/frontend/app/components/arrivals/ReducedArrivalCard.tsx @@ -0,0 +1,198 @@ +import { AlertTriangle, LocateIcon } from "lucide-react"; +import React, { useMemo } from "react"; +import { useTranslation } from "react-i18next"; +import LineIcon from "~/components/LineIcon"; +import { type Arrival } from "../../api/schema"; +import "./ArrivalCard.css"; + +interface ArrivalCardProps { + arrival: Arrival; + onClick?: () => void; +} + +export const ReducedArrivalCard: React.FC = ({ + arrival, + onClick, +}) => { + const { t } = useTranslation(); + const { route, headsign, estimate, delay, shift } = arrival; + + const etaValue = estimate.minutes.toString(); + const etaUnit = t("estimates.minutes", "min"); + + const timeClass = useMemo(() => { + switch (estimate.precision) { + case "confident": + return "time-running"; + case "unsure": + return "time-delayed"; + case "past": + return "time-past"; + default: + return "time-scheduled"; + } + }, [estimate.precision]); + + const metaChips = useMemo(() => { + const chips: Array<{ + label: string; + tone?: string; + kind?: "regular" | "gps" | "delay" | "warning"; + }> = []; + + // Badge/Shift info as a chip + if (headsign.badge) { + chips.push({ + label: headsign.badge, + kind: "regular", + }); + } + + // Delay chip + if (delay) { + const delta = Math.round(delay.minutes); + const absDelta = Math.abs(delta); + + if (delta === 0) { + chips.push({ + label: "OK", + tone: "delay-ok", + kind: "delay", + }); + } else if (delta > 0) { + const tone = + delta <= 2 + ? "delay-ok" + : delta <= 10 + ? "delay-warn" + : "delay-critical"; + chips.push({ + label: `R${delta}`, + tone, + kind: "delay", + }); + } else { + const tone = absDelta <= 2 ? "delay-ok" : "delay-early"; + chips.push({ + label: `A${absDelta}`, + tone, + kind: "delay", + }); + } + } + + // Shift chip + if (shift) { + chips.push({ + label: `${shift.shiftName} · ${shift.shiftTrip}`, + kind: "regular", + }); + } + + // Precision chips + if (estimate.precision === "unsure") { + chips.push({ + label: "!", + tone: "warning", + kind: "warning", + }); + } else if (estimate.precision === "confident") { + chips.push({ + label: "", // Just the icon for reduced + kind: "gps", + }); + } + + return chips; + }, [delay, shift, estimate.precision, headsign.badge]); + + const isClickable = !!onClick && estimate.precision !== "past"; + const Tag = isClickable ? "button" : "div"; + + return ( + +
+ +
+
+ + {headsign.destination} + + {metaChips.length > 0 && ( +
+ {metaChips.map((chip, idx) => { + let chipColourClasses = ""; + switch (chip.tone) { + case "delay-ok": + chipColourClasses = + "bg-green-600/10 dark:bg-green-600/20 text-green-700 dark:text-green-300"; + break; + case "delay-warn": + chipColourClasses = + "bg-amber-600/10 dark:bg-yellow-600/20 text-amber-700 dark:text-yellow-300"; + break; + case "delay-critical": + chipColourClasses = + "bg-red-400/10 dark:bg-red-600/20 text-red-600 dark:text-red-300"; + break; + case "delay-early": + chipColourClasses = + "bg-blue-400/10 dark:bg-blue-600/20 text-blue-700 dark:text-blue-300"; + break; + case "warning": + chipColourClasses = + "bg-orange-400/10 dark:bg-orange-600/20 text-orange-700 dark:text-orange-300"; + break; + default: + chipColourClasses = + "bg-black/[0.04] dark:bg-white/[0.08] text-slate-500 dark:text-slate-400"; + } + + return ( + + {chip.kind === "gps" && ( + + )} + {chip.kind === "warning" && ( + + )} + {chip.label} + + ); + })} +
+ )} +
+
+
+ {etaValue} + + {etaUnit} + +
+
+
+ ); +}; -- cgit v1.3