()
| 25 | } |
| 26 | |
| 27 | export function WeatherWidget() { |
| 28 | const [weather, setWeather] = useState<WeatherData | null>(null); |
| 29 | const [loading, setLoading] = useState(true); |
| 30 | const [now, setNow] = useState(new Date()); |
| 31 | |
| 32 | useEffect(() => { |
| 33 | fetch("/api/weather") |
| 34 | .then((r) => r.json()) |
| 35 | .then((d) => { setWeather(d); setLoading(false); }) |
| 36 | .catch(() => setLoading(false)); |
| 37 | |
| 38 | // Update clock every second |
| 39 | const timer = setInterval(() => setNow(new Date()), 1000); |
| 40 | return () => clearInterval(timer); |
| 41 | }, []); |
| 42 | |
| 43 | if (loading) { |
| 44 | return ( |
| 45 | <div style={{ |
| 46 | padding: "1.25rem", |
| 47 | backgroundColor: "var(--card)", |
| 48 | borderRadius: "0.75rem", |
| 49 | border: "1px solid var(--border)", |
| 50 | minHeight: "120px", |
| 51 | display: "flex", alignItems: "center", justifyContent: "center", |
| 52 | }}> |
| 53 | <span style={{ color: "var(--text-muted)", fontSize: "0.875rem" }}>Loading weather...</span> |
| 54 | </div> |
| 55 | ); |
| 56 | } |
| 57 | |
| 58 | if (!weather || (weather as unknown as Record<string, unknown>).error) { |
| 59 | return null; |
| 60 | } |
| 61 | |
| 62 | return ( |
| 63 | <div style={{ |
| 64 | padding: "1.25rem", |
| 65 | backgroundColor: "var(--card)", |
| 66 | borderRadius: "0.75rem", |
| 67 | border: "1px solid var(--border)", |
| 68 | background: "linear-gradient(135deg, var(--card) 0%, color-mix(in srgb, var(--accent) 5%, var(--card)) 100%)", |
| 69 | }}> |
| 70 | {/* Header: city + clock */} |
| 71 | <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: "0.75rem" }}> |
| 72 | <div> |
| 73 | <div style={{ fontSize: "0.75rem", color: "var(--text-muted)", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: "0.125rem" }}> |
| 74 | 📍 {weather.city} |
| 75 | </div> |
| 76 | <div style={{ fontSize: "2rem", fontWeight: 800, color: "var(--text-primary)", lineHeight: 1, letterSpacing: "-1px" }}> |
| 77 | {format(now, "HH:mm")} |
| 78 | </div> |
| 79 | <div style={{ fontSize: "0.75rem", color: "var(--text-muted)", marginTop: "0.125rem" }}> |
| 80 | {format(now, "EEEE, d MMM")} |
| 81 | </div> |
| 82 | </div> |
| 83 | |
| 84 | {/* Current temp */} |
nothing calls this directly
no outgoing calls
no test coverage detected