diff options
Diffstat (limited to 'src/frontend')
| -rw-r--r-- | src/frontend/app/components/LineIcon.css | 146 | ||||
| -rw-r--r-- | src/frontend/app/components/LineIcon.tsx | 16 | ||||
| -rw-r--r-- | src/frontend/app/components/StopSheet.tsx | 2 | ||||
| -rw-r--r-- | src/frontend/app/components/Stops/ConsolidatedCirculationCard.css | 214 | ||||
| -rw-r--r-- | src/frontend/app/components/Stops/ConsolidatedCirculationCard.tsx | 26 | ||||
| -rw-r--r-- | src/frontend/app/components/Stops/ConsolidatedCirculationList.css | 226 | ||||
| -rw-r--r-- | src/frontend/app/root.css | 4 | ||||
| -rw-r--r-- | src/frontend/app/routes/estimates-$id.tsx | 2 | ||||
| -rw-r--r-- | src/frontend/app/routes/stops-$id.css | 2 | ||||
| -rw-r--r-- | src/frontend/app/routes/stops-$id.tsx | 2 | ||||
| -rw-r--r-- | src/frontend/package-lock.json | 643 | ||||
| -rw-r--r-- | src/frontend/package.json | 4 | ||||
| -rw-r--r-- | src/frontend/vite.config.ts | 7 |
13 files changed, 899 insertions, 395 deletions
diff --git a/src/frontend/app/components/LineIcon.css b/src/frontend/app/components/LineIcon.css index 721de76..50f2d5f 100644 --- a/src/frontend/app/components/LineIcon.css +++ b/src/frontend/app/components/LineIcon.css @@ -1,72 +1,76 @@ /* 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); - --line-vigo-l4a-text: #ffffff; - --line-vigo-l4c: rgb(0, 153, 0); - --line-vigo-l4c-text: #ffffff; - --line-vigo-l5a: rgb(0, 176, 240); - --line-vigo-l5b: rgb(0, 176, 240); - --line-vigo-l6: rgb(204, 51, 153); - --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); - --line-vigo-l14: rgb(129, 142, 126); - --line-vigo-l14-text: #ffffff; - --line-vigo-l15a: rgb(216, 168, 206); - --line-vigo-l15b: rgb(216, 168, 206); - --line-vigo-l15c: rgb(216, 168, 168); - --line-vigo-l16: rgb(129, 142, 126); - --line-vigo-l16-text: #ffffff; - --line-vigo-l17: rgb(214, 245, 31); - --line-vigo-l18a: rgb(212, 80, 168); - --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); - --line-vigo-l27: rgb(112, 74, 42); - --line-vigo-l27-text: #ffffff; - --line-vigo-l28: rgb(176, 189, 254); - --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-h-text: #ffffff; - --line-vigo-h1: rgb(0, 96, 168); - --line-vigo-h1-text: #ffffff; - --line-vigo-h2: rgb(0, 96, 168); - --line-vigo-h2-text: #ffffff; - --line-vigo-h3: rgb(0, 96, 168); - --line-vigo-h3-text: #ffffff; - --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); - --line-vigo-turistico: rgb(102, 51, 102); - --line-vigo-u1: rgb(172, 100, 4); - --line-vigo-u1-text: #ffffff; - --line-vigo-u2: rgb(172, 100, 4); - --line-vigo-u2-text: #ffffff; + --line-c1: hsl(14, 86%, 50%); + --line-c1-text: hsl(0, 0%, 100%); + --line-c3d: hsl(48, 100%, 50%); + --line-c3i: hsl(48, 100%, 50%); + --line-l4a: hsl(120, 100%, 30%); + --line-l4a-text: hsl(0, 0%, 100%); + --line-l4c: hsl(120, 100%, 30%); + --line-l4c-text: hsl(0, 0%, 100%); + --line-l5a: hsl(204, 100%, 54%); + --line-l5a-text: hsl(0, 0%, 100%); + --line-l5b: hsl(204, 100%, 54%); + --line-l5b-text: hsl(0, 0%, 100%); + --line-l6: hsl(330, 60%, 50%); + --line-l7: hsl(120, 60%, 70%); + --line-l9b: hsl(36, 83%, 75%); + --line-l10: hsl(30, 80%, 20%); + --line-l10-text: hsl(0, 0%, 100%); + --line-l11: hsl(0, 100%, 44%); + --line-l11-text: hsl(0, 0%, 100%); + --line-l12a: hsl(210, 40%, 56%); + --line-l12b: hsl(209, 39%, 58%); + --line-l13: hsl(196, 100%, 47%); + --line-l14: hsl(120, 10%, 44%); + --line-l14-text: hsl(0, 0%, 100%); + --line-l15a: hsl(313, 38%, 75%); + --line-l15b: hsl(313, 38%, 75%); + --line-l15c: #d8a8a8; + --line-l16: hsl(120, 10%, 44%); + --line-l16-text: hsl(0, 0%, 100%); + --line-l17: hsl(69, 91%, 54%); + --line-l18a: hsl(320, 61%, 57%); + --line-l18a-text: hsl(0, 0%, 100%); + --line-l18b: hsl(320, 61%, 57%); + --line-l18b-text: hsl(0, 0%, 100%); + --line-l18h: hsl(320, 61%, 57%); + --line-l18h-text: hsl(0, 0%, 100%); + --line-l23: hsl(220, 100%, 41%); + --line-l24: hsl(0, 0%, 75%); + --line-l25: hsl(34, 95%, 35%); + --line-l27: hsl(30, 60%, 30%); + --line-l27-text: hsl(0, 0%, 100%); + --line-l28: hsl(230, 98%, 84%); + --line-l29: hsl(36, 92%, 66%); + --line-l31: hsl(60, 100%, 50%); + --line-a: hsl(300, 70%, 35%); + --line-a-text: hsl(0, 0%, 100%); + --line-a1: hsl(300, 70%, 35%); + --line-a1-text: hsl(0, 0%, 100%); + --line-h: hsl(210, 100%, 33%); + --line-h-text: hsl(0, 0%, 100%); + --line-h1: hsl(210, 100%, 33%); + --line-h1-text: hsl(0, 0%, 100%); + --line-h2: hsl(210, 100%, 33%); + --line-h2-text: hsl(0, 0%, 100%); + --line-h3: hsl(210, 100%, 33%); + --line-h3-text: hsl(0, 0%, 100%); + --line-lzd: hsl(220, 60%, 50%); + --line-n1: hsl(0, 51%, 53%); + --line-n4: hsl(300, 33%, 30%); + --line-n4-text: hsl(0, 0%, 100%); + --line-psa1: hsl(120, 100%, 30%); + --line-psa4: hsl(120, 100%, 30%); + --line-ptl: hsl(120, 60%, 70%); + --line-tur: hsl(300, 33%, 30%); + --line-u1: hsl(30, 80%, 20%); + --line-u1-text: hsl(0, 0%, 100%); + --line-u2: hsl(30, 80%, 20%); + --line-u2-text: hsl(0, 0%, 100%); } -.line-icon { +.line-icon-default { display: inline-block; padding: 0.25rem 0.5rem; margin-right: 0.5rem; @@ -81,6 +85,18 @@ border-color: var(--line-colour); } +.line-icon-pill { + display: inline-block; + padding: 0.25rem 0.5rem; + margin-right: 0.5rem; + font-size: 0.9rem; + font-weight: 600; + border-radius: 0.25rem; + + background-color: var(--line-colour); + color: var(--line-text-colour); +} + .line-icon-rounded { display: block; width: 5ch; diff --git a/src/frontend/app/components/LineIcon.tsx b/src/frontend/app/components/LineIcon.tsx index 8c3dbeb..5ccf80a 100644 --- a/src/frontend/app/components/LineIcon.tsx +++ b/src/frontend/app/components/LineIcon.tsx @@ -4,24 +4,28 @@ import "./LineIcon.css"; interface LineIconProps { line: string; + + /** + * @deprecated Unused since region is only Vigo + */ region?: RegionId; - rounded?: boolean; + + mode?: "rounded"|"pill"|"default"; } const LineIcon: React.FC<LineIconProps> = ({ line, - region = "vigo", - rounded = false, + mode = "default", }) => { 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`; + const cssVarName = `--line-${formattedLine.toLowerCase()}`; + const cssTextVarName = `--line-${formattedLine.toLowerCase()}-text`; return ( <span - className={rounded ? "line-icon-rounded" : "line-icon"} + className={`line-icon-${mode}`} style={ { "--line-colour": `var(${cssVarName})`, diff --git a/src/frontend/app/components/StopSheet.tsx b/src/frontend/app/components/StopSheet.tsx index cf070e2..6d2abf0 100644 --- a/src/frontend/app/components/StopSheet.tsx +++ b/src/frontend/app/components/StopSheet.tsx @@ -130,7 +130,7 @@ export const StopSheet: React.FC<StopSheetProps> = ({ > {stop.lines.map((line) => ( <div key={line} className="stop-sheet-line-icon"> - <LineIcon line={line} region={region} rounded /> + <LineIcon line={line} region={region} mode="rounded" /> </div> ))} </div> diff --git a/src/frontend/app/components/Stops/ConsolidatedCirculationCard.css b/src/frontend/app/components/Stops/ConsolidatedCirculationCard.css new file mode 100644 index 0000000..8a442c3 --- /dev/null +++ b/src/frontend/app/components/Stops/ConsolidatedCirculationCard.css @@ -0,0 +1,214 @@ +@reference "../../root.css"; + +.consolidated-circulation-card { + all: unset; + flex: 0 0 auto; + display: flex; + flex-direction: column; + gap: 0.5rem; + background-color: var(--message-background-color); + border-radius: 12px; + border: 1px solid var(--border-color); + padding: 0.65rem 0.85rem; + transition: all 0.2s ease; +} + +.consolidated-circulation-card.has-gps { + cursor: pointer; +} + +.consolidated-circulation-card.no-gps { + cursor: not-allowed; + opacity: 0.7; +} + +.consolidated-circulation-card.has-gps:hover { + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); + border-color: var(--button-background-color); + background-color: color-mix( + in oklab, + var(--button-background-color) 5%, + var(--message-background-color) + ); +} + +.consolidated-circulation-card.has-gps:active { + transform: scale(0.98); +} + +.consolidated-circulation-card:disabled { + pointer-events: none; +} + + +.consolidated-circulation-card .card-row { + display: flex; + align-items: center; + gap: 0.65rem; +} + +.consolidated-circulation-card .card-row.main { + min-height: 48px; +} + +.consolidated-circulation-card .line-info { + flex-shrink: 0; +} + +.consolidated-circulation-card .route-info { + flex: 1; + min-width: 0; +} + +.consolidated-circulation-card .route-info strong { + font-size: 1rem; + color: var(--text-color); + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + line-height: 1.25; +} + +.consolidated-circulation-card .eta-badge { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.3rem 0.45rem; + border-radius: 12px; +} + +.consolidated-circulation-card .eta-text { + display: flex; + flex-direction: column; + align-items: center; + line-height: 1; +} + +.consolidated-circulation-card .eta-value { + font-size: 1.15rem; + font-weight: 700; +} + +.consolidated-circulation-card .eta-unit { + font-size: 0.65rem; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.consolidated-circulation-card .eta-badge.time-running { + background: rgba(34, 197, 94, 0.12); + color: #1a9e56; +} + +.consolidated-circulation-card .eta-badge.time-delayed { + background: rgba(255, 106, 0, 0.12); + color: #d06100; +} + +.consolidated-circulation-card .eta-badge.time-scheduled { + background: rgba(11, 61, 145, 0.12); + color: #0b3d91; +} + +[data-theme="dark"] .consolidated-circulation-card .eta-badge.time-scheduled { + color: #8fb4ff; +} + +.consolidated-circulation-card .card-row.meta { + flex-wrap: wrap; + justify-content: flex-start; + gap: 0.4rem; +} + +.meta-chip { + font-size: 0.75rem; + padding: 0.2rem 0.55rem; + border-radius: 999px; + background: rgba(0, 0, 0, 0.03); + + @apply flex items-center justify-center gap-1 flex-shrink-0 bg-gray-200/30 dark:bg-gray-600/30; +} + +.meta-chip.delay-ok { + @apply bg-green-400/30 dark:bg-green-600/30 border-green-500 dark:border-green-700 text-green-800 dark:text-green-200; +} + +.meta-chip.delay-warn { + @apply bg-yellow-400/30 dark:bg-yellow-600/30 border-yellow-500 dark:border-yellow-700 text-yellow-800 dark:text-yellow-200; +} + +.meta-chip.delay-critical { + @apply bg-red-400/30 dark:bg-red-600/30 border-red-500 dark:border-red-700 text-white; +} + +.meta-chip.delay-early { + @apply bg-blue-400/30 dark:bg-blue-600/30 border-blue-500 dark:border-blue-700 text-blue-800 dark:text-blue-200; +} + +/* GPS Indicator */ +.gps-indicator { + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + flex-shrink: 0; + position: relative; +} + +.gps-pulse { + position: absolute; + width: 8px; + height: 8px; + background: #22c55e; + border-radius: 50%; + box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2); + animation: gpsPulse 2s ease-in-out infinite; +} + +.gps-pulse.previous-trip { + background: #ff9800; + box-shadow: 0 0 0 2px rgba(255, 152, 0, 0.2); + animation: gpsPulseOrange 2s ease-in-out infinite; +} + +@keyframes gpsPulse { + 0% { + box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2); + } + 50% { + box-shadow: 0 0 0 6px rgba(34, 197, 94, 0.1); + } + 100% { + box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2); + } +} + +@keyframes gpsPulseOrange { + 0% { + box-shadow: 0 0 0 2px rgba(255, 152, 0, 0.2); + } + 50% { + box-shadow: 0 0 0 6px rgba(255, 152, 0, 0.1); + } + 100% { + box-shadow: 0 0 0 2px rgba(255, 152, 0, 0.2); + } +} + +@media (max-width: 480px) { + .consolidated-circulation-card { + padding: 0.65rem 0.75rem; + } + + .consolidated-circulation-card .card-row { + gap: 0.5rem; + } + + .consolidated-circulation-card .eta-badge { + padding: 0.25rem 0.4rem; + } +} diff --git a/src/frontend/app/components/Stops/ConsolidatedCirculationCard.tsx b/src/frontend/app/components/Stops/ConsolidatedCirculationCard.tsx index 707ecce..0b97c11 100644 --- a/src/frontend/app/components/Stops/ConsolidatedCirculationCard.tsx +++ b/src/frontend/app/components/Stops/ConsolidatedCirculationCard.tsx @@ -4,7 +4,7 @@ import { type RegionConfig } from "~/config/RegionConfig"; import LineIcon from "~components/LineIcon"; import { type ConsolidatedCirculation } from "~routes/stops-$id"; -import "./ConsolidatedCirculationList.css"; +import "./ConsolidatedCirculationCard.css"; interface ConsolidatedCirculationCardProps { estimate: ConsolidatedCirculation; @@ -75,18 +75,6 @@ export const ConsolidatedCirculationCard: React.FC< > = ({ estimate, regionConfig, onMapClick, readonly }) => { const { t } = useTranslation(); - const absoluteArrivalTime = (minutes: number) => { - const now = new Date(); - const arrival = new Date(now.getTime() + minutes * 60000); - return Intl.DateTimeFormat( - typeof navigator !== "undefined" ? navigator.language : "en", - { - hour: "2-digit", - minute: "2-digit", - } - ).format(arrival); - }; - const formatDistance = (meters: number) => { if (meters > 1024) { return `${(meters / 1000).toFixed(1)} km`; @@ -123,9 +111,12 @@ export const ConsolidatedCirculationCard: React.FC< return null; } - const delta = Math.round(estimate.realTime.minutes - estimate.schedule.minutes); + const delta = Math.round( + estimate.realTime.minutes - estimate.schedule.minutes + ); const absDelta = Math.abs(delta); + // On time if (delta === 0) { return { label: t("estimates.delay_on_time", "En hora (0 min)"), @@ -133,8 +124,10 @@ export const ConsolidatedCirculationCard: React.FC< } as const; } + // Delayed if (delta > 0) { - const tone = delta <= 2 ? "delay-ok" : delta <= 10 ? "delay-warn" : "delay-critical"; + const tone = + delta <= 2 ? "delay-ok" : delta <= 10 ? "delay-warn" : "delay-critical"; return { label: t("estimates.delay_positive", "Retraso de {{minutes}} min", { minutes: delta, @@ -143,6 +136,7 @@ export const ConsolidatedCirculationCard: React.FC< } as const; } + // Early const tone = absDelta <= 2 ? "delay-ok" : "delay-early"; return { label: t("estimates.delay_negative", "Adelanto de {{minutes}} min", { @@ -200,7 +194,7 @@ export const ConsolidatedCirculationCard: React.FC< > <div className="card-row main"> <div className="line-info"> - <LineIcon line={estimate.line} region={regionConfig.id} rounded /> + <LineIcon line={estimate.line} region={regionConfig.id} mode="pill" /> </div> <div className="route-info"> <strong>{estimate.route}</strong> diff --git a/src/frontend/app/components/Stops/ConsolidatedCirculationList.css b/src/frontend/app/components/Stops/ConsolidatedCirculationList.css index 5afdeaa..044b4a3 100644 --- a/src/frontend/app/components/Stops/ConsolidatedCirculationList.css +++ b/src/frontend/app/components/Stops/ConsolidatedCirculationList.css @@ -11,229 +11,3 @@ color: var(--subtitle-color); font-size: 0.95rem; } - -.consolidated-circulation-card { - all: unset; - flex: 0 0 auto; - display: flex; - flex-direction: column; - gap: 0.5rem; - background-color: var(--message-background-color); - border-radius: 12px; - border: 1px solid var(--border-color); - padding: 0.65rem 0.85rem; - transition: all 0.2s ease; -} - -.consolidated-circulation-card.has-gps { - cursor: pointer; -} - -.consolidated-circulation-card.no-gps { - cursor: not-allowed; - opacity: 0.7; -} - -.consolidated-circulation-card.has-gps:hover { - box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); - border-color: var(--button-background-color); - background-color: color-mix( - in oklab, - var(--button-background-color) 5%, - var(--message-background-color) - ); -} - -.consolidated-circulation-card.has-gps:active { - transform: scale(0.98); -} - -.consolidated-circulation-card:disabled { - pointer-events: none; -} - - -.consolidated-circulation-card .card-row { - display: flex; - align-items: center; - gap: 0.65rem; -} - -.consolidated-circulation-card .card-row.main { - min-height: 48px; -} - -.consolidated-circulation-card .line-info { - flex-shrink: 0; -} - -.consolidated-circulation-card .route-info { - flex: 1; - min-width: 0; -} - -.consolidated-circulation-card .route-info strong { - font-size: 1rem; - color: var(--text-color); - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - line-height: 1.25; -} - -.consolidated-circulation-card .eta-badge { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 0.3rem 0.45rem; - border-radius: 12px; -} - -.consolidated-circulation-card .eta-text { - display: flex; - flex-direction: column; - align-items: center; - line-height: 1; -} - -.consolidated-circulation-card .eta-value { - font-size: 1.15rem; - font-weight: 700; -} - -.consolidated-circulation-card .eta-unit { - font-size: 0.65rem; - text-transform: uppercase; - letter-spacing: 0.08em; -} - -.consolidated-circulation-card .eta-badge.time-running { - background: rgba(34, 197, 94, 0.12); - color: #1a9e56; -} - -.consolidated-circulation-card .eta-badge.time-delayed { - background: rgba(255, 106, 0, 0.12); - color: #d06100; -} - -.consolidated-circulation-card .eta-badge.time-scheduled { - background: rgba(11, 61, 145, 0.12); - color: #0b3d91; -} - -[data-theme="dark"] .consolidated-circulation-card .eta-badge.time-scheduled { - color: #8fb4ff; -} - -.consolidated-circulation-card .card-row.meta { - flex-wrap: wrap; - justify-content: flex-start; - gap: 0.4rem; -} - -.meta-chip { - font-size: 0.75rem; - padding: 0.2rem 0.55rem; - border-radius: 999px; - background: rgba(0, 0, 0, 0.03); - color: var(--subtitle-color); - border: 1px solid var(--border-color); -} - -@media (prefers-color-scheme: dark) { - .meta-chip { - background: rgba(255, 255, 255, 0.05); - } -} - -.meta-chip.delay-ok { - background: hsla(142, 71%, 45%, 0.15); - border-color: hsla(142, 71%, 45%, 0.5); - color: #1a9e56; -} - -.meta-chip.delay-warn { - background: hsla(43, 96%, 56%, 0.25); - border-color: hsla(43, 96%, 56%, 0.6); - color: hsl(43, 96%, 40%); -} - -.meta-chip.delay-critical { - background: hsla(0, 84%, 60%, 0.2); - border-color: hsla(0, 84%, 60%, 0.35); - color: hsl(0, 74%, 42%); -} - -.meta-chip.delay-early { - background: hsla(217, 91%, 60%, 0.17); - border-color: hsla(217, 91%, 60%, 0.3); - color: hsl(224, 90%, 50%); -} - -/* GPS Indicator */ -.gps-indicator { - display: flex; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - flex-shrink: 0; - position: relative; -} - -.gps-pulse { - position: absolute; - width: 8px; - height: 8px; - background: #22c55e; - border-radius: 50%; - box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2); - animation: gpsPulse 2s ease-in-out infinite; -} - -.gps-pulse.previous-trip { - background: #ff9800; - box-shadow: 0 0 0 2px rgba(255, 152, 0, 0.2); - animation: gpsPulseOrange 2s ease-in-out infinite; -} - -@keyframes gpsPulse { - 0% { - box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2); - } - 50% { - box-shadow: 0 0 0 6px rgba(34, 197, 94, 0.1); - } - 100% { - box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2); - } -} - -@keyframes gpsPulseOrange { - 0% { - box-shadow: 0 0 0 2px rgba(255, 152, 0, 0.2); - } - 50% { - box-shadow: 0 0 0 6px rgba(255, 152, 0, 0.1); - } - 100% { - box-shadow: 0 0 0 2px rgba(255, 152, 0, 0.2); - } -} - -@media (max-width: 480px) { - .consolidated-circulation-card { - padding: 0.65rem 0.75rem; - } - - .consolidated-circulation-card .card-row { - gap: 0.5rem; - } - - .consolidated-circulation-card .eta-badge { - padding: 0.25rem 0.4rem; - } -} diff --git a/src/frontend/app/root.css b/src/frontend/app/root.css index d718d92..c880763 100644 --- a/src/frontend/app/root.css +++ b/src/frontend/app/root.css @@ -1,3 +1,5 @@ +@import "tailwindcss"; + :root { --colour-scheme: light; --background-color: #ffffff; @@ -122,8 +124,6 @@ body { overflow-x: hidden; } - - .theme-toggle { background: none; border: none; diff --git a/src/frontend/app/routes/estimates-$id.tsx b/src/frontend/app/routes/estimates-$id.tsx index b92e59d..afeb3d2 100644 --- a/src/frontend/app/routes/estimates-$id.tsx +++ b/src/frontend/app/routes/estimates-$id.tsx @@ -302,7 +302,7 @@ export default function Estimates() { <div className={`estimates-lines-container`}> {stopData.lines.map((line) => ( <div key={line} className="estimates-line-icon"> - <LineIcon line={line} region={region} rounded /> + <LineIcon line={line} region={region} mode="rounded" /> </div> ))} </div> diff --git a/src/frontend/app/routes/stops-$id.css b/src/frontend/app/routes/stops-$id.css index 782d9a1..4d204a7 100644 --- a/src/frontend/app/routes/stops-$id.css +++ b/src/frontend/app/routes/stops-$id.css @@ -7,7 +7,6 @@ } .estimates-list-container { - overflow-y: auto; flex: 1; min-height: 0; @@ -47,7 +46,6 @@ display: flex; flex-direction: column; height: 100%; - overflow: hidden; padding-block: 1rem; box-sizing: border-box; diff --git a/src/frontend/app/routes/stops-$id.tsx b/src/frontend/app/routes/stops-$id.tsx index 4d96928..de552bd 100644 --- a/src/frontend/app/routes/stops-$id.tsx +++ b/src/frontend/app/routes/stops-$id.tsx @@ -238,7 +238,7 @@ export default function Estimates() { <div className={`estimates-lines-container scrollable`}> {stopData.lines.map((line) => ( <div key={line} className="estimates-line-icon"> - <LineIcon line={line} region={region} rounded /> + <LineIcon line={line} region={region} mode="rounded" /> </div> ))} </div> diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 55a3610..98bd7be 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -11,6 +11,7 @@ "@fontsource-variable/roboto": "^5.2.8", "@react-router/node": "^7.9.6", "@react-router/serve": "^7.9.6", + "@tailwindcss/vite": "^4.1.17", "framer-motion": "^12.23.24", "fuse.js": "^7.1.0", "i18next-browser-languagedetector": "^8.2.0", @@ -24,7 +25,8 @@ "react-leaflet-markercluster": "^5.0.0-rc.0", "react-loading-skeleton": "^3.5.0", "react-modal-sheet": "^5.2.1", - "react-router": "^7.9.6" + "react-router": "^7.9.6", + "tailwindcss": "^4.1.17" }, "devDependencies": { "@eslint/js": "^9.39.1", @@ -533,7 +535,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -550,7 +551,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -567,7 +567,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -584,7 +583,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -601,7 +599,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -618,7 +615,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -635,7 +631,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -652,7 +647,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -669,7 +663,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -686,7 +679,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -703,7 +695,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -720,7 +711,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -737,7 +727,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -754,7 +743,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -771,7 +759,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -788,7 +775,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -805,7 +791,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -822,7 +807,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -839,7 +823,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -856,7 +839,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -873,7 +855,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -890,7 +871,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -907,7 +887,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -924,7 +903,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -941,7 +919,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -958,7 +935,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1208,7 +1184,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1219,7 +1194,6 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1230,7 +1204,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1240,14 +1213,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1647,7 +1618,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1661,7 +1631,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1675,7 +1644,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1689,7 +1657,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1703,7 +1670,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1717,7 +1683,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1731,7 +1696,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1745,7 +1709,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1759,7 +1722,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1773,7 +1735,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1787,7 +1748,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1801,7 +1761,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1815,7 +1774,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1829,7 +1787,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1843,7 +1800,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1870,7 +1826,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1884,7 +1839,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1898,7 +1852,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1912,7 +1865,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1926,7 +1878,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1940,18 +1891,273 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ] }, + "node_modules/@tailwindcss/node": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", + "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.17" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", + "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-x64": "4.1.17", + "@tailwindcss/oxide-freebsd-x64": "4.1.17", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-x64-musl": "4.1.17", + "@tailwindcss/oxide-wasm32-wasi": "4.1.17", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", + "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", + "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", + "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", + "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", + "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", + "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", + "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", + "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", + "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", + "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.6.0", + "@emnapi/runtime": "^1.6.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.7", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", + "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", + "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.17.tgz", + "integrity": "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.17", + "@tailwindcss/oxide": "4.1.17", + "tailwindcss": "4.1.17" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, "node_modules/@types/geojson": { @@ -1992,7 +2198,7 @@ "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "dependencies": { @@ -3004,6 +3210,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -3064,6 +3279,19 @@ "node": ">= 0.8" } }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", @@ -3115,7 +3343,6 @@ "version": "0.25.10", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -3534,7 +3761,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -3723,7 +3949,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -3945,6 +4170,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -4290,9 +4521,7 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -4428,6 +4657,255 @@ "node": ">= 0.8.0" } }, + "node_modules/lightningcss": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4477,6 +4955,15 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/maplibre-gl": { "version": "5.12.0", "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.12.0.tgz", @@ -4786,7 +5273,6 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, "funding": [ { "type": "github", @@ -5100,14 +5586,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, "license": "MIT", "peer": true, "engines": { @@ -5131,7 +5615,6 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -5569,7 +6052,6 @@ "version": "4.52.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -5614,7 +6096,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5960,7 +6441,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -6203,11 +6683,29 @@ "node": ">=8" } }, + "node_modules/tailwindcss": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", + "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -6377,7 +6875,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/union-value": { @@ -6467,9 +6965,9 @@ } }, "node_modules/valibot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.1.0.tgz", - "integrity": "sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", + "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -6516,7 +7014,6 @@ "version": "7.2.4", "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz", "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", - "dev": true, "license": "MIT", "peer": true, "dependencies": { diff --git a/src/frontend/package.json b/src/frontend/package.json index cbf353a..53d23ef 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -17,6 +17,7 @@ "@fontsource-variable/roboto": "^5.2.8", "@react-router/node": "^7.9.6", "@react-router/serve": "^7.9.6", + "@tailwindcss/vite": "^4.1.17", "framer-motion": "^12.23.24", "fuse.js": "^7.1.0", "i18next-browser-languagedetector": "^8.2.0", @@ -30,7 +31,8 @@ "react-leaflet-markercluster": "^5.0.0-rc.0", "react-loading-skeleton": "^3.5.0", "react-modal-sheet": "^5.2.1", - "react-router": "^7.9.6" + "react-router": "^7.9.6", + "tailwindcss": "^4.1.17" }, "devDependencies": { "@eslint/js": "^9.39.1", diff --git a/src/frontend/vite.config.ts b/src/frontend/vite.config.ts index 89623c0..b827847 100644 --- a/src/frontend/vite.config.ts +++ b/src/frontend/vite.config.ts @@ -1,4 +1,5 @@ import { reactRouter } from "@react-router/dev/vite"; +import tailwindcss from "@tailwindcss/vite"; import { execSync } from "child_process"; import { defineConfig } from "vite"; import tsconfigPaths from "vite-tsconfig-paths"; @@ -10,7 +11,11 @@ export default defineConfig({ define: { __COMMIT_HASH__: JSON.stringify(commitHash), }, - plugins: [reactRouter(), tsconfigPaths()], + plugins: [ + reactRouter(), + tsconfigPaths(), + tailwindcss() + ], server: { proxy: { "^/api": { |
