aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app/components
diff options
context:
space:
mode:
authorAriel Costas Guerrero <ariel@costas.dev>2025-11-03 11:20:27 +0100
committerAriel Costas Guerrero <ariel@costas.dev>2025-11-03 11:20:27 +0100
commit769d12a525875d3577b2868208d6315c56ce77d6 (patch)
tree4fa84a86a2eda352b6bea14e6ecab741134de62a /src/frontend/app/components
parent809941dcc8f37967a22516b1c4f7af1f3b8a82bc (diff)
Display available lines on StopSheet
Diffstat (limited to 'src/frontend/app/components')
-rw-r--r--src/frontend/app/components/LineIcon.css37
-rw-r--r--src/frontend/app/components/LineIcon.tsx8
-rw-r--r--src/frontend/app/components/StopSheet.css6
-rw-r--r--src/frontend/app/components/StopSheet.tsx29
4 files changed, 63 insertions, 17 deletions
diff --git a/src/frontend/app/components/LineIcon.css b/src/frontend/app/components/LineIcon.css
index 7d46b98..b52e566 100644
--- a/src/frontend/app/components/LineIcon.css
+++ b/src/frontend/app/components/LineIcon.css
@@ -1,6 +1,7 @@
/* Vigo line colors */
:root {
--line-vigo-c1: rgb(237, 71, 19);
+ --line-vigo-c1-text: #ffffff;
--line-vigo-c3d: rgb(255, 204, 0);
--line-vigo-c3i: rgb(255, 204, 0);
--line-vigo-l4a: rgb(0, 153, 0);
@@ -11,7 +12,9 @@
--line-vigo-l7: rgb(150, 220, 153);
--line-vigo-l9b: rgb(244, 202, 140);
--line-vigo-l10: rgb(153, 51, 0);
+ --line-vigo-l10-text: #ffffff;
--line-vigo-l11: rgb(226, 0, 38);
+ --line-vigo-l11-text: #ffffff;
--line-vigo-l12a: rgb(106, 150, 190);
--line-vigo-l12b: rgb(106, 150, 190);
--line-vigo-l13: rgb(0, 176, 240);
@@ -22,8 +25,11 @@
--line-vigo-l16: rgb(129, 142, 126);
--line-vigo-l17: rgb(214, 245, 31);
--line-vigo-l18a: rgb(212, 80, 168);
- --line-vigo-l18b: rgb(0, 0, 0);
- --line-vigo-l18h: rgb(0, 0, 0);
+ --line-vigo-l18a-text: #ffffff;
+ --line-vigo-l18b: rgb(212, 80, 168);
+ --line-vigo-l18b-text: #ffffff;
+ --line-vigo-l18h: rgb(212, 80, 168);
+ --line-vigo-l18h-text: #ffffff;
--line-vigo-l23: rgb(0, 70, 210);
--line-vigo-l24: rgb(191, 191, 191);
--line-vigo-l25: rgb(172, 100, 4);
@@ -32,6 +38,7 @@
--line-vigo-l29: rgb(248, 184, 90);
--line-vigo-l31: rgb(255, 255, 0);
--line-vigo-a: rgb(119, 41, 143);
+ --line-vigo-a-text: #ffffff;
--line-vigo-h: rgb(0, 96, 168);
--line-vigo-h1: rgb(0, 96, 168);
--line-vigo-h2: rgb(0, 96, 168);
@@ -39,6 +46,7 @@
--line-vigo-lzd: rgb(61, 78, 167);
--line-vigo-n1: rgb(191, 191, 191);
--line-vigo-n4: rgb(102, 51, 102);
+ --line-vigo-n4-text: #ffffff;
--line-vigo-psa1: rgb(0, 153, 0);
--line-vigo-psa4: rgb(0, 153, 0);
--line-vigo-ptl: rgb(150, 220, 153);
@@ -56,12 +64,18 @@
--line-santiago-l9: #46b8bb;
--line-santiago-c11: #aec741;
--line-santiago-l12: #842e14;
+ --line-santiago-l12-text: #ffffff;
--line-santiago-l13: #336600;
+ --line-santiago-l13-text: #ffffff;
--line-santiago-l15: #7a4b2a;
+ --line-santiago-l15-text: #ffffff;
--line-santiago-c2: #283a87;
+ --line-santiago-c2-text: #ffffff;
--line-santiago-c4: #283a87;
+ --line-santiago-c4-text: #ffffff;
--line-santiago-c5: #999999;
--line-santiago-c6: #006666;
+ --line-santiago-c6-text: #ffffff;
--line-santiago-p1: #537eb3;
--line-santiago-p2: #d23354;
--line-santiago-p3: #75bd96;
@@ -76,13 +90,30 @@
display: inline-block;
padding: 0.25rem 0.5rem;
margin-right: 0.5rem;
- border-bottom: 3px solid;
font-size: 0.9rem;
font-weight: 600;
text-transform: uppercase;
border-radius: 0.25rem 0.25rem 0 0;
color: var(--text-color);
background-color: var(--background-color);
+
+ border-bottom: 3px solid;
+ border-color: var(--line-colour);
}
+.line-icon-rounded {
+ display: block;
+ width: 54px;
+ height: 54px;
+ box-sizing: border-box;
+
+ background-color: var(--line-colour);
+ color: var(--line-text-colour);
+ padding: 1.25rem 0.5rem;
+ text-align: center;
+ border-radius: 50%;
+
+ font: 600 14px/1 monospace;
+ letter-spacing: 0.05em;
+}
diff --git a/src/frontend/app/components/LineIcon.tsx b/src/frontend/app/components/LineIcon.tsx
index 4f4bfd9..55e38c2 100644
--- a/src/frontend/app/components/LineIcon.tsx
+++ b/src/frontend/app/components/LineIcon.tsx
@@ -5,18 +5,20 @@ import { type RegionId } from "../data/RegionConfig";
interface LineIconProps {
line: string;
region?: RegionId;
+ rounded?: boolean;
}
-const LineIcon: React.FC<LineIconProps> = ({ line, region = "vigo" }) => {
+const LineIcon: React.FC<LineIconProps> = ({ line, region = "vigo", rounded = false }) => {
const formattedLine = useMemo(() => {
return /^[a-zA-Z]/.test(line) ? line : `L${line}`;
}, [line]);
const cssVarName = `--line-${region}-${formattedLine.toLowerCase()}`;
+ const cssTextVarName = `--line-${region}-${formattedLine.toLowerCase()}-text`;
return (
<span
- className="line-icon"
- style={{ borderColor: `var(${cssVarName})` }}
+ className={rounded ? 'line-icon-rounded' : 'line-icon'}
+ style={{ '--line-colour': `var(${cssVarName})`, '--line-text-colour': `var(${cssTextVarName}, unset)` } as React.CSSProperties}
>
{formattedLine}
</span>
diff --git a/src/frontend/app/components/StopSheet.css b/src/frontend/app/components/StopSheet.css
index d3c0b06..931dcc3 100644
--- a/src/frontend/app/components/StopSheet.css
+++ b/src/frontend/app/components/StopSheet.css
@@ -29,6 +29,11 @@
color: var(--subtitle-color);
}
+.stop-sheet-lines-container {
+ display: flex;
+ gap: 0.75rem;
+}
+
.stop-sheet-loading {
display: flex;
justify-content: center;
@@ -42,6 +47,7 @@
flex: 1;
overflow-y: auto;
min-height: 0;
+ margin-block-start: 1.25rem;
}
.stop-sheet-subtitle {
diff --git a/src/frontend/app/components/StopSheet.tsx b/src/frontend/app/components/StopSheet.tsx
index 8080220..afa530b 100644
--- a/src/frontend/app/components/StopSheet.tsx
+++ b/src/frontend/app/components/StopSheet.tsx
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
import { Sheet } from "react-modal-sheet";
import { Link } from "react-router";
import { useTranslation } from "react-i18next";
-import { Clock, ClockFading, Hourglass, RefreshCw } from "lucide-react";
+import { Clock, RefreshCw } from "lucide-react";
import LineIcon from "./LineIcon";
import { StopSheetSkeleton } from "./StopSheetSkeleton";
import { ErrorDisplay } from "./ErrorDisplay";
@@ -10,12 +10,12 @@ import { type Estimate } from "../routes/estimates-$id";
import { REGIONS, type RegionId, getRegionConfig } from "../data/RegionConfig";
import { useApp } from "../AppContext";
import "./StopSheet.css";
+import type { Stop } from "~/data/StopDataProvider";
interface StopSheetProps {
isOpen: boolean;
onClose: () => void;
- stopId: number;
- stopName: string;
+ stop: Stop;
}
interface ErrorInfo {
@@ -42,8 +42,7 @@ const loadStopData = async (region: RegionId, stopId: number): Promise<Estimate[
export const StopSheet: React.FC<StopSheetProps> = ({
isOpen,
onClose,
- stopId,
- stopName,
+ stop
}) => {
const { t } = useTranslation();
const { region } = useApp();
@@ -77,7 +76,7 @@ export const StopSheet: React.FC<StopSheetProps> = ({
setError(null);
setData(null);
- const stopData = await loadStopData(region, stopId);
+ const stopData = await loadStopData(region, stop.stopId);
setData(stopData);
setLastUpdated(new Date());
} catch (err) {
@@ -89,10 +88,10 @@ export const StopSheet: React.FC<StopSheetProps> = ({
};
useEffect(() => {
- if (isOpen && stopId) {
+ if (isOpen && stop.stopId) {
loadData();
}
- }, [isOpen, stopId, region]);
+ }, [isOpen, stop.stopId, region]);
const formatTime = (minutes: number) => {
if (minutes > 15) {
@@ -133,8 +132,16 @@ export const StopSheet: React.FC<StopSheetProps> = ({
<Sheet.Content>
<div className="stop-sheet-content">
<div className="stop-sheet-header">
- <h2 className="stop-sheet-title">{stopName}</h2>
- <span className="stop-sheet-id">({stopId})</span>
+ <h2 className="stop-sheet-title">{stop.name.original}</h2>
+ <span className="stop-sheet-id">({stop.stopId})</span>
+ </div>
+
+ <div className="stop-sheet-lines-container">
+ {stop.lines.map((line) => (
+ <div key={line} className="stop-sheet-line-icon">
+ <LineIcon line={line} region={region} rounded />
+ </div>
+ ))}
</div>
{loading ? (
@@ -210,7 +217,7 @@ export const StopSheet: React.FC<StopSheetProps> = ({
</button>
<Link
- to={`/estimates/${stopId}`}
+ to={`/estimates/${stop.stopId}`}
className="stop-sheet-view-all"
onClick={onClose}
>