diff --git a/challenge/package.json b/challenge/package.json index fac12b9..7433fe1 100644 --- a/challenge/package.json +++ b/challenge/package.json @@ -18,6 +18,7 @@ "chokidar": "^3.5.3", "express": "^4.18.2", "local-storage": "^2.0.0", + "lucide-react": "^0.488.0", "prop-types": "^15.8.1", "react": "^18.0.0", "react-dom": "^18.0.0", @@ -50,8 +51,8 @@ }, "devDependencies": { "autoprefixer": "^10.4.20", + "dotenv": "^16.4.5", "postcss": "^8.4.45", - "tailwindcss": "^3.4.10", - "dotenv": "^16.4.5" + "tailwindcss": "^3.4.10" } } diff --git a/challenge/src/components/Announcements.jsx b/challenge/src/components/Announcements.jsx new file mode 100644 index 0000000..4df6d87 --- /dev/null +++ b/challenge/src/components/Announcements.jsx @@ -0,0 +1,124 @@ +import React, { useState, useEffect } from "react"; +import { Cloud } from "lucide-react"; +import "./announcements.css"; + +export default function AnnouncementsPage() { + const [current, setCurrent] = useState(null); + const [forecast, setForecast] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [unit, setUnit] = useState("imperial"); + + const API_KEY = "31893d8dd93053c2be70003e332f20a5"; + const lat = 40.5408; + const lon = -74.4815; + const currentUrl = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&units=${unit}&appid=${API_KEY}`; + const forecastUrl = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&units=${unit}&appid=${API_KEY}`; + + useEffect(() => { + const ctrl = new AbortController(); + const signal = ctrl.signal; + + async function fetchData() { + setLoading(true); + setError(null); + + try { + const [resCur, resFor] = await Promise.all([ + fetch(currentUrl, { signal }), + fetch(forecastUrl, { signal }), + ]); + + if (resCur.status === 401 || resFor.status === 401) { + throw new Error("Unauthorized: invalid API key"); + } + if (!resCur.ok) + throw new Error(`Current weather error: ${resCur.status}`); + if (!resFor.ok) throw new Error(`Forecast error: ${resFor.status}`); + + setCurrent(await resCur.json()); + setForecast(await resFor.json()); + } catch (err) { + if (err.name !== "AbortError") setError(err.message); + } finally { + setLoading(false); + } + } + + fetchData(); + return () => ctrl.abort(); + }, [currentUrl, forecastUrl, unit]); + + if (loading) return
Loading weather…
; + if (error) returnError: {error}
; + + const localMs = (current.dt + current.timezone) * 1000; + const date = new Date(localMs); + const weekday = date.toLocaleDateString("en-US", { weekday: "long" }); + const time = date.toLocaleTimeString("en-US", { + hour: "numeric", + minute: "2-digit", + hour12: true, + }); + const pop = Math.round((forecast.list[0]?.pop || 0) * 100); + const temp = Math.round(current.main.temp); + const humidity = current.main.humidity; + const windSpeed = Math.round(current.wind.speed); + const weatherMain = current.weather[0].main.toLowerCase(); + const weatherDesc = current.weather[0].description; + + return ( +