From be81080b24f1f46a1b306baf630b84e859bef86a Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Tue, 3 Sep 2024 20:03:04 +0100 Subject: [PATCH] Daily --- src/app/_components/forecast-daily.tsx | 35 +++--- src/app/_components/forecast-hourly.tsx | 19 ++- src/app/page.tsx | 4 +- src/lib/serverActions/accuweather.ts | 19 ++- src/lib/types/accuweather.ts | 152 +++++++++++++++++++++++- 5 files changed, 194 insertions(+), 35 deletions(-) diff --git a/src/app/_components/forecast-daily.tsx b/src/app/_components/forecast-daily.tsx index 0e9c4f8..5b36033 100644 --- a/src/app/_components/forecast-daily.tsx +++ b/src/app/_components/forecast-daily.tsx @@ -5,7 +5,7 @@ import { CloudSun } from "lucide-react"; import { getLocationFromLocalStorage } from "~/lib/localStorage"; import { getWeatherForecastDaily } from "~/lib/serverActions/accuweather"; -import { type AccuweatherHourlyForecast } from "~/lib/types/accuweather"; +import { type AccuweatherDailyForecast } from "~/lib/types/accuweather"; export function ForecastDaily() { const location = useQuery({ @@ -16,7 +16,7 @@ export function ForecastDaily() { const forecastDaily = useQuery({ staleTime: 1000 * 60 * 30, // 30 minutes queryKey: [location.data, "forecast", "daily"], - queryFn: async (): Promise> => { + queryFn: async (): Promise => { if (location.isLoading || !location.data) return Promise.reject("No location data."); console.log("Get daily forecast for location:", location.data); @@ -37,35 +37,36 @@ export function ForecastDaily() { ) : !forecastDaily.data ? ( No daily forecast data. ) : ( -
- {forecastDaily.data.map((item) => { - const time = dayjs(item.time); +
+ {forecastDaily.data.DailyForecasts.map((item) => { + const dateTime = dayjs(item.Date); return (
- {time.format("ddd")} + {dateTime.format("ddd")}
-
- -
+ + {item.Day.IconPhrase}
- {item.temperatureMin.toFixed(1)} + {item.Temperature.Minimum.Value.toFixed(1)} - °C -
-
- - {item.temperatureMax.toFixed(1)} + + °{item.Temperature.Minimum.Unit} - °C
+ + {item.Temperature.Maximum.Value.toFixed(1)} + + °{item.Temperature.Maximum.Unit} + +
); })} diff --git a/src/app/_components/forecast-hourly.tsx b/src/app/_components/forecast-hourly.tsx index 13c5018..b78d8a7 100644 --- a/src/app/_components/forecast-hourly.tsx +++ b/src/app/_components/forecast-hourly.tsx @@ -37,13 +37,13 @@ export function ForecastHourly() { ) : !forecastHourly.data ? ( No hourly forecast data. ) : ( -
+
{forecastHourly.data.map((item) => { const dateTime = dayjs(item.DateTime); return (
@@ -54,17 +54,14 @@ export function ForecastHourly() { {dateTime.format("HH:mm")}
-
- -
-
- - {item.Temperature.Value.toFixed(1)} - - + + {item.IconPhrase} + + {item.Temperature.Value.toFixed(1)} + °{item.Temperature.Unit.toUpperCase()} -
+
); })} diff --git a/src/app/page.tsx b/src/app/page.tsx index 1dbf9d2..cd67177 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,5 @@ import { CloudSun } from "lucide-react"; -// import { ForecastDaily } from "~/app/_components/forecast-daily"; +import { ForecastDaily } from "~/app/_components/forecast-daily"; import { ForecastHourly } from "~/app/_components/forecast-hourly"; import { ForecastNow } from "~/app/_components/forecast-now"; @@ -23,7 +23,7 @@ export default function HomePage() {

Forecast

- {/* */} +
); diff --git a/src/lib/serverActions/accuweather.ts b/src/lib/serverActions/accuweather.ts index cd75284..e4f3185 100644 --- a/src/lib/serverActions/accuweather.ts +++ b/src/lib/serverActions/accuweather.ts @@ -8,6 +8,7 @@ import { type AccuweatherLocation, type AccuweatherCurrentConditions, AccuweatherHourlyForecast, + AccuweatherDailyForecast, } from "~/lib/types/accuweather"; const BASE_PARAMS = `apikey=${env.WEATHER_API_KEY}&details=true&language=en-gb`; @@ -95,10 +96,22 @@ export async function getWeatherForecastHourly( export async function getWeatherForecastDaily( location: Location, -): Promise { +): Promise { return unstable_cache( - async (): Promise => { - return Promise.reject("Not implemented."); + async (): Promise => { + const weatherLocation = await getWeatherLocation(location); + console.log("Location key:", weatherLocation.Key); + + const url = `https://dataservice.accuweather.com/forecasts/v1/daily/5day/${weatherLocation.Key}?${BASE_PARAMS}&metric=true`; + console.log("Get daily forecast:", url); + const response = await fetch(url, BASE_REQUEST_OPTIONS); + const responseData = await response.json(); + console.log("Response:", JSON.stringify(responseData)); + + if (!responseData || responseData.length === 0 || !responseData[0]) + return Promise.reject("No data found."); + + return responseData; }, [`${location.latitude},${location.longitude}`], { diff --git a/src/lib/types/accuweather.ts b/src/lib/types/accuweather.ts index 01191dc..8a0b67b 100644 --- a/src/lib/types/accuweather.ts +++ b/src/lib/types/accuweather.ts @@ -104,11 +104,159 @@ export interface AccuweatherHourlyForecast { export interface Temperature { Value: number; - Unit: UnitEnum; + Unit: TemperatureUnit | string; UnitType: number; } -export enum UnitEnum { +export enum TemperatureUnit { C = "C", F = "F", } + +export interface AccuweatherDailyForecast { + Headline: Headline; + DailyForecasts: DailyForecast[]; +} + +export interface DailyForecast { + Date: Date; + EpochDate: number; + Sun: Sun; + Moon: Moon; + Temperature: RealFeelTemperature; + RealFeelTemperature: RealFeelTemperature; + RealFeelTemperatureShade: RealFeelTemperature; + HoursOfSun: number; + DegreeDaySummary: DegreeDaySummary; + AirAndPollen: AirAndPollen[]; + Day: Day; + Night: Day; + Sources: string[]; + MobileLink: string; + Link: string; +} + +export interface AirAndPollen { + Name: string; + Value: number; + Category: Category | string; + CategoryValue: number; + Type?: string; +} + +export enum Category { + Good = "Good", + Moderate = "Moderate", +} + +export interface Day { + Icon: number; + IconPhrase: string; + HasPrecipitation: boolean; + PrecipitationType?: string; + PrecipitationIntensity?: string; + ShortPhrase: string; + LongPhrase: string; + PrecipitationProbability: number; + ThunderstormProbability: number; + RainProbability: number; + SnowProbability: number; + IceProbability: number; + Wind: Wind; + WindGust: Wind; + TotalLiquid: Measurement; + Rain: Measurement; + Snow: Measurement; + Ice: Measurement; + HoursOfPrecipitation: number; + HoursOfRain: number; + HoursOfSnow: number; + HoursOfIce: number; + CloudCover: number; + Evapotranspiration: Measurement; + SolarIrradiance: Measurement; + RelativeHumidity: RelativeHumidity; + WetBulbTemperature: WetBulbTemperature; + WetBulbGlobeTemperature: WetBulbTemperature; +} + +export interface Measurement { + Value: number; + Unit: TemperatureUnit | MeasurementUnit | string; + UnitType: number; + Phrase?: Phrase | string; +} + +export enum Phrase { + Chilly = "Chilly", + Cool = "Cool", + Pleasant = "Pleasant", +} + +export enum MeasurementUnit { + CM = "cm", + KMH = "km/h", + Mm = "mm", + WM = "W/m²", +} + +export interface RelativeHumidity { + Minimum: number; + Maximum: number; + Average: number; +} + +export interface WetBulbTemperature { + Minimum: Measurement; + Maximum: Measurement; + Average: Measurement; +} + +export interface Wind { + Speed: Measurement; + Direction: Direction; +} + +export interface Direction { + Degrees: number; + Localized: string; + English: string; +} + +export interface DegreeDaySummary { + Heating: Measurement; + Cooling: Measurement; +} + +export interface Moon { + Rise: Date; + EpochRise: number; + Set: Date; + EpochSet: number; + Phase: string; + Age: number; +} + +export interface RealFeelTemperature { + Minimum: Measurement; + Maximum: Measurement; +} + +export interface Sun { + Rise: Date; + EpochRise: number; + Set: Date; + EpochSet: number; +} + +export interface Headline { + EffectiveDate: Date; + EffectiveEpochDate: number; + Severity: number; + Text: string; + Category: string; + EndDate: Date; + EndEpochDate: number; + MobileLink: string; + Link: string; +}