This commit is contained in:
632
package-lock.json
generated
632
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gfazioli/mantine-text-animate": "^4.0.6",
|
||||
"@gfazioli/mantine-text-animate": "^4.0.7",
|
||||
"@hello-pangea/dnd": "^18.0.1",
|
||||
"@mantine/core": "^9.4.1",
|
||||
"@mantine/dates": "^9.4.1",
|
||||
@@ -23,6 +23,7 @@
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"idb-keyval": "^6.3.0",
|
||||
"jotai": "^2.20.1",
|
||||
"openmeteo": "^1.2.3",
|
||||
"react": "^19.2.7",
|
||||
"react-dom": "^19.2.7",
|
||||
"react-fontpicker-ts": "^1.3.0",
|
||||
|
||||
@@ -1,27 +1,63 @@
|
||||
import { Box, Card, Group, Stack, Text } from '@mantine/core';
|
||||
import { useCommonHooks, useSunStateAPI } from 'hooks';
|
||||
import { useCommonHooks } from 'hooks';
|
||||
import { FaLongArrowAltDown, FaLongArrowAltUp, FaWind } from 'react-icons/fa';
|
||||
import { WiBarometer, WiHumidity, WiSunrise, WiSunset } from 'react-icons/wi';
|
||||
import { textClasses } from 'styles';
|
||||
import type { CurrentWeather as CW, SunState } from 'types';
|
||||
import type { MeteoWeather } from 'types';
|
||||
import WeatherFeaturePill from './WeatherFeaturePill';
|
||||
import WeatherIcon from './WeatherIcon';
|
||||
|
||||
export default function CurrentWeather({ current }: { current: CW }) {
|
||||
const { currentLocation, isMobile, theme, textShadowStyle } = useCommonHooks();
|
||||
const { data: sunStateData } = useSunStateAPI({
|
||||
lat: currentLocation.lat,
|
||||
lon: currentLocation.lon,
|
||||
});
|
||||
const WeatherCodes: { [key: number]: string } = {
|
||||
0: 'Clear sky',
|
||||
|
||||
const sunStates: SunState[] =
|
||||
sunStateData?.results.map((item: SunState) => {
|
||||
return {
|
||||
date: item.date,
|
||||
sunrise: item.sunrise,
|
||||
sunset: item.sunset,
|
||||
};
|
||||
}) || [];
|
||||
1: 'Mainly clear',
|
||||
2: 'Partly cloudy',
|
||||
3: 'Overcast',
|
||||
|
||||
45: 'Fog',
|
||||
48: 'Depositing rime fog',
|
||||
|
||||
51: 'Drizzle: Light',
|
||||
53: 'Drizzle: Moderate',
|
||||
55: 'Drizzle: Dense',
|
||||
|
||||
56: 'Freezing Drizzle: Light',
|
||||
57: 'Freezing Drizzle: Dense',
|
||||
|
||||
61: 'Rain: Slight',
|
||||
63: 'Rain: Moderate',
|
||||
65: 'Rain: Heavy',
|
||||
|
||||
66: 'Freezing Rain: Light',
|
||||
67: 'Freezing Rain: Heavy',
|
||||
|
||||
71: 'Snowfall: Slight',
|
||||
73: 'Snowfall: Moderate',
|
||||
75: 'Snowfall: Heavy',
|
||||
|
||||
77: 'Snow grains',
|
||||
|
||||
80: 'Rain showers: Slight',
|
||||
81: 'Rain showers: Moderate',
|
||||
82: 'Rain showers: Violent',
|
||||
|
||||
85: 'Snow showers: Slight',
|
||||
86: 'Snow showers: Heavy',
|
||||
|
||||
95: 'Thunderstorm: Slight or moderate',
|
||||
96: 'Thunderstorm with slight hail',
|
||||
99: 'Thunderstorm with heavy hail',
|
||||
} as const;
|
||||
|
||||
export default function CurrentWeather({
|
||||
current,
|
||||
}: {
|
||||
current: MeteoWeather | undefined;
|
||||
}) {
|
||||
const { isMobile, theme, textShadowStyle } = useCommonHooks();
|
||||
|
||||
const sunrise = new Date(current?.daily.sunrise[0] || 0).toLocaleTimeString();
|
||||
const sunset = new Date(current?.daily.sunset[0] || 0).toLocaleTimeString();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -71,25 +107,11 @@ export default function CurrentWeather({ current }: { current: CW }) {
|
||||
opacity: 0.5,
|
||||
}}
|
||||
>
|
||||
<WeatherIcon
|
||||
size={'lg'}
|
||||
textDescription={`${
|
||||
current.textDescription.includes('and')
|
||||
? current.textDescription.substring(
|
||||
0,
|
||||
current.textDescription.indexOf('and'),
|
||||
)
|
||||
: current.textDescription.includes('then')
|
||||
? current.textDescription.substring(
|
||||
0,
|
||||
current.textDescription.indexOf('then'),
|
||||
)
|
||||
: current.textDescription
|
||||
}`}
|
||||
/>
|
||||
<WeatherIcon size={'lg'} code={current.current.weather_code} />
|
||||
</Box>
|
||||
<Text fz={isMobile ? '5rem' : '7rem'} fw={400}>
|
||||
{current.temperature}°<sup style={{ fontSize: '3rem' }}>F</sup>
|
||||
{current.current.temperature_2m?.toFixed(0)}°
|
||||
<sup style={{ fontSize: '3rem' }}>F</sup>
|
||||
</Text>
|
||||
<Box
|
||||
style={{
|
||||
@@ -108,7 +130,11 @@ export default function CurrentWeather({ current }: { current: CW }) {
|
||||
/>
|
||||
</Group>
|
||||
<Group
|
||||
justify={current.textDescription.length > 25 ? 'flex-start' : 'center'}
|
||||
justify={
|
||||
WeatherCodes[Number(current.current.weather_code)].length > 25
|
||||
? 'flex-start'
|
||||
: 'center'
|
||||
}
|
||||
style={{
|
||||
width: '100%',
|
||||
maxWidth: '180px',
|
||||
@@ -120,13 +146,15 @@ export default function CurrentWeather({ current }: { current: CW }) {
|
||||
>
|
||||
<Text
|
||||
className={
|
||||
current.textDescription.length > 25
|
||||
WeatherCodes[Number(current.current.weather_code)].length > 25
|
||||
? textClasses.autoScrollText
|
||||
: undefined
|
||||
}
|
||||
classNames={textClasses}
|
||||
>
|
||||
{current.temperature === 69 ? 'Nice' : current.textDescription}
|
||||
{current.current.temperature_2m?.toFixed(0) === '69'
|
||||
? 'Nice'
|
||||
: WeatherCodes[Number(current.current.weather_code)]}
|
||||
</Text>
|
||||
</Group>
|
||||
</Stack>
|
||||
@@ -134,8 +162,9 @@ export default function CurrentWeather({ current }: { current: CW }) {
|
||||
leftSide={<Text>Feels like </Text>}
|
||||
rightSide={
|
||||
<Text>
|
||||
{current.heatIndex ?? current.windChill ?? current.temperature}°
|
||||
<sup>F</sup>
|
||||
{current.current.apparent_temperature?.toFixed(0) ??
|
||||
current.current.temperature_2m?.toFixed(0)}
|
||||
°<sup>F</sup>
|
||||
</Text>
|
||||
}
|
||||
highlight='right'
|
||||
@@ -164,7 +193,7 @@ export default function CurrentWeather({ current }: { current: CW }) {
|
||||
}
|
||||
rightSide={
|
||||
<Text>
|
||||
{current.highTemp}°<sup>F</sup>
|
||||
{current.daily.temperature_2m_max?.[0].toFixed(0)}°<sup>F</sup>
|
||||
</Text>
|
||||
}
|
||||
highlight='right'
|
||||
@@ -178,7 +207,7 @@ export default function CurrentWeather({ current }: { current: CW }) {
|
||||
}
|
||||
rightSide={
|
||||
<Text>
|
||||
{current.lowTemp}°<sup>F</sup>
|
||||
{current.daily.temperature_2m_min?.[0].toFixed(0)}°<sup>F</sup>
|
||||
</Text>
|
||||
}
|
||||
highlight='right'
|
||||
@@ -205,7 +234,7 @@ export default function CurrentWeather({ current }: { current: CW }) {
|
||||
Humidity
|
||||
</Group>
|
||||
}
|
||||
rightSide={<Text>{current.humidity}%</Text>}
|
||||
rightSide={<Text>{current.current.relative_humidity_2m}%</Text>}
|
||||
highlight='right'
|
||||
/>
|
||||
<WeatherFeaturePill
|
||||
@@ -215,7 +244,9 @@ export default function CurrentWeather({ current }: { current: CW }) {
|
||||
Wind
|
||||
</Group>
|
||||
}
|
||||
rightSide={<Text>{current.windSpeed}mph</Text>}
|
||||
rightSide={
|
||||
<Text>{current.current.wind_speed_10m?.toFixed(0)}mph</Text>
|
||||
}
|
||||
highlight='right'
|
||||
/>
|
||||
<WeatherFeaturePill
|
||||
@@ -225,7 +256,9 @@ export default function CurrentWeather({ current }: { current: CW }) {
|
||||
Pressure
|
||||
</Group>
|
||||
}
|
||||
rightSide={<Text>{current.pressure} in</Text>}
|
||||
rightSide={
|
||||
<Text>{current.current.surface_pressure?.toFixed(0)} mbar</Text>
|
||||
}
|
||||
highlight='right'
|
||||
/>
|
||||
</Group>
|
||||
@@ -257,7 +290,7 @@ export default function CurrentWeather({ current }: { current: CW }) {
|
||||
/>
|
||||
</Group>
|
||||
}
|
||||
rightSide={<Text>{sunStates[0]?.sunrise}</Text>}
|
||||
rightSide={<Text>{sunrise}</Text>}
|
||||
highlight='left'
|
||||
/>
|
||||
<WeatherFeaturePill
|
||||
@@ -274,7 +307,7 @@ export default function CurrentWeather({ current }: { current: CW }) {
|
||||
/>
|
||||
</Group>
|
||||
}
|
||||
rightSide={<Text>{sunStates[0]?.sunset}</Text>}
|
||||
rightSide={<Text>{sunset}</Text>}
|
||||
highlight='left'
|
||||
/>
|
||||
</Group>
|
||||
|
||||
@@ -1,17 +1,78 @@
|
||||
import { Card, Group, SimpleGrid, Stack, Text } from '@mantine/core';
|
||||
import { useCommonHooks } from 'hooks';
|
||||
import { textClasses } from 'styles';
|
||||
import type { ExtendedForecast } from 'types';
|
||||
import type { Daily } from 'types';
|
||||
import WeatherIcon from './WeatherIcon';
|
||||
|
||||
export default function ExtendedForecastCard({
|
||||
forecast,
|
||||
}: {
|
||||
forecast: ExtendedForecast[];
|
||||
}) {
|
||||
const WeatherCodes: { [key: number]: string } = {
|
||||
0: 'Clear sky',
|
||||
|
||||
1: 'Mainly clear',
|
||||
2: 'Partly cloudy',
|
||||
3: 'Overcast',
|
||||
|
||||
45: 'Fog',
|
||||
48: 'Depositing rime fog',
|
||||
|
||||
51: 'Drizzle: Light',
|
||||
53: 'Drizzle: Moderate',
|
||||
55: 'Drizzle: Dense',
|
||||
|
||||
56: 'Freezing Drizzle: Light',
|
||||
57: 'Freezing Drizzle: Dense',
|
||||
|
||||
61: 'Rain: Slight',
|
||||
63: 'Rain: Moderate',
|
||||
65: 'Rain: Heavy',
|
||||
|
||||
66: 'Freezing Rain: Light',
|
||||
67: 'Freezing Rain: Heavy',
|
||||
|
||||
71: 'Snowfall: Slight',
|
||||
73: 'Snowfall: Moderate',
|
||||
75: 'Snowfall: Heavy',
|
||||
|
||||
77: 'Snow grains',
|
||||
|
||||
80: 'Rain showers: Slight',
|
||||
81: 'Rain showers: Moderate',
|
||||
82: 'Rain showers: Violent',
|
||||
|
||||
85: 'Snow showers: Slight',
|
||||
86: 'Snow showers: Heavy',
|
||||
|
||||
95: 'Thunderstorm: Slight or moderate',
|
||||
96: 'Thunderstorm with slight hail',
|
||||
99: 'Thunderstorm with heavy hail',
|
||||
} as const;
|
||||
|
||||
export default function ExtendedForecastCard({ daily }: { daily: Daily | undefined }) {
|
||||
const { isMobile, theme } = useCommonHooks();
|
||||
const day = forecast.slice(2, 14).filter((item: ExtendedForecast) => item.isDaytime);
|
||||
const night = forecast.slice(2, 14).filter((item: ExtendedForecast) => !item.isDaytime);
|
||||
const forecast = daily?.time.map((_: any, index: number) => {
|
||||
const time = new Date(daily.time[index]);
|
||||
time.setMinutes(daily.time[index].getTimezoneOffset() + time.getMinutes());
|
||||
|
||||
return {
|
||||
apparentTemperatureMax: daily.apparent_temperature_max?.[index],
|
||||
apparentTemperatureMin: daily.apparent_temperature_min?.[index],
|
||||
daylightDuration: daily.daylight_duration?.[index],
|
||||
precipitationHours: daily.precipitation_hours?.[index],
|
||||
precipitationProbability: daily.precipitation_probability_max?.[index],
|
||||
precipitationSum: daily.precipitation_sum?.[index],
|
||||
sunrise: daily.sunrise[index],
|
||||
sunset: daily.sunset[index],
|
||||
sunshineDuration: daily.sunshine_duration?.[index],
|
||||
temperatureMax: daily.temperature_2m_max?.[index],
|
||||
temperatureMin: daily.temperature_2m_min?.[index],
|
||||
time: time,
|
||||
unIndexClearSkyMax: daily.uv_index_clear_sky_max?.[index],
|
||||
uvIndexMax: daily.uv_index_max?.[index],
|
||||
weatherCode: daily.weather_code?.[index],
|
||||
windDirection: daily.wind_direction_10m_dominant?.[index],
|
||||
windGusts: daily.wind_gusts_10m_max?.[index],
|
||||
windSpeed: daily.wind_speed_10m_max?.[index],
|
||||
};
|
||||
});
|
||||
return (
|
||||
<Card
|
||||
w={'100%'}
|
||||
@@ -37,13 +98,7 @@ export default function ExtendedForecastCard({
|
||||
Extended Forecast
|
||||
</Text>
|
||||
<SimpleGrid w={'100%'} pt={'.5rem'} cols={isMobile ? 3 : 6}>
|
||||
{day.map((item: ExtendedForecast, index: number) => {
|
||||
const short = item.shortForecast.includes('And')
|
||||
? item.shortForecast.substring(0, item.shortForecast.indexOf('And'))
|
||||
: item.shortForecast.includes('then')
|
||||
? item.shortForecast.substring(0, item.shortForecast.indexOf('then'))
|
||||
: item.shortForecast;
|
||||
|
||||
{forecast?.slice(1, 7).map((item: any) => {
|
||||
return (
|
||||
<Stack
|
||||
key={item.name}
|
||||
@@ -55,7 +110,11 @@ export default function ExtendedForecastCard({
|
||||
}}
|
||||
>
|
||||
<Group
|
||||
justify={item.name.length > 15 ? 'flex-start' : 'center'}
|
||||
justify={
|
||||
item.time.toLocaleDateString('en-US', { weekday: 'long' }).length > 15
|
||||
? 'flex-start'
|
||||
: 'center'
|
||||
}
|
||||
style={{
|
||||
width: '100%',
|
||||
whiteSpace: 'nowrap',
|
||||
@@ -66,16 +125,20 @@ export default function ExtendedForecastCard({
|
||||
>
|
||||
<Text
|
||||
className={
|
||||
item.name.length > 15 ? textClasses.autoScrollText : undefined
|
||||
item.time.toLocaleDateString('en-US', { weekday: 'long' }).length > 15
|
||||
? textClasses.autoScrollText
|
||||
: undefined
|
||||
}
|
||||
classNames={textClasses}
|
||||
>
|
||||
{item.name}
|
||||
{item.time.toLocaleDateString('en-US', { weekday: 'long' })}
|
||||
</Text>
|
||||
</Group>
|
||||
<WeatherIcon size='md' textDescription={short} />
|
||||
<WeatherIcon size='md' code={item.weatherCode} />
|
||||
<Group
|
||||
justify={short.length > 15 ? 'flex-start' : 'center'}
|
||||
justify={
|
||||
WeatherCodes[item.weatherCode].length > 15 ? 'flex-start' : 'center'
|
||||
}
|
||||
style={{
|
||||
width: '100%',
|
||||
whiteSpace: 'nowrap',
|
||||
@@ -85,15 +148,18 @@ export default function ExtendedForecastCard({
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
className={short.length > 15 ? textClasses.autoScrollText : undefined}
|
||||
className={
|
||||
WeatherCodes[item.weatherCode].length > 15
|
||||
? textClasses.autoScrollText
|
||||
: undefined
|
||||
}
|
||||
classNames={textClasses}
|
||||
>
|
||||
{short}
|
||||
{WeatherCodes[item.weatherCode]}
|
||||
</Text>
|
||||
</Group>
|
||||
<Text>
|
||||
{item.temperature}°{item.temperatureUnit}/{night[index].temperature}°
|
||||
{item.temperatureUnit}
|
||||
{item.temperatureMax.toFixed(0)}°F/{item.temperatureMin.toFixed(0)}°F
|
||||
</Text>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -11,16 +11,31 @@ import {
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts';
|
||||
import type { HourlyForecast } from 'types';
|
||||
import type { Hourly } from 'types';
|
||||
import WeatherTooltip from './WeatherTooltip';
|
||||
|
||||
export default function WeatherGraphs({
|
||||
hourlyForecast,
|
||||
}: {
|
||||
hourlyForecast: HourlyForecast[];
|
||||
}) {
|
||||
export default function WeatherGraphs({ hourly }: { hourly: Hourly | undefined }) {
|
||||
const colorPrecipitation = 'colorPrecipitation';
|
||||
const colorHumidity = 'colorHumidity';
|
||||
|
||||
const later = new Date();
|
||||
const now = new Date();
|
||||
later.setDate(now.getDate() + 1);
|
||||
const hourlyForecast = hourly?.time
|
||||
?.map((_: any, index: number) => {
|
||||
return {
|
||||
dewpoint: hourly.dew_point_2m?.[index].toFixed(0),
|
||||
isDaytime: hourly.is_day?.[index],
|
||||
name: hourly.time[index],
|
||||
startTime: hourly.time[index],
|
||||
endTime: hourly.time[index + 1],
|
||||
probabilityOfPrecipitation: hourly.precipitation_probability?.[index],
|
||||
temperature: hourly.temperature_2m?.[index].toFixed(0),
|
||||
relativeHumidity: hourly.relative_humidity_2m?.[index].toFixed(0),
|
||||
};
|
||||
})
|
||||
.filter((hour: any) => hour.startTime > now && hour.startTime < later);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
style={{
|
||||
@@ -34,7 +49,7 @@ export default function WeatherGraphs({
|
||||
<XAxis
|
||||
type='category'
|
||||
stroke={'var(--mantine-color-primary-0)'}
|
||||
dataKey={(data: HourlyForecast) => {
|
||||
dataKey={(data) => {
|
||||
return new Date(data.startTime).toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
@@ -49,7 +64,7 @@ export default function WeatherGraphs({
|
||||
}}
|
||||
/>
|
||||
<CartesianGrid stroke='#f5f5f522' />
|
||||
<Tooltip content={<WeatherTooltip />} />
|
||||
<Tooltip wrapperStyle={{ zIndex: 100 }} content={<WeatherTooltip />} />
|
||||
<Line
|
||||
unit={'°F'}
|
||||
type='monotone'
|
||||
@@ -67,9 +82,7 @@ export default function WeatherGraphs({
|
||||
unit={'°F'}
|
||||
type='monotone'
|
||||
name='Dew Point'
|
||||
dataKey={(data: HourlyForecast) => {
|
||||
return (data.dewpoint.value * 9) / 5 + 32;
|
||||
}}
|
||||
dataKey='dewpoint'
|
||||
stroke={'var(--mantine-color-accent-0)'}
|
||||
strokeWidth={2}
|
||||
dot={{
|
||||
@@ -78,7 +91,7 @@ export default function WeatherGraphs({
|
||||
fill: 'var(--mantine-color-accent-0)',
|
||||
}}
|
||||
/>
|
||||
<Legend style={{ background: 'var(--mantine-color-search-0)' }} />
|
||||
<Legend wrapperStyle={{ zIndex: 0, position: 'absolute' }} />
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
<ResponsiveContainer width={'100%'} height={150}>
|
||||
@@ -112,7 +125,7 @@ export default function WeatherGraphs({
|
||||
<XAxis
|
||||
type='category'
|
||||
stroke={'var(--mantine-color-primary-0)'}
|
||||
dataKey={(data: HourlyForecast) => {
|
||||
dataKey={(data) => {
|
||||
return new Date(data.startTime).toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
@@ -121,14 +134,12 @@ export default function WeatherGraphs({
|
||||
/>
|
||||
<YAxis stroke={'var(--mantine-color-primary-0)'} unit={'%'} domain={[0, 100]} />
|
||||
<CartesianGrid stroke='#f5f5f522' />
|
||||
<Tooltip content={<WeatherTooltip />} />
|
||||
<Tooltip wrapperStyle={{ zIndex: 100 }} content={<WeatherTooltip />} />
|
||||
<Area
|
||||
unit={'%'}
|
||||
type='monotone'
|
||||
name='Humidity'
|
||||
dataKey={(data: HourlyForecast) => {
|
||||
return data.relativeHumidity.value;
|
||||
}}
|
||||
dataKey={'relativeHumidity'}
|
||||
stroke={'var(--mantine-color-primary-0)'}
|
||||
strokeWidth={2}
|
||||
fill={`url(#${colorPrecipitation})`}
|
||||
@@ -142,9 +153,7 @@ export default function WeatherGraphs({
|
||||
unit={'%'}
|
||||
type='monotone'
|
||||
name='Precipitation'
|
||||
dataKey={(data: HourlyForecast) => {
|
||||
return data.probabilityOfPrecipitation.value;
|
||||
}}
|
||||
dataKey={'probabilityOfPrecipitation'}
|
||||
stroke={'var(--mantine-color-accent-0)'}
|
||||
strokeWidth={2}
|
||||
fill={`url(#${colorHumidity})`}
|
||||
@@ -154,7 +163,7 @@ export default function WeatherGraphs({
|
||||
fill: 'var(--mantine-color-accent-0)',
|
||||
}}
|
||||
/>
|
||||
<Legend style={{ background: 'var(--mantine-color-search-0)' }} />
|
||||
<Legend wrapperStyle={{ zIndex: 0, position: 'absolute' }} />
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</Stack>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Group, Image } from '@mantine/core';
|
||||
import { FaArrowRight } from 'react-icons/fa';
|
||||
import { Image } from '@mantine/core';
|
||||
import CloudyIcon from '../../assets/weather/cloudy.svg';
|
||||
import HazeIcon from '../../assets/weather/haze.svg';
|
||||
import HeavyRainIcon from '../../assets/weather/heavy-rain.svg';
|
||||
@@ -9,168 +8,166 @@ import SleetIcon from '../../assets/weather/sleet.svg';
|
||||
import SnowIcon from '../../assets/weather/snow.svg';
|
||||
import SunnyIcon from '../../assets/weather/sunny.svg';
|
||||
import ThunderstormIcon from '../../assets/weather/thunderstorm.svg';
|
||||
import WindyIcon from '../../assets/weather/windy.svg';
|
||||
|
||||
export default function WeatherIcon({
|
||||
size,
|
||||
textDescription,
|
||||
code,
|
||||
}: {
|
||||
size: string;
|
||||
textDescription: string;
|
||||
code: number | undefined;
|
||||
}) {
|
||||
const icon = textDescription.includes('then')
|
||||
? [
|
||||
textDescription.substring(0, textDescription.trim().indexOf('then')),
|
||||
textDescription.substring(textDescription.trim().indexOf('then') + 4).trim(),
|
||||
]
|
||||
: [textDescription.trim()];
|
||||
|
||||
const lookupSource = (icon: any) => {
|
||||
switch (icon.trim()) {
|
||||
case 'Ash':
|
||||
const lookupSource = (code: any) => {
|
||||
switch (code) {
|
||||
case 0:
|
||||
return {
|
||||
src: HazeIcon,
|
||||
alt: 'HazeIcon',
|
||||
src: SunnyIcon,
|
||||
alt: 'SunnyIcon',
|
||||
};
|
||||
case 'Broken Clouds':
|
||||
|
||||
case 1:
|
||||
return {
|
||||
src: PartlyCloudyIcon,
|
||||
alt: 'PartlyCloudyIcon',
|
||||
};
|
||||
case 'Chance Rain Showers':
|
||||
case 2:
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
src: PartlyCloudyIcon,
|
||||
alt: 'PartlyCloudyIcon',
|
||||
};
|
||||
case 'Chance Showers':
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
case 'Slight Chance Showers':
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
case 'Cloudy':
|
||||
case 3:
|
||||
return {
|
||||
src: CloudyIcon,
|
||||
alt: 'CloudyIcon',
|
||||
};
|
||||
case 'Dust':
|
||||
|
||||
case 45:
|
||||
return {
|
||||
src: HazeIcon,
|
||||
alt: 'HazeIcon',
|
||||
};
|
||||
case 'Drizzle':
|
||||
case 48:
|
||||
return {
|
||||
src: HazeIcon,
|
||||
alt: 'HazeIcon',
|
||||
};
|
||||
|
||||
case 51:
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
case 'Fog':
|
||||
case 53:
|
||||
return {
|
||||
src: HazeIcon,
|
||||
alt: 'HazeIcon',
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
case 'Freezing Rain':
|
||||
case 55:
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
|
||||
case 56:
|
||||
return {
|
||||
src: SleetIcon,
|
||||
alt: 'SleetIcon',
|
||||
};
|
||||
case 'Haze':
|
||||
case 57:
|
||||
return {
|
||||
src: HazeIcon,
|
||||
alt: 'HazeIcon',
|
||||
src: SleetIcon,
|
||||
alt: 'SleetIcon',
|
||||
};
|
||||
case 'Heavy Rain':
|
||||
|
||||
case 61:
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
case 63:
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
case 65:
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
|
||||
case 66:
|
||||
return {
|
||||
src: SleetIcon,
|
||||
alt: 'SleetIcon',
|
||||
};
|
||||
case 67:
|
||||
return {
|
||||
src: SleetIcon,
|
||||
alt: 'SleetIcon',
|
||||
};
|
||||
|
||||
case 71:
|
||||
return {
|
||||
src: SnowIcon,
|
||||
alt: 'SnowIcon',
|
||||
};
|
||||
case 73:
|
||||
return {
|
||||
src: SnowIcon,
|
||||
alt: 'SnowIcon',
|
||||
};
|
||||
case 75:
|
||||
return {
|
||||
src: SnowIcon,
|
||||
alt: 'SnowIcon',
|
||||
};
|
||||
|
||||
case 77:
|
||||
return {
|
||||
src: SnowIcon,
|
||||
alt: 'SnowIcon',
|
||||
};
|
||||
|
||||
case 80:
|
||||
return {
|
||||
src: HeavyRainIcon,
|
||||
alt: 'HeavyRainIcon',
|
||||
};
|
||||
case 'Heavy Snow':
|
||||
case 81:
|
||||
return {
|
||||
src: HeavyRainIcon,
|
||||
alt: 'HeavyRainIcon',
|
||||
};
|
||||
case 82:
|
||||
return {
|
||||
src: HeavyRainIcon,
|
||||
alt: 'HeavyRainIcon',
|
||||
};
|
||||
|
||||
case 85:
|
||||
return {
|
||||
src: SnowIcon,
|
||||
alt: 'SnowIcon',
|
||||
};
|
||||
case 'Light Rain':
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
case 'Mist':
|
||||
return {
|
||||
src: HazeIcon,
|
||||
alt: 'HazeIcon',
|
||||
};
|
||||
case 'Moderate Rain':
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
case 'Mostly Cloudy':
|
||||
return {
|
||||
src: CloudyIcon,
|
||||
alt: 'CloudyIcon',
|
||||
};
|
||||
case 'Overcast Clouds':
|
||||
return {
|
||||
src: PartlyCloudyIcon,
|
||||
alt: 'PartlyCloudyIcon',
|
||||
};
|
||||
case 'Patchy Fog':
|
||||
return {
|
||||
src: HazeIcon,
|
||||
alt: 'HazeIcon',
|
||||
};
|
||||
case 'Partly Sunny':
|
||||
return {
|
||||
src: PartlyCloudyIcon,
|
||||
alt: 'PartlyCloudyIcon',
|
||||
};
|
||||
case 'Rain':
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
case 'Scattered Clouds':
|
||||
return {
|
||||
src: PartlyCloudyIcon,
|
||||
alt: 'PartlyCloudyIcon',
|
||||
};
|
||||
case 'Showers':
|
||||
return {
|
||||
src: RainIcon,
|
||||
alt: 'RainIcon',
|
||||
};
|
||||
case 'Sleet':
|
||||
return {
|
||||
src: SleetIcon,
|
||||
alt: 'SleetIcon',
|
||||
};
|
||||
case 'Smoke':
|
||||
return {
|
||||
src: HazeIcon,
|
||||
alt: 'HazeIcon',
|
||||
};
|
||||
case 'Snow':
|
||||
case 86:
|
||||
return {
|
||||
src: SnowIcon,
|
||||
alt: 'SnowIcon',
|
||||
};
|
||||
case 'Squall':
|
||||
return {
|
||||
src: WindyIcon,
|
||||
alt: 'WindyIcon',
|
||||
};
|
||||
case 'Thunderstorm':
|
||||
|
||||
case 95:
|
||||
return {
|
||||
src: ThunderstormIcon,
|
||||
alt: 'ThunderstormIcon',
|
||||
};
|
||||
case 'Tornado':
|
||||
case 96:
|
||||
return {
|
||||
src: WindyIcon,
|
||||
alt: 'WindyIcon',
|
||||
src: ThunderstormIcon,
|
||||
alt: 'ThunderstormIcon',
|
||||
};
|
||||
case 99:
|
||||
return {
|
||||
src: ThunderstormIcon,
|
||||
alt: 'ThunderstormIcon',
|
||||
};
|
||||
|
||||
default:
|
||||
@@ -182,61 +179,14 @@ export default function WeatherIcon({
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{icon.length === 1 ? (
|
||||
<Image
|
||||
style={
|
||||
size === 'lg'
|
||||
? { width: '150px', height: '150px' }
|
||||
: { width: '75px', height: '75px' }
|
||||
}
|
||||
src={lookupSource(icon[0]).src}
|
||||
alt={lookupSource(icon[0]).alt}
|
||||
src={lookupSource(code).src}
|
||||
alt={lookupSource(code).alt}
|
||||
/>
|
||||
) : (
|
||||
<Group
|
||||
pos={'relative'}
|
||||
style={
|
||||
size === 'lg'
|
||||
? { width: '150px', height: '150px' }
|
||||
: { width: '75px', height: '75px' }
|
||||
}
|
||||
align='center'
|
||||
justify='center'
|
||||
>
|
||||
<Image
|
||||
style={{
|
||||
mask: 'linear-gradient(105deg, rgba(0, 0, 0, 1) 52%, rgba(0, 0, 0, 0) 56%)',
|
||||
}}
|
||||
pos={'absolute'}
|
||||
left={-8}
|
||||
src={lookupSource(icon[0]).src}
|
||||
alt={lookupSource(icon[0]).alt}
|
||||
/>
|
||||
<Image
|
||||
style={{
|
||||
mask: 'linear-gradient(105deg, rgba(0, 0, 0, 0) 44%, rgba(0, 0, 0, 1) 48%)',
|
||||
}}
|
||||
pos={'absolute'}
|
||||
right={-8}
|
||||
src={lookupSource(icon[1]).src}
|
||||
alt={lookupSource(icon[1]).alt}
|
||||
/>
|
||||
<FaArrowRight
|
||||
size={28}
|
||||
style={{
|
||||
color: 'var(--mantine-color-background-0)',
|
||||
position: 'absolute',
|
||||
stroke: 'var(--mantine-color-primary-0)',
|
||||
strokeWidth: 16,
|
||||
backgroundColor:
|
||||
'color-mix(in srgb, var(--mantine-color-accent-0), transparent 20%)',
|
||||
borderRadius: '50%',
|
||||
padding: '5px',
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,34 +1,23 @@
|
||||
import { Center, Group, Modal, Stack, Text } from '@mantine/core';
|
||||
import { useCommonHooks, useWeatherAPI } from 'hooks';
|
||||
import { useCommonHooks } from 'hooks';
|
||||
import { modalClasses } from 'styles';
|
||||
|
||||
import type { CurrentWeather as CW, ExtendedForecast, HourlyForecast } from 'types';
|
||||
import type { MeteoWeather } from 'types';
|
||||
import CurrentWeather from './CurrentWeather';
|
||||
import ExtendedForecastCard from './ExtendedForecastCard';
|
||||
import WeatherGraphs from './WeatherGraphs';
|
||||
|
||||
export function WeatherModal({
|
||||
current,
|
||||
extendedForecast,
|
||||
weatherModalOpened,
|
||||
handleResetSearch,
|
||||
closeWeatherModal,
|
||||
}: {
|
||||
current: CW;
|
||||
extendedForecast: ExtendedForecast[];
|
||||
current: MeteoWeather | undefined;
|
||||
weatherModalOpened: boolean;
|
||||
handleResetSearch: () => void;
|
||||
closeWeatherModal: () => void;
|
||||
}) {
|
||||
const { isMobile, currentUser, currentLocation } = useCommonHooks();
|
||||
const { data: hourly } = useWeatherAPI({
|
||||
endpoint: 'hourly',
|
||||
office: currentLocation.office,
|
||||
x: currentLocation.x,
|
||||
y: currentLocation.y,
|
||||
});
|
||||
const hourlyForecast: HourlyForecast[] =
|
||||
hourly?.properties?.periods?.slice(1, 25) || [];
|
||||
const { isMobile, currentUser } = useCommonHooks();
|
||||
|
||||
return (
|
||||
<Modal.Root
|
||||
@@ -68,8 +57,8 @@ export function WeatherModal({
|
||||
<Center w={'100%'}>
|
||||
<Stack w={'100%'}>
|
||||
<CurrentWeather current={current} />
|
||||
<WeatherGraphs hourlyForecast={hourlyForecast} />
|
||||
<ExtendedForecastCard forecast={extendedForecast} />
|
||||
<WeatherGraphs hourly={current?.hourly} />
|
||||
<ExtendedForecastCard daily={current?.daily} />
|
||||
</Stack>
|
||||
</Center>
|
||||
</Modal.Body>
|
||||
|
||||
@@ -10,6 +10,9 @@ function WeatherTooltip({ payload, active }: any) {
|
||||
borderRadius: '8px',
|
||||
}}
|
||||
>
|
||||
<Text size='md' c='var(--mantine-color-primary-0)'>
|
||||
{payload[0]?.payload.startTime?.toLocaleString()}
|
||||
</Text>
|
||||
{payload.map((item: any) => (
|
||||
<Text
|
||||
key={item.name}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
export {
|
||||
useMutationAPI,
|
||||
useOpenMeteoAPI,
|
||||
useOSMAPI,
|
||||
useQueryAPI,
|
||||
useSunStateAPI,
|
||||
useWeatherAPI,
|
||||
} from './useAPI';
|
||||
export { useCommonHooks } from './useCommonHooks';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Text } from '@mantine/core';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { fetchWeatherApi } from 'openmeteo';
|
||||
import { FaCheck } from 'react-icons/fa';
|
||||
import { VscLoading } from 'react-icons/vsc';
|
||||
|
||||
@@ -14,12 +15,195 @@ const headers: HeadersInit = {
|
||||
'Sec-Fetch-Mode': 'no-cors',
|
||||
'Sec-Fetch-Site': 'same-site',
|
||||
};
|
||||
const callEndpoint = async (url: string) => {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`${response.status} ${response.statusText}`);
|
||||
}
|
||||
return await response.json();
|
||||
|
||||
const callWeatherEndpoint = async (lat: number, lon: number) => {
|
||||
const weatherParams = {
|
||||
latitude: lat,
|
||||
longitude: lon,
|
||||
daily: [
|
||||
'sunrise',
|
||||
'sunset',
|
||||
'daylight_duration',
|
||||
'sunshine_duration',
|
||||
'uv_index_max',
|
||||
'uv_index_clear_sky_max',
|
||||
'temperature_2m_max',
|
||||
'temperature_2m_min',
|
||||
'apparent_temperature_max',
|
||||
'apparent_temperature_min',
|
||||
'wind_speed_10m_max',
|
||||
'wind_gusts_10m_max',
|
||||
'wind_direction_10m_dominant',
|
||||
'precipitation_sum',
|
||||
'precipitation_hours',
|
||||
'precipitation_probability_max',
|
||||
'weather_code',
|
||||
],
|
||||
hourly: [
|
||||
'temperature_2m',
|
||||
'relative_humidity_2m',
|
||||
'dew_point_2m',
|
||||
'apparent_temperature',
|
||||
'precipitation_probability',
|
||||
'precipitation',
|
||||
'rain',
|
||||
'showers',
|
||||
'snowfall',
|
||||
'snow_depth',
|
||||
'weather_code',
|
||||
'pressure_msl',
|
||||
'surface_pressure',
|
||||
'cloud_cover',
|
||||
'cloud_cover_low',
|
||||
'cloud_cover_mid',
|
||||
'cloud_cover_high',
|
||||
'visibility',
|
||||
'evapotranspiration',
|
||||
'et0_fao_evapotranspiration',
|
||||
'vapour_pressure_deficit',
|
||||
'wind_speed_10m',
|
||||
'wind_direction_10m',
|
||||
'wind_gusts_10m',
|
||||
'uv_index',
|
||||
'uv_index_clear_sky',
|
||||
'is_day',
|
||||
'sunshine_duration',
|
||||
],
|
||||
current: [
|
||||
'temperature_2m',
|
||||
'relative_humidity_2m',
|
||||
'apparent_temperature',
|
||||
'is_day',
|
||||
'wind_speed_10m',
|
||||
'wind_direction_10m',
|
||||
'wind_gusts_10m',
|
||||
'precipitation',
|
||||
'rain',
|
||||
'showers',
|
||||
'snowfall',
|
||||
'surface_pressure',
|
||||
'pressure_msl',
|
||||
'cloud_cover',
|
||||
'weather_code',
|
||||
],
|
||||
wind_speed_unit: 'mph',
|
||||
temperature_unit: 'fahrenheit',
|
||||
precipitation_unit: 'inch',
|
||||
};
|
||||
|
||||
const url = 'https://api.open-meteo.com/v1/forecast';
|
||||
const responses = await fetchWeatherApi(url, weatherParams);
|
||||
|
||||
const response = responses[0];
|
||||
|
||||
const current = response ? response.current() : null;
|
||||
const hourly = response ? response.hourly() : null;
|
||||
const daily = response ? response.daily() : null;
|
||||
|
||||
const sunrise = daily?.variables(0);
|
||||
const sunset = daily?.variables(1);
|
||||
const utcOffsetSeconds = response.utcOffsetSeconds();
|
||||
const weatherData = {
|
||||
current: {
|
||||
time: new Date((Number(current?.time()) + utcOffsetSeconds) * 1000),
|
||||
temperature_2m: current?.variables(0)?.value(),
|
||||
relative_humidity_2m: current?.variables(1)?.value(),
|
||||
apparent_temperature: current?.variables(2)?.value(),
|
||||
is_day: current?.variables(3)?.value(),
|
||||
wind_speed_10m: current?.variables(4)?.value(),
|
||||
wind_direction_10m: current?.variables(5)?.value(),
|
||||
wind_gusts_10m: current?.variables(6)?.value(),
|
||||
precipitation: current?.variables(7)?.value(),
|
||||
rain: current?.variables(8)?.value(),
|
||||
showers: current?.variables(9)?.value(),
|
||||
snowfall: current?.variables(10)?.value(),
|
||||
surface_pressure: current?.variables(11)?.value(),
|
||||
pressure_msl: current?.variables(12)?.value(),
|
||||
cloud_cover: current?.variables(13)?.value(),
|
||||
weather_code: current?.variables(14)?.value(),
|
||||
},
|
||||
hourly: {
|
||||
time: Array.from(
|
||||
{
|
||||
length:
|
||||
(Number(hourly?.timeEnd()) - Number(hourly?.time())) /
|
||||
Number(hourly?.interval()),
|
||||
},
|
||||
(_, i) =>
|
||||
new Date(
|
||||
(Number(hourly?.time()) + i * Number(hourly?.interval()) + utcOffsetSeconds) *
|
||||
1000,
|
||||
),
|
||||
),
|
||||
temperature_2m: hourly?.variables(0)?.valuesArray(),
|
||||
relative_humidity_2m: hourly?.variables(1)?.valuesArray(),
|
||||
dew_point_2m: hourly?.variables(2)?.valuesArray(),
|
||||
apparent_temperature: hourly?.variables(3)?.valuesArray(),
|
||||
precipitation_probability: hourly?.variables(4)?.valuesArray(),
|
||||
precipitation: hourly?.variables(5)?.valuesArray(),
|
||||
rain: hourly?.variables(6)?.valuesArray(),
|
||||
showers: hourly?.variables(7)?.valuesArray(),
|
||||
snowfall: hourly?.variables(8)?.valuesArray(),
|
||||
snow_depth: hourly?.variables(9)?.valuesArray(),
|
||||
weather_code: hourly?.variables(10)?.valuesArray(),
|
||||
pressure_msl: hourly?.variables(11)?.valuesArray(),
|
||||
surface_pressure: hourly?.variables(12)?.valuesArray(),
|
||||
cloud_cover: hourly?.variables(13)?.valuesArray(),
|
||||
cloud_cover_low: hourly?.variables(14)?.valuesArray(),
|
||||
cloud_cover_mid: hourly?.variables(15)?.valuesArray(),
|
||||
cloud_cover_high: hourly?.variables(16)?.valuesArray(),
|
||||
visibility: hourly?.variables(17)?.valuesArray(),
|
||||
evapotranspiration: hourly?.variables(18)?.valuesArray(),
|
||||
et0_fao_evapotranspiration: hourly?.variables(19)?.valuesArray(),
|
||||
vapour_pressure_deficit: hourly?.variables(20)?.valuesArray(),
|
||||
wind_speed_10m: hourly?.variables(21)?.valuesArray(),
|
||||
wind_direction_10m: hourly?.variables(22)?.valuesArray(),
|
||||
wind_gusts_10m: hourly?.variables(23)?.valuesArray(),
|
||||
uv_index: hourly?.variables(24)?.valuesArray(),
|
||||
uv_index_clear_sky: hourly?.variables(25)?.valuesArray(),
|
||||
is_day: hourly?.variables(26)?.valuesArray(),
|
||||
sunshine_duration: hourly?.variables(27)?.valuesArray(),
|
||||
},
|
||||
daily: {
|
||||
time: Array.from(
|
||||
{
|
||||
length:
|
||||
(Number(daily?.timeEnd()) - Number(daily?.time())) /
|
||||
Number(daily?.interval()),
|
||||
},
|
||||
(_, i) =>
|
||||
new Date(
|
||||
(Number(daily?.time()) + i * Number(daily?.interval()) + utcOffsetSeconds) *
|
||||
1000,
|
||||
),
|
||||
),
|
||||
// Map Int64 values to according structure
|
||||
sunrise: [new Array(sunrise?.valuesInt64Length())].map(
|
||||
(_, i) => new Date((Number(sunrise?.valuesInt64(i)) + utcOffsetSeconds) * 1000),
|
||||
),
|
||||
// Map Int64 values to according structure
|
||||
sunset: [new Array(sunset?.valuesInt64Length())].map(
|
||||
(_, i) => new Date((Number(sunset?.valuesInt64(i)) + utcOffsetSeconds) * 1000),
|
||||
),
|
||||
daylight_duration: daily?.variables(2)?.valuesArray(),
|
||||
sunshine_duration: daily?.variables(3)?.valuesArray(),
|
||||
uv_index_max: daily?.variables(4)?.valuesArray(),
|
||||
uv_index_clear_sky_max: daily?.variables(5)?.valuesArray(),
|
||||
temperature_2m_max: daily?.variables(6)?.valuesArray(),
|
||||
temperature_2m_min: daily?.variables(7)?.valuesArray(),
|
||||
apparent_temperature_max: daily?.variables(8)?.valuesArray(),
|
||||
apparent_temperature_min: daily?.variables(9)?.valuesArray(),
|
||||
wind_speed_10m_max: daily?.variables(10)?.valuesArray(),
|
||||
wind_gusts_10m_max: daily?.variables(11)?.valuesArray(),
|
||||
wind_direction_10m_dominant: daily?.variables(12)?.valuesArray(),
|
||||
precipitation_sum: daily?.variables(13)?.valuesArray(),
|
||||
precipitation_hours: daily?.variables(14)?.valuesArray(),
|
||||
precipitation_probability_max: daily?.variables(15)?.valuesArray(),
|
||||
weather_code: daily?.variables(16)?.valuesArray(),
|
||||
},
|
||||
};
|
||||
|
||||
return weatherData;
|
||||
};
|
||||
export function useQueryAPI({ endpoint }: { endpoint: string }) {
|
||||
const callEndpoint = async (endpoint: string) => {
|
||||
@@ -143,54 +327,10 @@ export function useMutationAPI() {
|
||||
});
|
||||
}
|
||||
|
||||
export function useWeatherAPI({
|
||||
endpoint,
|
||||
lat,
|
||||
lon,
|
||||
office,
|
||||
station,
|
||||
x,
|
||||
y,
|
||||
}: {
|
||||
endpoint: string;
|
||||
lat?: number;
|
||||
lon?: number;
|
||||
office?: string;
|
||||
station?: string;
|
||||
x?: number;
|
||||
y?: number;
|
||||
}) {
|
||||
let url = '';
|
||||
if (endpoint === 'baseline') {
|
||||
url = `https://api.weather.gov/gridpoints/${office}/${x},${y}`;
|
||||
} else if (endpoint === 'current') {
|
||||
url = `https://api.weather.gov/stations/${station}/observations`;
|
||||
} else if (endpoint === 'forecast') {
|
||||
url = `https://api.weather.gov/gridpoints/${office}/${x},${y}/forecast`;
|
||||
} else if (endpoint === 'hourly') {
|
||||
url = `https://api.weather.gov/gridpoints/${office}/${x},${y}/forecast/hourly`;
|
||||
} else if (endpoint === 'points') {
|
||||
url = `https://api.weather.gov/points/${lat},${lon}`;
|
||||
} else if (endpoint === 'stations') {
|
||||
url = `https://api.weather.gov/gridpoints/${office}/${x},${y}/stations`;
|
||||
}
|
||||
|
||||
export function useOpenMeteoAPI({ lat, lon }: { lat: number; lon: number }) {
|
||||
return useQuery({
|
||||
queryKey: ['GET', url],
|
||||
queryFn: () => callEndpoint(url),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSunStateAPI({ lat, lon }: { lat: number; lon: number }) {
|
||||
const date = new Date();
|
||||
const today = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
|
||||
date.setDate(date.getDate() + 1);
|
||||
const tomorrow = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
|
||||
const url = `https://api.sunrisesunset.io/json?lat=${lat}&lng=${lon}&date_start=${today}&date_end=${tomorrow}`;
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['GET', url],
|
||||
queryFn: () => callEndpoint(url),
|
||||
queryKey: ['GET', 'weather'],
|
||||
queryFn: () => callWeatherEndpoint(lat, lon),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -108,10 +108,6 @@ export function useCommonHooks() {
|
||||
const [currentLocation, setCurrentLocation] = useLocalStorage<Location>({
|
||||
key: 'location',
|
||||
defaultValue: {
|
||||
office: 'HGX',
|
||||
station: 'KHOU',
|
||||
x: 65,
|
||||
y: 96,
|
||||
lat: 29.750950134386066,
|
||||
lon: -95.36220654860034,
|
||||
},
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -11,10 +11,9 @@ export type { Theme, ThemePlaylist } from './theme';
|
||||
export { ThemeProperties } from './theme';
|
||||
export type { User } from './user';
|
||||
export type {
|
||||
CurrentWeather,
|
||||
ExtendedForecast,
|
||||
HourlyForecast,
|
||||
OutlookWeather,
|
||||
Daily,
|
||||
Hourly,
|
||||
MeteoWeather,
|
||||
SunState,
|
||||
} from './weather';
|
||||
export type { WolClient } from './wolClient';
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
export interface Location {
|
||||
office: string;
|
||||
station: string;
|
||||
x: number;
|
||||
y: number;
|
||||
lat: number;
|
||||
lon: number;
|
||||
}
|
||||
|
||||
@@ -1,73 +1,83 @@
|
||||
export interface CurrentWeather {
|
||||
icon: string;
|
||||
textDescription: string;
|
||||
temperature: number;
|
||||
highTemp: number;
|
||||
lowTemp: number;
|
||||
precipitation: string;
|
||||
humidity: number;
|
||||
heatIndex: number | null;
|
||||
windChill: number | null;
|
||||
windSpeed: number;
|
||||
windDirection: number;
|
||||
pressure: number;
|
||||
}
|
||||
|
||||
export interface OutlookWeather {
|
||||
name: string;
|
||||
icon: string;
|
||||
isDayTime: boolean;
|
||||
temperature: number;
|
||||
precipitation: number;
|
||||
windSpeed: string;
|
||||
windDirection: string;
|
||||
}
|
||||
|
||||
export interface ExtendedForecast {
|
||||
detailedForecast: string;
|
||||
isDaytime: boolean;
|
||||
name: string;
|
||||
shortForecast: string;
|
||||
temperature: number;
|
||||
temperatureUnit: string;
|
||||
}
|
||||
|
||||
export interface HourlyForecast {
|
||||
detailedForecast: string;
|
||||
dewpoint: DewPoint;
|
||||
endTime: string;
|
||||
icon: string;
|
||||
isDaytime: boolean;
|
||||
name: string;
|
||||
number: number;
|
||||
probabilityOfPrecipitation: ProbabilityOfPrecipitation;
|
||||
relativeHumidity: RelativeHumidity;
|
||||
shortForecast: string;
|
||||
startTime: string;
|
||||
temperature: number;
|
||||
temperatureTrend: string;
|
||||
temperatureUnit: string;
|
||||
windDirection: string;
|
||||
windSpeed: string;
|
||||
}
|
||||
|
||||
export interface ProbabilityOfPrecipitation {
|
||||
unitCode: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface RelativeHumidity {
|
||||
unitCode: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface DewPoint {
|
||||
unitCode: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface SunState {
|
||||
date: string;
|
||||
sunrise: string;
|
||||
sunset: string;
|
||||
}
|
||||
|
||||
export interface MeteoWeather {
|
||||
current: Current;
|
||||
hourly: Hourly;
|
||||
daily: Daily;
|
||||
}
|
||||
|
||||
export interface Current {
|
||||
time: Date;
|
||||
temperature_2m: number | undefined;
|
||||
relative_humidity_2m: number | undefined;
|
||||
apparent_temperature: number | undefined;
|
||||
is_day: number | undefined;
|
||||
wind_speed_10m: number | undefined;
|
||||
wind_direction_10m: number | undefined;
|
||||
wind_gusts_10m: number | undefined;
|
||||
precipitation: number | undefined;
|
||||
rain: number | undefined;
|
||||
showers: number | undefined;
|
||||
snowfall: number | undefined;
|
||||
surface_pressure: number | undefined;
|
||||
pressure_msl: number | undefined;
|
||||
cloud_cover: number | undefined;
|
||||
weather_code: number | undefined;
|
||||
}
|
||||
|
||||
export interface Hourly {
|
||||
apparent_temperature: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
cloud_cover: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
cloud_cover_high: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
cloud_cover_mid: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
cloud_cover_low: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
dew_point_2m: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
et0_fao_evapotranspiration: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
evapotranspiration: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
is_day: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
precipitation: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
precipitation_probability: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
pressure_msl: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
rain: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
relative_humidity_2m: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
showers: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
snow_depth: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
snowfall: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
sunshine_duration: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
surface_pressure: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
temperature_2m: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
time: Date[];
|
||||
uv_index_clear_sky: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
uv_index: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
vapour_pressure_deficit: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
visibility: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
weather_code: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
wind_direction_10m: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
wind_gusts_10m: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
wind_speed_10m: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
}
|
||||
|
||||
export interface Daily {
|
||||
apparent_temperature_max: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
apparent_temperature_min: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
daylight_duration: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
precipitation_hours: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
precipitation_probability_max: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
precipitation_sum: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
sunrise: Date[];
|
||||
sunset: Date[];
|
||||
sunshine_duration: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
temperature_2m_max: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
temperature_2m_min: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
time: Date[];
|
||||
uv_index_clear_sky_max: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
uv_index_max: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
weather_code: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
wind_direction_10m_dominant: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
wind_gusts_10m_max: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
wind_speed_10m_max: Float32Array<ArrayBufferLike> | null | undefined;
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "es2023",
|
||||
"lib": ["ES2023", "DOM"],
|
||||
"module": "esnext",
|
||||
"types": ["vite/client"],
|
||||
"skipLibCheck": true,
|
||||
"composite": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": false,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": false,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"target": "ES2022",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "nodenext",
|
||||
"module": "ES2022",
|
||||
"skipLibCheck": true,
|
||||
"rootDir": "./src",
|
||||
"allowJs": false,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"types": ["vite/client", "vitest"],
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "nodenext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
@@ -25,17 +24,13 @@
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
"paths": {
|
||||
"components": ["./components/index.ts"],
|
||||
"contexts": ["./contexts/index.ts"],
|
||||
"hooks": ["./hooks/index.ts"],
|
||||
"pages": ["./pages/index.ts"],
|
||||
"styles": ["./styles/index.ts"],
|
||||
"types": ["./types/index.ts"]
|
||||
"components": ["./src/components/index.ts"],
|
||||
"contexts": ["./src/contexts/index.ts"],
|
||||
"hooks": ["./src/hooks/index.ts"],
|
||||
"pages": ["./src/pages/index.ts"],
|
||||
"styles": ["./src/styles/index.ts"],
|
||||
"types": ["./src/types/index.ts"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.node.json" },
|
||||
{ "path": "./tsconfig.app.json" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "es2023",
|
||||
"lib": ["ES2023"],
|
||||
"module": "esnext",
|
||||
"types": ["node"],
|
||||
"skipLibCheck": true,
|
||||
"composite": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": false,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": false,
|
||||
|
||||
/* Linting */
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user