From 97908d274ee12eb2301fadd5fc445d0f79479a56 Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Sat, 4 Apr 2026 15:44:41 +0200 Subject: Enhance arrival and transit functionality with new vehicle operation logic and transit kind classification --- src/frontend/app/api/schema.ts | 1 + .../app/components/arrivals/ArrivalCard.tsx | 122 +++++++++++++-------- .../app/components/arrivals/ReducedArrivalCard.tsx | 46 +++++++- src/frontend/app/routes/home.tsx | 2 - src/frontend/app/routes/stops-$id.tsx | 3 + 5 files changed, 125 insertions(+), 49 deletions(-) (limited to 'src/frontend/app') diff --git a/src/frontend/app/api/schema.ts b/src/frontend/app/api/schema.ts index 40358a6..4d34a44 100644 --- a/src/frontend/app/api/schema.ts +++ b/src/frontend/app/api/schema.ts @@ -66,6 +66,7 @@ export const ArrivalSchema = z.object({ currentPosition: PositionSchema.optional().nullable(), vehicleInformation: VehicleInformationSchema.optional().nullable(), operator: z.string().nullable(), + operation: z.enum(["pickup_dropoff", "pickup_only", "dropoff_only"]), }); export const ArrivalEstimateSchema = z.object({ diff --git a/src/frontend/app/components/arrivals/ArrivalCard.tsx b/src/frontend/app/components/arrivals/ArrivalCard.tsx index ec14492..827599e 100644 --- a/src/frontend/app/components/arrivals/ArrivalCard.tsx +++ b/src/frontend/app/components/arrivals/ArrivalCard.tsx @@ -1,4 +1,11 @@ -import { AlertTriangle, BusFront, LocateIcon, Navigation } from "lucide-react"; +import { + AlertTriangle, + ArrowDownRightSquare, + ArrowUpRightSquare, + BusFront, + LocateIcon, + Navigation, +} from "lucide-react"; import React, { useEffect, useMemo, useRef, useState } from "react"; import Marquee from "react-fast-marquee"; import { useTranslation } from "react-i18next"; @@ -71,10 +78,10 @@ export const ArrivalCard: React.FC = ({ shift, vehicleInformation, operator, + operation, } = arrival; const etaValue = estimate.minutes.toString(); - const etaUnit = t("estimates.minutes", "min"); const timeClass = useMemo(() => { switch (estimate.precision) { @@ -93,9 +100,27 @@ export const ArrivalCard: React.FC = ({ const chips: Array<{ label: string; tone?: string; - kind?: "regular" | "gps" | "delay" | "warning" | "vehicle"; + kind?: + | "regular" + | "gps" + | "delay" + | "warning" + | "vehicle" + | "pickup" + | "dropoff"; }> = []; + if (operation !== "pickup_dropoff") { + chips.push({ + label: + operation === "pickup_only" + ? t("journey.pickup_only", "Solo subida") + : t("journey.dropoff_only", "Solo bajada"), + tone: operation === "pickup_only" ? "pickup" : "dropoff", + kind: operation === "pickup_only" ? "pickup" : "dropoff", + }); + } + // Badge/Shift info as a chip if (headsign.badge) { chips.push({ @@ -154,14 +179,6 @@ export const ArrivalCard: React.FC = ({ }); } - if (estimate.precision === "scheduled") { - chips.push({ - label: t("estimates.no_realtime"), - tone: "warning", - kind: "warning", - }); - } - // Vehicle information if available if (vehicleInformation) { let label = vehicleInformation.identifier; @@ -240,6 +257,14 @@ export const ArrivalCard: React.FC = ({ {metaChips.map((chip, idx) => { let chipColourClasses = ""; switch (chip.tone) { + case "pickup": + chipColourClasses = + "bg-green-600/10 dark:bg-green-600/20 text-green-700 dark:text-green-300"; + break; + case "dropoff": + chipColourClasses = + "bg-orange-400/10 dark:bg-orange-600/20 text-orange-700 dark:text-orange-300"; + break; case "delay-ok": chipColourClasses = "bg-green-600/10 dark:bg-green-600/20 text-green-700 dark:text-green-300"; @@ -279,47 +304,56 @@ export const ArrivalCard: React.FC = ({ {chip.kind === "vehicle" && ( )} + {chip.kind === "pickup" && ( + + )} + {chip.kind === "dropoff" && ( + + )} + {chip.label} ); })} - {onTrack && estimate.precision !== "past" && ( - // Use a instead of a