summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAriel Costas Guerrero <ariel@costas.dev>2026-03-16 16:44:43 +0100
committerAriel Costas Guerrero <ariel@costas.dev>2026-03-16 16:44:43 +0100
commitfbcf5ecb762b3cff314b9339aec3314bb813a3fe (patch)
tree7a4237ddb06188decf35aedd60f13c5c078429dc
parentac626a9c2edc2e528eb0b39002c836a747b2fc16 (diff)
Display operator name on xunta arrivals
-rw-r--r--src/Enmarcha.Backend/Controllers/ArrivalsController.cs3
-rw-r--r--src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs2
-rw-r--r--src/Enmarcha.Backend/Types/Arrivals/Arrival.cs13
-rw-r--r--src/Enmarcha.Sources.OpenTripPlannerGql/Queries/ArrivalsAtStop.cs9
-rw-r--r--src/frontend/app/api/schema.ts1
-rw-r--r--src/frontend/app/components/arrivals/ArrivalCard.tsx25
-rw-r--r--src/frontend/app/components/arrivals/ReducedArrivalCard.tsx27
7 files changed, 69 insertions, 11 deletions
diff --git a/src/Enmarcha.Backend/Controllers/ArrivalsController.cs b/src/Enmarcha.Backend/Controllers/ArrivalsController.cs
index 861038f..8ce63f7 100644
--- a/src/Enmarcha.Backend/Controllers/ArrivalsController.cs
+++ b/src/Enmarcha.Backend/Controllers/ArrivalsController.cs
@@ -146,6 +146,8 @@ public partial class ArrivalsController : ControllerBase
var tz = TimeZoneInfo.FindSystemTimeZoneById("Europe/Madrid");
var nowLocal = TimeZoneInfo.ConvertTime(DateTime.UtcNow, tz);
+ var feedId = id.Split(':')[0];
+
var requestContent = ArrivalsAtStopContent.Query(new ArrivalsAtStopContent.Args(id, reduced || nano));
var request = new HttpRequestMessage(HttpMethod.Post, $"{_config.OpenTripPlannerBaseUrl}/gtfs/v1");
@@ -192,6 +194,7 @@ public partial class ArrivalsController : ControllerBase
Minutes = minutesToArrive,
Precision = departureTime < nowLocal.AddMinutes(-1) ? ArrivalPrecision.Past : ArrivalPrecision.Scheduled
},
+ Operator = feedId == "xunta" ? item.Trip.Route.Agency?.Name : null,
RawOtpTrip = item
});
}
diff --git a/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs b/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs
index 84e0d0f..6a76fe3 100644
--- a/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs
+++ b/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs
@@ -94,7 +94,7 @@ public class NextStopsProcessor : IArrivalsProcessor
if (!seenConcellos.Contains(concello))
{
seenConcellos.Add(concello);
- item += $" ({concello})";
+ item = $"({concello}) {item}";
}
items.Add(item);
diff --git a/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs b/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs
index a07c988..bdcae07 100644
--- a/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs
+++ b/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs
@@ -20,6 +20,9 @@ public class Arrival
[JsonPropertyName("currentPosition")] public Position? CurrentPosition { get; set; }
+ [JsonPropertyName("operator")] public string? Operator { get; set; }
+ [JsonPropertyName("operation")] public VehicleOperation Operation { get; set; } = VehicleOperation.PickupDropoff;
+
[JsonPropertyName("vehicleInformation")]
public VehicleBadge? VehicleInformation { get; set; }
@@ -35,6 +38,16 @@ public class Arrival
[JsonIgnore] public bool Delete { get; set; }
}
+public enum VehicleOperation
+{
+ [JsonStringEnumMemberName("pickup_dropoff")]
+ PickupDropoff = 0,
+ [JsonStringEnumMemberName("pickup_only")]
+ PickupOnly = 1,
+ [JsonStringEnumMemberName("dropoff_only")]
+ DropoffOnly = 2
+}
+
public class RouteInfo
{
[JsonPropertyName("gtfsId")] public required string GtfsId { get; set; }
diff --git a/src/Enmarcha.Sources.OpenTripPlannerGql/Queries/ArrivalsAtStop.cs b/src/Enmarcha.Sources.OpenTripPlannerGql/Queries/ArrivalsAtStop.cs
index 7cf107a..2ad30e5 100644
--- a/src/Enmarcha.Sources.OpenTripPlannerGql/Queries/ArrivalsAtStop.cs
+++ b/src/Enmarcha.Sources.OpenTripPlannerGql/Queries/ArrivalsAtStop.cs
@@ -43,6 +43,9 @@ public class ArrivalsAtStopContent : IGraphRequest<ArrivalsAtStopContent.Args>
color
textColor
longName
+ agency {{
+ name
+ }}
}}
departureStoptime {{
scheduledDeparture
@@ -190,6 +193,12 @@ public class ArrivalsAtStopResponse : AbstractGraphResponse
[JsonPropertyName("textColor")] public string? TextColor { get; set; }
[JsonPropertyName("longName")] public string? LongName { get; set; }
+ [JsonPropertyName("agency")] public AgencyDetails? Agency { get; set; }
+ }
+
+ public class AgencyDetails
+ {
+ [JsonPropertyName("name")] public required string Name { get; set; }
}
public class PickupType
diff --git a/src/frontend/app/api/schema.ts b/src/frontend/app/api/schema.ts
index f68d413..20daede 100644
--- a/src/frontend/app/api/schema.ts
+++ b/src/frontend/app/api/schema.ts
@@ -65,6 +65,7 @@ export const ArrivalSchema = z.object({
shape: z.any().optional().nullable(),
currentPosition: PositionSchema.optional().nullable(),
vehicleInformation: VehicleInformationSchema.optional().nullable(),
+ operator: z.string().nullable(),
});
export const ArrivalEstimateSchema = z.object({
diff --git a/src/frontend/app/components/arrivals/ArrivalCard.tsx b/src/frontend/app/components/arrivals/ArrivalCard.tsx
index b99d3aa..bdd20a5 100644
--- a/src/frontend/app/components/arrivals/ArrivalCard.tsx
+++ b/src/frontend/app/components/arrivals/ArrivalCard.tsx
@@ -59,8 +59,15 @@ export const ArrivalCard: React.FC<ArrivalCardProps> = ({
onClick,
}) => {
const { t } = useTranslation();
- const { route, headsign, estimate, delay, shift, vehicleInformation } =
- arrival;
+ const {
+ route,
+ headsign,
+ estimate,
+ delay,
+ shift,
+ vehicleInformation,
+ operator,
+ } = arrival;
const etaValue = estimate.minutes.toString();
const etaUnit = t("estimates.minutes", "min");
@@ -211,11 +218,15 @@ export const ArrivalCard: React.FC<ArrivalCardProps> = ({
>
{headsign.destination}
</span>
- {headsign.marquee && (
- <div className="mt-0.5 w-auto">
- <AutoMarquee text={headsign.marquee} />
- </div>
- )}
+ <div className="mt-0.5 w-auto flex">
+ {operator && (
+ <span className="text-xs font-mono text-slate-700 dark:text-slate-200 font-medium shrink-0">
+ {operator}
+ {headsign.marquee && <>&nbsp;ยท&nbsp;</>}
+ </span>
+ )}
+ {headsign.marquee && <AutoMarquee text={headsign.marquee} />}
+ </div>
</div>
</div>
</div>
diff --git a/src/frontend/app/components/arrivals/ReducedArrivalCard.tsx b/src/frontend/app/components/arrivals/ReducedArrivalCard.tsx
index 27d97b3..19cc8d9 100644
--- a/src/frontend/app/components/arrivals/ReducedArrivalCard.tsx
+++ b/src/frontend/app/components/arrivals/ReducedArrivalCard.tsx
@@ -15,8 +15,15 @@ export const ReducedArrivalCard: React.FC<ArrivalCardProps> = ({
onClick,
}) => {
const { t } = useTranslation();
- const { route, headsign, estimate, delay, shift, vehicleInformation } =
- arrival;
+ const {
+ route,
+ headsign,
+ estimate,
+ delay,
+ shift,
+ vehicleInformation,
+ operator,
+ } = arrival;
const etaValue = estimate.minutes.toString();
const etaUnit = t("estimates.minutes", "min");
@@ -41,6 +48,13 @@ export const ReducedArrivalCard: React.FC<ArrivalCardProps> = ({
kind?: "regular" | "gps" | "delay" | "warning" | "vehicle";
}> = [];
+ if (operator) {
+ chips.push({
+ label: operator,
+ kind: "regular",
+ });
+ }
+
// Badge/Shift info as a chip
if (headsign.badge) {
chips.push({
@@ -130,7 +144,14 @@ export const ReducedArrivalCard: React.FC<ArrivalCardProps> = ({
}
return chips;
- }, [delay, shift, estimate.precision, headsign.badge, vehicleInformation]);
+ }, [
+ delay,
+ shift,
+ estimate.precision,
+ headsign.badge,
+ vehicleInformation,
+ operator,
+ ]);
const isClickable = !!onClick && estimate.precision !== "past";
const Tag = isClickable ? "button" : "div";