aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Enmarcha.Backend/Controllers/ArrivalsController.cs19
-rw-r--r--src/Enmarcha.Backend/Types/Arrivals/Arrival.cs6
-rw-r--r--src/frontend/app/api/schema.ts8
-rw-r--r--src/frontend/app/components/arrivals/ArrivalCard.tsx43
-rw-r--r--src/frontend/app/components/arrivals/ReducedArrivalCard.tsx30
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;