From f884982766c9180b582867dd2d8a657def465776 Mon Sep 17 00:00:00 2001 From: kenneth Date: Fri, 24 Oct 2025 23:03:55 +0000 Subject: [PATCH] feat(dashboard): add TfL disruptions tile to dashboard - Add TFLTile component displaying real-time transport disruptions - Show Northern line status prominently when good service - Display disruptions sorted by severity with line colors - Add optional tile decorations prop for flexible styling - Auto-refresh every 5 minutes with background updates Co-authored-by: Ona --- apps/dashboard/src/App.tsx | 106 +++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 5 deletions(-) diff --git a/apps/dashboard/src/App.tsx b/apps/dashboard/src/App.tsx index 488b7cf..4c98295 100644 --- a/apps/dashboard/src/App.tsx +++ b/apps/dashboard/src/App.tsx @@ -1,6 +1,7 @@ import { useQuery } from "@tanstack/react-query" import { useEffect, useState } from "react" import cn from "./components/lib/cn" +import { StatusSeverity, getLineColor, getStatusBorderColor, tflDisruptionsQuery } from "./tfl" import { DEFAULT_LATITUDE, DEFAULT_LONGITUDE, @@ -15,17 +16,26 @@ function App() {
+
) } -function Tile({ children, className }: { children: React.ReactNode; className?: string }) { +function Tile({ + decorations = true, + children, + className, +}: { decorations?: boolean; children: React.ReactNode; className?: string }) { return (
-
-
-
-
+ {decorations && ( + <> +
+
+
+
+ + )} {children}
) @@ -195,4 +205,90 @@ function WeatherTile() { ) } +function TFLTile() { + const { + data: tflData, + isLoading: isLoadingTFL, + error: errorTFL, + } = useQuery({ + ...tflDisruptionsQuery(), + select: (data) => { + data.disruptions.sort((a, b) => { + if (a.lineName.match(/northern/i)) return -1 + return a.statusSeverity - b.statusSeverity + }) + return data + }, + refetchInterval: 5 * 60 * 1000, // 5 minutes + refetchIntervalInBackground: true, + }) + + if (isLoadingTFL) { + return ( + +

Loading TfL

+
+ ) + } + + return ( + + {tflData?.goodService.includes("Northern") && ( + + )} + {tflData?.disruptions.map((disruption) => ( + <> + +
+ + ))} +
+ ) +} + +function TFLDistruptionItem({ + lineId, + lineName, + reason, + severity, +}: { lineId: string; lineName: string; reason: string; severity: number }) { + return ( + <> +
+

+ {lineName} +

+
+

+ {reason} +

+ + ) +} + export default App