From a24639e17b63c5ebb9b2bb26af18e17302e9360b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Nov 2025 20:34:36 +0100 Subject: Add manual stop creation and override alerts with alternate stop codes (#74) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: arielcostas <94913521+arielcostas@users.noreply.github.com> Co-authored-by: Ariel Costas Guerrero --- src/frontend/app/components/StopAlert.css | 89 +++++++++++++++++++++++++++++++ src/frontend/app/components/StopAlert.tsx | 36 +++++++++++++ src/frontend/app/components/StopSheet.tsx | 3 ++ src/frontend/app/data/StopDataProvider.ts | 5 ++ src/frontend/app/routes/estimates-$id.tsx | 3 ++ 5 files changed, 136 insertions(+) create mode 100644 src/frontend/app/components/StopAlert.css create mode 100644 src/frontend/app/components/StopAlert.tsx (limited to 'src/frontend/app') diff --git a/src/frontend/app/components/StopAlert.css b/src/frontend/app/components/StopAlert.css new file mode 100644 index 0000000..0032d09 --- /dev/null +++ b/src/frontend/app/components/StopAlert.css @@ -0,0 +1,89 @@ +.stop-alert { + display: flex; + align-items: flex-start; + gap: 0.75rem; + padding: 0.75rem; + border-radius: 0.5rem; + margin: 0.75rem 0; + border: 1px solid; +} + +.stop-alert-info { + background-color: rgba(59, 130, 246, 0.1); + border-color: rgba(59, 130, 246, 0.3); + color: #1e40af; +} + +.stop-alert-error { + background-color: rgba(239, 68, 68, 0.1); + border-color: rgba(239, 68, 68, 0.3); + color: #991b1b; +} + +.stop-alert-compact { + padding: 0.5rem; + margin: 0.5rem 0; + font-size: 0.875rem; +} + +.stop-alert-icon { + flex-shrink: 0; + width: 1.25rem; + height: 1.25rem; +} + +.stop-alert-compact .stop-alert-icon { + width: 1rem; + height: 1rem; +} + +.stop-alert-content { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.stop-alert-title { + font-weight: 600; + font-size: 0.95rem; +} + +.stop-alert-compact .stop-alert-title { + font-size: 0.85rem; +} + +.stop-alert-message { + font-size: 0.9rem; + opacity: 0.9; +} + +.stop-alert-compact .stop-alert-message { + font-size: 0.8rem; +} + +.stop-alert-alternate-codes { + font-size: 0.85rem; + margin-top: 0.25rem; + font-style: italic; + opacity: 0.8; +} + +.stop-alert-compact .stop-alert-alternate-codes { + font-size: 0.75rem; +} + +/* Dark mode support */ +@media (prefers-color-scheme: dark) { + .stop-alert-info { + background-color: rgba(59, 130, 246, 0.15); + border-color: rgba(59, 130, 246, 0.4); + color: #93c5fd; + } + + .stop-alert-error { + background-color: rgba(239, 68, 68, 0.15); + border-color: rgba(239, 68, 68, 0.4); + color: #fca5a5; + } +} diff --git a/src/frontend/app/components/StopAlert.tsx b/src/frontend/app/components/StopAlert.tsx new file mode 100644 index 0000000..69ecc22 --- /dev/null +++ b/src/frontend/app/components/StopAlert.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { AlertCircle, Info } from "lucide-react"; +import type { Stop } from "~/data/StopDataProvider"; +import "./StopAlert.css"; + +interface StopAlertProps { + stop: Stop; + compact?: boolean; +} + +export const StopAlert: React.FC = ({ stop, compact = false }) => { + // Don't render anything if there's no alert content + const hasContent = stop.title || stop.message; + if (!hasContent) { + return null; + } + + const isError = stop.cancelled === true; + + return ( +
+
+ {isError ? : } +
+
+ {stop.title &&
{stop.title}
} + {stop.message &&
{stop.message}
} + {stop.alternateCodes && stop.alternateCodes.length > 0 && ( +
+ Alternative stops: {stop.alternateCodes.join(", ")} +
+ )} +
+
+ ); +}; diff --git a/src/frontend/app/components/StopSheet.tsx b/src/frontend/app/components/StopSheet.tsx index afa530b..695b18e 100644 --- a/src/frontend/app/components/StopSheet.tsx +++ b/src/frontend/app/components/StopSheet.tsx @@ -6,6 +6,7 @@ import { Clock, RefreshCw } from "lucide-react"; import LineIcon from "./LineIcon"; import { StopSheetSkeleton } from "./StopSheetSkeleton"; import { ErrorDisplay } from "./ErrorDisplay"; +import { StopAlert } from "./StopAlert"; import { type Estimate } from "../routes/estimates-$id"; import { REGIONS, type RegionId, getRegionConfig } from "../data/RegionConfig"; import { useApp } from "../AppContext"; @@ -144,6 +145,8 @@ export const StopSheet: React.FC = ({ ))} + + {loading ? ( ) : error ? ( diff --git a/src/frontend/app/data/StopDataProvider.ts b/src/frontend/app/data/StopDataProvider.ts index e49faaa..25a617b 100644 --- a/src/frontend/app/data/StopDataProvider.ts +++ b/src/frontend/app/data/StopDataProvider.ts @@ -17,6 +17,11 @@ export interface Stop { longitude?: number; lines: string[]; favourite?: boolean; + amenities?: string[]; + cancelled?: boolean; + title?: string; + message?: string; + alternateCodes?: string[]; } // In-memory cache and lookup map per region diff --git a/src/frontend/app/routes/estimates-$id.tsx b/src/frontend/app/routes/estimates-$id.tsx index f213105..21186fb 100644 --- a/src/frontend/app/routes/estimates-$id.tsx +++ b/src/frontend/app/routes/estimates-$id.tsx @@ -14,6 +14,7 @@ import { ErrorDisplay } from "~/components/ErrorDisplay"; import { PullToRefresh } from "~/components/PullToRefresh"; import { useAutoRefresh } from "~/hooks/useAutoRefresh"; import { type RegionId, getRegionConfig } from "~/data/RegionConfig"; +import { StopAlert } from "~/components/StopAlert"; export interface Estimate { line: string; @@ -276,6 +277,8 @@ export default function Estimates() { + {stopData && } +
{estimatesLoading ? ( tableStyle === "grouped" ? ( -- cgit v1.3