Files
2026-10-04 12:04:52 +01:00

133 lines
4.9 KiB
TypeScript

import { JSX } from "react";
import RefreshButton from "./RefreshButton";
// 1. Define explicit TypeScript interfaces for the Open-Meteo response
interface OpenMeteoResponse {
current: {
time: string;
temperature_2m: number;
relative_humidity_2m: number;
wind_speed_10m: number;
weather_code: number;
};
current_units: {
temperature_2m: string;
relative_humidity_2m: string;
wind_speed_10m: string;
};
}
// Map WMO Weather Interpretation Codes to friendly labels & badge styles
// https://emojipedia.org/ copy and paste!
function getWeatherInfo(code: number): { label: string; badgeClass: string } {
if (code === 0) return { label: "Clear Sky ☀️", badgeClass: "badge-warning" };
if (code >= 1 && code <= 3)
return { label: "Partly Cloudy ⛅", badgeClass: "badge-info" };
if (code >= 45 && code <= 48)
return { label: "Foggy 🌫️", badgeClass: "badge-ghost" };
if (code >= 51 && code <= 67)
return { label: "Rain / Drizzle 🌧️", badgeClass: "badge-primary" };
if (code >= 71 && code <= 77)
return { label: "Snow ❄️", badgeClass: "badge-secondary" };
return { label: "Stormy ⛈️", badgeClass: "badge-error" };
}
// 2. Async Server Component - direct data fetching on the server
export default async function WeatherPage(): Promise<JSX.Element> {
// Fetching London forecast from Open-Meteo (No API key needed)
const res = await fetch(
"https://api.open-meteo.com/v1/forecast?latitude=51.5074&longitude=-0.1278&current=temperature_2m,relative_humidity_2m,wind_speed_10m,weather_code",
{
// Revalidate every 10 minutes (ISR - Incremental Static Regeneration)
next: { revalidate: 600 }
// next: {
// tags: ["weather-data"] // <--- Label this request with a tag
// }
}
);
// -------------------------------------------------------------
// ⏳ SIMULATION DELAY
// Slows down the revalidation so the STALE window stays open.
// await new Promise((resolve) => setTimeout(resolve, 3000));
// -------------------------------------------------------------
if (!res.ok) {
throw new Error("Failed to fetch weather data from Open-Meteo.");
}
const data: OpenMeteoResponse = await res.json();
const weather = getWeatherInfo(data.current.weather_code);
return (
<main className="min-h-screen bg-base-200 p-8 flex justify-center items-center">
{/* daisyUI Card Container */}
<div className="card w-96 bg-base-100 shadow-xl border border-base-300">
<div className="card-body">
{/* Header section with Location & Condition Badge */}
<div className="flex justify-between items-center">
<h2 className="card-title text-2xl font-bold">London Weather</h2>
<span
className={`badge ${weather.badgeClass} gap-2 p-3 font-semibold`}
>
{weather.label}
</span>
</div>
<p className="text-xs text-base-content/60">
Last updated: {new Date(data.current.time).toLocaleTimeString()}
</p>
<div className="divider my-2"></div>
{/* daisyUI Stats Grid */}
<div className="stats stats-vertical shadow bg-base-200">
{/* Temperature Stat */}
<div className="stat">
<div className="stat-title">Temperature</div>
<div className="stat-value text-primary">
{data.current.temperature_2m}
<span className="text-2xl font-normal ml-1">
{data.current_units.temperature_2m}
</span>
</div>
<div className="stat-desc">Real-time server reading</div>
</div>
{/* Humidity Stat */}
<div className="stat">
<div className="stat-title">Humidity</div>
<div className="stat-value text-secondary">
{data.current.relative_humidity_2m}
<span className="text-xl font-normal ml-1">
{data.current_units.relative_humidity_2m}
</span>
</div>
<div className="stat-desc">Relative humidity</div>
</div>
{/* Wind Speed Stat */}
<div className="stat">
<div className="stat-title">Wind Speed</div>
<div className="stat-value text-accent">
{data.current.wind_speed_10m}
<span className="text-xl font-normal ml-1">
{data.current_units.wind_speed_10m}
</span>
</div>
<div className="stat-desc">10m surface level</div>
</div>
</div>
<div className="card-actions justify-end mt-4">
<button className="btn btn-sm btn-outline btn-accent">
Live Data (Server Fetched)
</button>
</div>
<RefreshButton />
</div>
</div>
</main>
);
}