aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/app')
-rw-r--r--src/frontend/app/components/StopAlert.css89
-rw-r--r--src/frontend/app/components/StopAlert.tsx36
-rw-r--r--src/frontend/app/components/StopSheet.tsx3
-rw-r--r--src/frontend/app/data/StopDataProvider.ts5
-rw-r--r--src/frontend/app/routes/estimates-$id.tsx3
5 files changed, 136 insertions, 0 deletions
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<StopAlertProps> = ({ 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 (
+ <div className={`stop-alert ${isError ? 'stop-alert-error' : 'stop-alert-info'} ${compact ? 'stop-alert-compact' : ''}`}>
+ <div className="stop-alert-icon">
+ {isError ? <AlertCircle /> : <Info />}
+ </div>
+ <div className="stop-alert-content">
+ {stop.title && <div className="stop-alert-title">{stop.title}</div>}
+ {stop.message && <div className="stop-alert-message">{stop.message}</div>}
+ {stop.alternateCodes && stop.alternateCodes.length > 0 && (
+ <div className="stop-alert-alternate-codes">
+ Alternative stops: {stop.alternateCodes.join(", ")}
+ </div>
+ )}
+ </div>
+ </div>
+ );
+};
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<StopSheetProps> = ({
))}
</div>
+ <StopAlert stop={stop} compact />
+
{loading ? (
<StopSheetSkeleton />
) : 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() {
</button>
</div>
+ {stopData && <StopAlert stop={stopData} />}
+
<div className="table-responsive">
{estimatesLoading ? (
tableStyle === "grouped" ? (