hypervisor/src/app/todo/page.js
2026-07-04 18:18:31 -05:00

245 lines
6.5 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { getWeatherIcon } from "@/utils/weather.js";
import Image from "next/image";
import { useEffect, useState } from "react";
import styles from "./todo.module.css";
export default function WeatherPage() {
const [weather, setWeather] = useState(null);
const [time, setTime] = useState(new Date().toLocaleTimeString());
const [date, setDate] = useState(new Date().toLocaleDateString());
const [weatherIcon, setWeatherIcon] = useState("/weather/sunnyday.png");
useEffect(() => {
async function fetchWeather() {
try {
const res = await fetch(
`https://api.pirateweather.net/forecast/${process.env.NEXT_PUBLIC_WEATHER_API}/42.3967,-88.1837?units=us&exclude=minutely,hourly,flags&icon=pirate`,
{ cache: "no-store" },
);
const data = await res.json();
setWeather(data);
} catch (err) {
console.error("Failed to fetch weather:", err);
}
}
// only fetch weather every 5 minutes on the 0th second
const fetchWeatherInterval = () => {
const now = new Date();
if (now.getSeconds() === 0 && now.getMinutes() % 5 === 0) {
fetchWeather();
}
};
fetchWeather();
const interval = setInterval(fetchWeatherInterval, 1000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const interval = setInterval(() => {
setTime(new Date().toLocaleTimeString());
}, 1000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const interval = setInterval(() => {
setDate(new Date().toLocaleDateString());
}, 1000);
return () => clearInterval(interval);
}, []);
const weatherTemp = Math.floor(weather?.currently?.temperature) || "--";
const weatherFeelsLike =
Math.floor(weather?.currently?.apparentTemperature) || "--";
const weatherHigh =
Math.floor(weather?.daily?.data[0]?.temperatureHigh) || "--";
const weatherLow =
Math.floor(weather?.daily?.data[0]?.temperatureLow) || "--";
const percentChance =
Math.floor(weather?.currently?.precipProbability * 100) || "0";
const lastUpdatedTime = weather
? new Date(weather.currently.time * 1000).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})
: "--";
useEffect(() => {
const interval = setInterval(() => {
setWeatherIcon(
`/weather/${getWeatherIcon(weather?.currently?.icon)}.png`,
);
}, 1000);
return () => clearInterval(interval);
}, [weather]);
const weatherAlerts = weather?.alerts || [];
const futureForecasts = weather?.daily?.data.slice(1, 4) || [];
return (
<div className={styles.todoContainer}>
<div className={styles.todoHeader}>
<span className={styles.left}>
<span className={styles.title}>Raceway Car Wash</span>
<span className={styles.location}>Fox Lake</span>
</span>
<span className={styles.right}>
<span className={styles.time}>{time || "00:00:00 AM"}</span>
<span className={styles.date}>{date}</span>
</span>
</div>
<span className={styles.container}>
<span className={styles.listContainer}>
<span className={styles.lists}>Automated List</span>
<span className={styles.lists}>Manual List</span>
</span>
<span className={styles.weatherContainer}>
<span className={styles.weatherContainerInner}>
<span className={styles.weatherHeader}>
Hyperlocal Weather
</span>
{weatherAlerts.length > 0 && (
<span className={styles.weatherAlerts}>
{weatherAlerts.map((alert, index) => (
<span
key={index}
className={styles.weatherAlert}
>
{alert.title}
<span
className={
styles.weatherAlertExpires
}
>
Expires:{" "}
{new Date(
alert.expires * 1000,
).toLocaleString()}
</span>
</span>
))}
</span>
)}
<span className={styles.weatherTop}>
<span className={styles.weatherTopLeft}>
<span className={styles.weatherCondition}>
{weather?.currently?.summary}
</span>
<span className={styles.weatherTemp}>
{weatherTemp}°
</span>
<span className={styles.weatherFeelsLike}>
Feels Like {weatherFeelsLike}°
</span>
<span className={styles.weatherHighLow}>
High: {weatherHigh}° &middot; Low:{" "}
{weatherLow}°
</span>
</span>
<span className={styles.weatherTopRight}>
<Image
alt="Weather Icon"
src={weatherIcon}
width={175}
height={175}
loading="eager"
/>
</span>
</span>
<span className={styles.weatherDetails}>
<span className={styles.detail}>
{weather?.currently.windSpeed || "--"}
<span className={styles.detailUnit}>mph</span>
<span className={styles.detailText}>
Wind Speed
</span>
</span>
<span className={styles.detail}>
{percentChance}%
<span className={styles.detailText}>
Precipitation
</span>
</span>
<span className={styles.detail}>
{Math.floor(
weather?.currently?.humidity * 100,
) || "--"}
%
<span className={styles.detailText}>
Humidity
</span>
</span>
</span>
<span className={styles.weatherFutureForecasts}>
{futureForecasts.map((forecast, index) => (
<span
key={index}
className={styles.futureForecast}
>
<span className={styles.futureForecastDay}>
{new Date(
forecast.time * 1000,
).toLocaleDateString([], {
weekday: "short",
})}
</span>
<span
className={
styles.futureForecastIconContainer
}
>
<Image
alt="Weather Icon"
src={`/weather/${getWeatherIcon(forecast.icon)}.png`}
className={
styles.futureForecastIcon
}
width={40}
height={40}
unoptimized
/>
</span>
<span className={styles.futurePrecipChance}>
{Math.floor(
forecast.precipProbability * 100,
)}
%
</span>
<span className={styles.futureForecastTemp}>
{Math.floor(forecast.temperatureHigh)}°
/ {Math.floor(forecast.temperatureLow)}°
</span>
</span>
))}
</span>
<span className={styles.weatherFooter}>
Last Updated {lastUpdatedTime}
</span>
</span>
</span>
</span>
</div>
);
}