New Weather API
All checks were successful
Deploy to production / build (push) Successful in 11s

This commit is contained in:
Sean Edmunds
2026-07-13 14:30:56 -05:00
parent f14fee945b
commit 3d5a2dd88a
20 changed files with 847 additions and 1025 deletions

View File

@@ -1,15 +1,10 @@
import { Box, Grid, Stack } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import CurrentWeather from 'components/WeatherModal/CurrentWeather';
import { useCommonHooks, useOSMAPI, useQueryAPI, useWeatherAPI } from 'hooks';
import { useCommonHooks, useOpenMeteoAPI, useOSMAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { stackClasses } from 'styles';
import type {
CurrentWeather as CW,
ExtendedForecast,
Message,
PersonalData,
} from 'types';
import type { Message, MeteoWeather, PersonalData } from 'types';
import CurrentWeather from '../../components/WeatherModal/CurrentWeather';
import {
Applications,
Bookmarks,
@@ -48,73 +43,19 @@ export function Homepage() {
const { data: weatherMetadata } = useOSMAPI({
address: personalData?.[0]?.physicalAddress,
});
const { data: points } = useWeatherAPI({
endpoint: 'points',
const { data: meteoWeather } = useOpenMeteoAPI({
lat: weatherMetadata?.[0].lat ?? currentLocation.lat,
lon: weatherMetadata?.[0].lon ?? currentLocation.lon,
});
const { data: basicWeather } = useWeatherAPI({
endpoint: 'baseline',
office: points?.properties.gridId ?? currentLocation.office,
x: points?.properties.gridX ?? currentLocation.x,
y: points?.properties.gridY ?? currentLocation.y,
});
const { data: stations } = useWeatherAPI({
endpoint: 'stations',
office: points?.properties.gridId ?? currentLocation.office,
x: points?.properties.gridX ?? currentLocation.x,
y: points?.properties.gridY ?? currentLocation.y,
});
const { data: weather } = useWeatherAPI({
endpoint: 'current',
station:
stations?.features[0].properties.stationIdentifier ?? currentLocation.station,
});
const { data: forecast } = useWeatherAPI({
endpoint: 'forecast',
office: points?.properties.gridId ?? currentLocation.office,
x: points?.properties.gridX ?? currentLocation.x,
y: points?.properties.gridY ?? currentLocation.y,
});
const personal: PersonalData = personalData?.[0];
const message: Message = messageData?.[0] || {};
const [displayedGreeting, setDisplayedGreeting] = useState<string | undefined>('');
const [current, setCurrent] = useState<CW>({
icon: '',
textDescription: '',
temperature: 0,
highTemp: 0,
lowTemp: 0,
precipitation: 'N/A',
humidity: 0,
heatIndex: null,
windChill: null,
windSpeed: 0,
windDirection: 0,
pressure: 0,
});
const [meteoCurrent, setMeteoCurrent] = useState<MeteoWeather>();
const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] =
useDisclosure(false);
const extendedForecast = forecast?.properties?.periods.map(
(item: ExtendedForecast) => ({
detailedForecast: item.detailedForecast,
isDaytime: item.isDaytime,
name: item.name,
shortForecast: item.shortForecast,
temperature: item.temperature,
temperatureUnit: item.temperatureUnit,
}),
) || [
{
detailedForecast: '',
isDaytime: true,
name: '',
shortForecast: '',
temperature: 0,
temperatureUnit: 'F',
},
];
const [weatherModalOpened, { open: openWeatherModal, close: closeWeatherModal }] =
useDisclosure(false);
@@ -142,59 +83,16 @@ export function Homepage() {
});
}
}, [message, personal]);
useEffect(() => {
if (weather !== undefined && basicWeather !== undefined) {
setCurrent({
icon: weather.features[0]?.properties.icon?.replace('medium', 'large') ?? '',
textDescription: weather.features[0]?.properties.textDescription,
temperature: Math.round(
(weather.features?.find((feature: any) => feature.properties.temperature.value)
.properties.temperature.value *
9) /
5 +
32,
),
highTemp: Math.round(
(basicWeather.properties.maxTemperature.values[0].value * 9) / 5 + 32,
),
lowTemp: Math.round(
(basicWeather.properties.minTemperature.values[0].value * 9) / 5 + 32,
),
precipitation:
weather.features[0]?.properties?.precipitationLastHour?.value ?? 'N/A',
humidity: Math.round(weather.features[0]?.properties?.relativeHumidity?.value),
heatIndex:
basicWeather.properties.heatIndex?.values[0].value === null
? null
: Math.round(
(basicWeather.properties.heatIndex.values[0].value * 9) / 5 + 32,
),
windChill:
basicWeather.properties.windChill?.values[0].value === null
? null
: Math.round(
(basicWeather.properties.windChill.values[0].value * 9) / 5 + 32,
),
windSpeed: Math.round(weather.features[0]?.properties.windSpeed.value / 1.60934),
windDirection: weather.features[0]?.properties.windDirection.value,
pressure: Math.round(
weather.features[0]?.properties.barometricPressure.value / 3386.38,
),
});
}
}, [weather, basicWeather]);
useEffect(() => {
if (forecast) {
if (meteoWeather) {
setMeteoCurrent(meteoWeather);
setCurrentLocation({
office: points?.properties.gridId,
station: stations?.features[0].properties.stationIdentifier,
x: points?.properties.gridX,
y: points?.properties.gridY,
lat: weatherMetadata?.[0].lat,
lon: weatherMetadata?.[0].lon,
});
}
}, [forecast]);
}, [meteoWeather]);
return (
<Stack
@@ -206,8 +104,7 @@ export function Homepage() {
{appFilter.length > 0 || bookmarkFilter.length > 0 ? (
<>
<Search
current={current}
extendedForecast={extendedForecast}
current={meteoCurrent}
weatherModalOpened={weatherModalOpened}
closeWeatherModal={closeWeatherModal}
openWeatherModal={openWeatherModal}
@@ -220,7 +117,7 @@ export function Homepage() {
<Header
greet={displayedGreeting}
date={date}
current={current}
current={meteoCurrent}
openWeatherModal={openWeatherModal}
/>
<Applications contentType={appFilter} />
@@ -231,12 +128,11 @@ export function Homepage() {
<Stack align='center'>
<LargeHeader greet={displayedGreeting} date={date} />
<Grid grow columns={24} pos={'absolute'} bottom={'1rem'}>
<CurrentWeather current={current} />
{meteoCurrent && <CurrentWeather current={meteoCurrent} />}
</Grid>
</Stack>
<Search
current={current}
extendedForecast={extendedForecast}
current={meteoCurrent}
weatherModalOpened={weatherModalOpened}
closeWeatherModal={closeWeatherModal}
openWeatherModal={openWeatherModal}

View File

@@ -2,7 +2,7 @@ import { Group, Stack, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks';
import { useEffect, useState } from 'react';
import { textClasses } from 'styles';
import type { CurrentWeather } from 'types';
import type { MeteoWeather } from 'types';
export function Header({
greet,
@@ -12,7 +12,7 @@ export function Header({
}: {
greet: string | undefined;
date: string;
current: CurrentWeather;
current: MeteoWeather | undefined;
openWeatherModal: () => void;
}) {
const [time, setTime] = useState(new Date());
@@ -101,7 +101,7 @@ export function Header({
textShadow: theme.textShadowsEnabled ? textShadowStyle : undefined,
}}
>
{current.temperature}°F
{current?.current.temperature_2m?.toFixed(0)}°F
</Text>
<Text
className={textClasses.dateTime}
@@ -112,7 +112,8 @@ export function Header({
textShadow: theme.textShadowsEnabled ? textShadowStyle : undefined,
}}
>
{current.highTemp}°F/{current.lowTemp}°F
{current?.daily.temperature_2m_max?.[0].toFixed(0)}°F/
{current?.daily.temperature_2m_min?.[0].toFixed(0)}°F
</Text>
</Group>
<Text

View File

@@ -18,9 +18,8 @@ import { selectClasses } from 'styles';
import type {
Application,
Bookmark,
CurrentWeather,
ExtendedForecast,
GuacamoleClient,
MeteoWeather,
PersonalData,
Provider,
SearchHistoryItem,
@@ -32,7 +31,6 @@ import RenderItem from './components/RenderItem';
export function Search({
current,
extendedForecast,
weatherModalOpened,
closeWeatherModal,
openWeatherModal,
@@ -42,8 +40,7 @@ export function Search({
openProviderModal,
closeProviderModal,
}: {
current: CurrentWeather;
extendedForecast: ExtendedForecast[];
current: MeteoWeather | undefined;
weatherModalOpened: boolean;
closeWeatherModal: () => void;
openWeatherModal: () => void;
@@ -647,7 +644,6 @@ export function Search({
{weatherModalOpened && (
<WeatherModal
current={current}
extendedForecast={extendedForecast}
weatherModalOpened={weatherModalOpened}
handleResetSearch={handleResetSearch}
closeWeatherModal={closeWeatherModal}