From a89174e360ef51d144441d738c43c8c2de1ec144 Mon Sep 17 00:00:00 2001 From: furyhawk Date: Wed, 28 May 2025 16:23:35 +0800 Subject: [PATCH] refactor: Add WeatherMap component with Leaflet integration for displaying air temperature readings --- frontend/src/routes/_layout/weather-hub.tsx | 58 ++++++++++++++++ package-lock.json | 74 +++++++++++++++++++++ package.json | 6 ++ 3 files changed, 138 insertions(+) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/frontend/src/routes/_layout/weather-hub.tsx b/frontend/src/routes/_layout/weather-hub.tsx index 7c5914e..7184d8e 100644 --- a/frontend/src/routes/_layout/weather-hub.tsx +++ b/frontend/src/routes/_layout/weather-hub.tsx @@ -20,6 +20,8 @@ import { import { createFileRoute } from "@tanstack/react-router" import { useQuery } from "@tanstack/react-query" import { createListCollection } from "@ark-ui/react" +import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet"; +import "leaflet/dist/leaflet.css"; import { WeatherService } from "@/client/sdk.gen" @@ -502,6 +504,57 @@ function WindDirection() { ) } +function WeatherMap() { + const { data, isLoading, error } = useQuery>({ + queryKey: ["weather", "air-temperature"], + queryFn: () => WeatherService.getAirTemperature() as Promise>, + refetchInterval: 1000 * 60 * 30, // Refetch every 30 minutes + }); + + if (isLoading) return ; + if (error || !data?.data) return Error loading map data; + + const { stations = [], readings = [] } = data.data; + + if (stations.length === 0 || readings.length === 0) { + return No map data available; + } + + // Get the latest reading + const latestReading = readings[0]; + if (!latestReading || !latestReading.data) { + return No map data available; + } + + // Create a map of station IDs to station details + const stationMap = new Map(stations.map(station => [station.id, station])); + + return ( + + + {latestReading.data.map((reading) => { + const station = stationMap.get(reading.stationId); + if (!station) return null; + + return ( + + + {station.name} + Temperature: {reading.value?.toFixed(1)}°C + + + ); + })} + + ); +} + // Define tab values for namespaced Tabs const tabValues = { twoHour: "twoHourForecast", @@ -509,6 +562,7 @@ const tabValues = { fourDay: "fourDayOutlook", stats: "weatherStatistics", windDir: "windDirection", + weatherMap: "weatherMap", } function WeatherHub() { @@ -529,6 +583,7 @@ function WeatherHub() { 4-Day Outlook Statistics Wind Direction + Weather Map @@ -547,6 +602,9 @@ function WeatherHub() { + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8cc8f00 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,74 @@ +{ + "name": "home_stack", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "leaflet": "^1.9.4", + "react-leaflet": "^5.0.0" + } + }, + "node_modules/@react-leaflet/core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", + "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==", + "license": "Hippocratic-2.1", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-leaflet": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", + "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==", + "license": "Hippocratic-2.1", + "dependencies": { + "@react-leaflet/core": "^3.0.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT", + "peer": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8e37337 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "leaflet": "^1.9.4", + "react-leaflet": "^5.0.0" + } +}