diff options
| -rw-r--r-- | src/Enmarcha.Backend/Controllers/ArrivalsController.cs | 19 | ||||
| -rw-r--r-- | src/Enmarcha.Backend/Types/Arrivals/Arrival.cs | 6 | ||||
| -rw-r--r-- | src/frontend/app/api/schema.ts | 8 | ||||
| -rw-r--r-- | src/frontend/app/components/arrivals/ArrivalCard.tsx | 43 | ||||
| -rw-r--r-- | src/frontend/app/components/arrivals/ReducedArrivalCard.tsx | 30 |
5 files changed, 85 insertions, 21 deletions
diff --git a/src/Enmarcha.Backend/Controllers/ArrivalsController.cs b/src/Enmarcha.Backend/Controllers/ArrivalsController.cs index 7feeee0..5608723 100644 --- a/src/Enmarcha.Backend/Controllers/ArrivalsController.cs +++ b/src/Enmarcha.Backend/Controllers/ArrivalsController.cs @@ -140,8 +140,23 @@ public partial class ArrivalsController : ControllerBase return Ok(new StopEstimatesResponse { Arrivals = estimates }); } - private static VehicleOperation GetVehicleOperation(ArrivalsAtStopResponse.PickupType pickup, ArrivalsAtStopResponse.PickupType dropoff) + private static VehicleOperation GetVehicleOperation( + ArrivalsAtStopResponse.Arrival item + ) { + var pickup = item.PickupTypeParsed; + var dropoff = item.DropoffTypeParsed; + + if (item.StopPosition == 0) + { + return VehicleOperation.Departure; + } + + if (item.StopPosition == item.Trip.Stoptimes.Count - 1) + { + return VehicleOperation.Arrival; + } + if (pickup == ArrivalsAtStopResponse.PickupType.None && dropoff == ArrivalsAtStopResponse.PickupType.None) return VehicleOperation.PickupDropoff; if (pickup != ArrivalsAtStopResponse.PickupType.None && dropoff != ArrivalsAtStopResponse.PickupType.None) return VehicleOperation.PickupDropoff; if (pickup != ArrivalsAtStopResponse.PickupType.None) return VehicleOperation.PickupOnly; @@ -215,7 +230,7 @@ public partial class ArrivalsController : ControllerBase }, Operator = feedId == "xunta" ? item.Trip.Route.Agency?.Name : null, RawOtpTrip = item, - Operation = GetVehicleOperation(item.PickupTypeParsed, item.DropoffTypeParsed) + Operation = GetVehicleOperation(item) }); } diff --git a/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs b/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs index 81811c2..0e74a44 100644 --- a/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs +++ b/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs @@ -46,7 +46,11 @@ public enum VehicleOperation [JsonStringEnumMemberName("pickup_only")] PickupOnly = 1, [JsonStringEnumMemberName("dropoff_only")] - DropoffOnly = 2 + DropoffOnly = 2, + [JsonStringEnumMemberName("departure")] + Departure = 3, + [JsonStringEnumMemberName("arrival")] + Arrival = 4 } public class RouteInfo diff --git a/src/frontend/app/api/schema.ts b/src/frontend/app/api/schema.ts index 64a9e94..71eeae9 100644 --- a/src/frontend/app/api/schema.ts +++ b/src/frontend/app/api/schema.ts @@ -66,7 +66,13 @@ 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"]), + operation: z.enum([ + "pickup_dropoff", + "pickup_only", + "dropoff_only", + "departure", + "arrival", + ]), }); export const ArrivalEstimateSchema = z.object({ diff --git a/src/frontend/app/components/arrivals/ArrivalCard.tsx b/src/frontend/app/components/arrivals/ArrivalCard.tsx index 827599e..51e0803 100644 --- a/src/frontend/app/components/arrivals/ArrivalCard.tsx +++ b/src/frontend/app/components/arrivals/ArrivalCard.tsx @@ -5,13 +5,18 @@ import { BusFront, LocateIcon, Navigation, + SquareArrowRightEnter, + SquareArrowRightExit, } from "lucide-react"; import React, { useEffect, useMemo, useRef, useState } from "react"; -import Marquee from "react-fast-marquee"; +import _MarqueeImport from "react-fast-marquee"; import { useTranslation } from "react-i18next"; import RouteIcon from "~/components/RouteIcon"; import { type Arrival } from "../../api/schema"; import "./ArrivalCard.css"; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const Marquee: typeof _MarqueeImport = + (_MarqueeImport as any).default ?? _MarqueeImport; interface ArrivalCardProps { arrival: Arrival; @@ -96,6 +101,22 @@ export const ArrivalCard: React.FC<ArrivalCardProps> = ({ } }, [estimate.precision]); + const OPERATION_LABELS: Record<string, string> = { + pickup_only: t("journey.pickup_only", "Solo subida"), + dropoff_only: t("journey.dropoff_only", "Solo bajada"), + departure: t("journey.departure", "Salida"), + arrival: t("journey.arrival", "Llegada"), + }; + + type OperationKind = "pickup" | "dropoff" | "departure" | "arrival"; + + const OPERATION_KINDS: Record<string, OperationKind> = { + pickup_only: "pickup", + dropoff_only: "dropoff", + departure: "departure", + arrival: "arrival", + }; + const metaChips = useMemo(() => { const chips: Array<{ label: string; @@ -106,18 +127,14 @@ export const ArrivalCard: React.FC<ArrivalCardProps> = ({ | "delay" | "warning" | "vehicle" - | "pickup" - | "dropoff"; + | OperationKind; }> = []; 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", + label: OPERATION_LABELS[operation] || operation, + tone: OPERATION_KINDS[operation] || "regular", + kind: OPERATION_KINDS[operation] || "regular", }); } @@ -258,10 +275,12 @@ export const ArrivalCard: React.FC<ArrivalCardProps> = ({ let chipColourClasses = ""; switch (chip.tone) { case "pickup": + case "departure": chipColourClasses = "bg-green-600/10 dark:bg-green-600/20 text-green-700 dark:text-green-300"; break; case "dropoff": + case "arrival": chipColourClasses = "bg-orange-400/10 dark:bg-orange-600/20 text-orange-700 dark:text-orange-300"; break; @@ -310,6 +329,12 @@ export const ArrivalCard: React.FC<ArrivalCardProps> = ({ {chip.kind === "dropoff" && ( <ArrowDownRightSquare className="w-3 h-3 inline-block" /> )} + {chip.kind === "departure" && ( + <SquareArrowRightExit className="w-3 h-3 inline-block" /> + )} + {chip.kind === "arrival" && ( + <SquareArrowRightEnter className="w-3 h-3 inline-block" /> + )} {chip.label} </span> diff --git a/src/frontend/app/components/arrivals/ReducedArrivalCard.tsx b/src/frontend/app/components/arrivals/ReducedArrivalCard.tsx index 6046ffc..6d6bf85 100644 --- a/src/frontend/app/components/arrivals/ReducedArrivalCard.tsx +++ b/src/frontend/app/components/arrivals/ReducedArrivalCard.tsx @@ -47,6 +47,22 @@ export const ReducedArrivalCard: React.FC<ArrivalCardProps> = ({ } }, [estimate.precision]); + const OPERATION_LABELS: Record<string, string> = { + pickup_only: t("journey.pickup_only", "Solo subida"), + dropoff_only: t("journey.dropoff_only", "Solo bajada"), + departure: t("journey.departure", "Salida"), + arrival: t("journey.arrival", "Llegada"), + }; + + type OperationKind = "pickup" | "dropoff" | "departure" | "arrival"; + + const OPERATION_KINDS: Record<string, OperationKind> = { + pickup_only: "pickup", + dropoff_only: "dropoff", + departure: "departure", + arrival: "arrival", + }; + const metaChips = useMemo(() => { const chips: Array<{ label: string; @@ -57,18 +73,14 @@ export const ReducedArrivalCard: React.FC<ArrivalCardProps> = ({ | "delay" | "warning" | "vehicle" - | "pickup" - | "dropoff"; + | OperationKind; }> = []; 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", + label: OPERATION_LABELS[operation] || operation, + tone: OPERATION_KINDS[operation] || "regular", + kind: OPERATION_KINDS[operation] || "regular", }); } @@ -210,10 +222,12 @@ export const ReducedArrivalCard: React.FC<ArrivalCardProps> = ({ let chipColourClasses = ""; switch (chip.tone) { case "pickup": + case "departure": chipColourClasses = "bg-green-600/10 dark:bg-green-600/20 text-green-700 dark:text-green-300"; break; case "dropoff": + case "arrival": chipColourClasses = "bg-orange-400/10 dark:bg-orange-600/20 text-orange-700 dark:text-orange-300"; break; |
