diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2025-12-22 22:06:06 +0100 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2025-12-22 22:06:06 +0100 |
| commit | bed48c3d7e49b1736d50ce42d92bb6c18cf02504 (patch) | |
| tree | 475571ad6fa8c7aa1f8e81520689bf1eb425164c /src/frontend/app/components/arrivals | |
| parent | 68f49dec91d68579803d6d579b1f1ecb4fc1dd1f (diff) | |
Refactor arrivals handling and improve type definitions; reorganise components
Diffstat (limited to 'src/frontend/app/components/arrivals')
| -rw-r--r-- | src/frontend/app/components/arrivals/ArrivalCard.css | 17 | ||||
| -rw-r--r-- | src/frontend/app/components/arrivals/ArrivalCard.tsx | 74 | ||||
| -rw-r--r-- | src/frontend/app/components/arrivals/ArrivalList.tsx | 25 |
3 files changed, 116 insertions, 0 deletions
diff --git a/src/frontend/app/components/arrivals/ArrivalCard.css b/src/frontend/app/components/arrivals/ArrivalCard.css new file mode 100644 index 0000000..5835352 --- /dev/null +++ b/src/frontend/app/components/arrivals/ArrivalCard.css @@ -0,0 +1,17 @@ +@import "../../tailwind.css"; + +.time-running { + @apply bg-green-600/20 dark:bg-green-600/25 text-[#1a9e56] dark:text-[#22c55e]; +} + +.time-delayed { + @apply bg-orange-600/20 dark:bg-orange-600/25 text-[#d06100] dark:text-[#fb923c]; +} + +.time-past { + @apply bg-gray-600/20 dark:bg-gray-600/25 text-gray-600 dark:text-gray-400; +} + +.time-scheduled { + @apply bg-blue-900/20 dark:bg-blue-600/25 text-[#0b3d91] dark:text-[#93c5fd]; +} diff --git a/src/frontend/app/components/arrivals/ArrivalCard.tsx b/src/frontend/app/components/arrivals/ArrivalCard.tsx new file mode 100644 index 0000000..de4fcc7 --- /dev/null +++ b/src/frontend/app/components/arrivals/ArrivalCard.tsx @@ -0,0 +1,74 @@ +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; + reduced?: boolean; +} + +export const ArrivalCard: React.FC<ArrivalCardProps> = ({ + arrival, + reduced, +}) => { + const { t } = useTranslation(); + const { route, headsign, estimate } = 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]); + + return ( + <div + className={` + flex-none flex items-center gap-2.5 min-h-12 + bg-(--message-background-color) border border-(--border-color) + rounded-xl px-3 py-2.5 transition-all + ${reduced ? "reduced" : ""} + `.trim()} + > + <div className="shrink-0 min-w-[7ch]"> + <LineIcon + line={route.shortName} + colour={route.colour} + textColour={route.textColour} + mode="pill" + /> + </div> + <div className="flex-1 min-w-0 flex flex-col gap-1"> + <strong + className={`text-base overflow-hidden text-ellipsis line-clamp-2 leading-tight ${estimate.precision == "past" ? "line-through" : ""}`} + > + {headsign.destination} + </strong> + </div> + <div + className={` + inline-flex items-center justify-center px-2 py-1.5 rounded-xl shrink-0 + ${timeClass} + `.trim()} + > + <div className="flex flex-col items-center leading-none"> + <span className="text-lg font-bold leading-none">{etaValue}</span> + <span className="text-[0.65rem] uppercase tracking-wider mt-0.5 opacity-90"> + {etaUnit} + </span> + </div> + </div> + </div> + ); +}; diff --git a/src/frontend/app/components/arrivals/ArrivalList.tsx b/src/frontend/app/components/arrivals/ArrivalList.tsx new file mode 100644 index 0000000..a1210d5 --- /dev/null +++ b/src/frontend/app/components/arrivals/ArrivalList.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import { type Arrival } from "../../api/schema"; +import { ArrivalCard } from "./ArrivalCard"; + +interface ArrivalListProps { + arrivals: Arrival[]; + reduced?: boolean; +} + +export const ArrivalList: React.FC<ArrivalListProps> = ({ + arrivals, + reduced, +}) => { + return ( + <div className="flex flex-col gap-3"> + {arrivals.map((arrival, index) => ( + <ArrivalCard + key={`${arrival.route.shortName}-${index}`} + arrival={arrival} + reduced={reduced} + /> + ))} + </div> + ); +}; |
