From f874fa6f3ac122696d37d9664e4540901a49efd6 Mon Sep 17 00:00:00 2001 From: techieadi4703 Date: Thu, 16 Oct 2025 15:20:45 +0530 Subject: [PATCH] integrate Recharts and add hourly forecast chart --- package.json | 5 +- src/pages/Weather.jsx | 120 ++++++++++++++++++++++++++++++------------ 2 files changed, 88 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index d527454..9a0dd53 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,12 @@ "preview": "vite preview" }, "dependencies": { + "leaflet": "^1.9.4", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-leaflet": "^4.2.1", "react-router-dom": "^6.27.0", - "leaflet": "^1.9.4", - "react-leaflet": "^4.2.1" + "recharts": "^3.2.1" }, "devDependencies": { "@vitejs/plugin-react": "^4.3.1", diff --git a/src/pages/Weather.jsx b/src/pages/Weather.jsx index 6a68d52..ebf451a 100644 --- a/src/pages/Weather.jsx +++ b/src/pages/Weather.jsx @@ -15,7 +15,7 @@ * - [x] Add error retry button component * - [ ] Add favorites list (pin cities) * Advanced: - * - [ ] Hourly forecast visualization (line / area chart) + * - [x] Hourly forecast visualization (line / area chart) * - [x] Animate background transitions * - [ ] Add geolocation: auto-detect user city (with permission) */ @@ -30,6 +30,17 @@ import { clearWeatherCache, getCacheStats, } from "../services/weather.js"; +import { + LineChart, + Line, + XAxis, + YAxis, + Tooltip, + ResponsiveContainer, + CartesianGrid, + AreaChart, + Area, +} from "recharts"; // Helper to determine weather background class const weatherToClass = (desc = "") => { @@ -65,7 +76,10 @@ function renderWeatherAnimation(variant) { <> - + @@ -105,7 +119,7 @@ function renderWeatherAnimation(variant) { left: `${(i / 12) * 100}%`, animationDelay: `${(i % 6) * 0.4}s`, "--dur": `${10 + (i % 6)}s`, - "--drift": `${(i % 2 === 0 ? -40 : 40)}px`, + "--drift": `${i % 2 === 0 ? -40 : 40}px`, width: `${8 + (i % 3) * 4}px`, height: `${8 + (i % 3) * 4}px`, }} @@ -213,6 +227,15 @@ export default function Weather() { return { color: "#E0E0E0", label: "Clear 🌤️" }; }; + // Hourly forecast chart data + const hourlyData = + data?.weather?.[0]?.hourly?.map((h) => ({ + time: h.time.length === 1 ? "00:00" : `${h.time.padStart(4, "0").slice(0, 2)}:00`, + temp: displayTemp(Number(h.tempC)), + feelsLike: displayTemp(Number(h.FeelsLikeC)), + humidity: Number(h.humidity), + })) || []; + return (
} {error && ( - fetchWeather(city)} - /> + fetchWeather(city)} /> )} {data && !loading && ( @@ -276,8 +296,8 @@ export default function Weather() { /> )} - Temperature:{" "} - {displayTemp(Number(current.temp_C))}°{unit} + Temperature: {displayTemp(Number(current.temp_C))}° + {unit}

@@ -290,36 +310,25 @@ export default function Weather() { {/* 3-Day Forecast */} {forecast.map((day, i) => { - const condition = - day.hourly?.[0]?.weatherDesc?.[0]?.value || "Clear"; + const condition = day.hourly?.[0]?.weatherDesc?.[0]?.value || "Clear"; const badge = getBadgeStyle(condition); return ( - {day.hourly?.[0] && - getIconUrl(day.hourly?.[0]?.weatherIconUrl) && ( -

- { - (e.currentTarget.style.display = "none") - } - /> -
- )} - -
+ {day.hourly?.[0] && getIconUrl(day.hourly?.[0]?.weatherIconUrl) && ( +
+ { (e.currentTarget.style.display = "none")} + /> +
+ )} + +
Avg Temp:{" "} {displayTemp(Number(day.avgtempC))}°{unit}
); })} + + {/* Hourly Forecast Visualization */} + {hourlyData.length > 0 && ( + +
+ + + + + + + + + + + + + + + + +
+
+ )}
)}