diff --git a/src/App.tsx b/src/App.tsx
index d2ceba2..38a688a 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -47,8 +47,14 @@ function App() {
const router = createBrowserRouter(
createRoutesFromElements(
- } />
- } />
+ }
+ />
+ }
+ />
,
),
);
diff --git a/src/hooks/useAPI.ts b/src/hooks/useAPI.ts
index e14b63d..a87dca4 100644
--- a/src/hooks/useAPI.ts
+++ b/src/hooks/useAPI.ts
@@ -45,6 +45,8 @@ export function useWeatherAPI({ endpoint }: { endpoint: string }) {
url = 'https://api.weather.gov/stations/KSGR/observations';
} else if (endpoint === 'forecast') {
url = 'https://api.weather.gov/gridpoints/HGX/55,96/forecast';
+ } else if (endpoint === 'hourly') {
+ url = 'https://api.weather.gov/gridpoints/HGX/55,96/forecast/hourly';
}
const callEndpoint = async (endpoint: string) => {
const options: object = {
@@ -158,7 +160,6 @@ export function useFileUploadAPI() {
endpoint: string,
body?: object | FormData | undefined | null,
) => {
-
const options = {
method: method,
headers: {
diff --git a/src/pages/homepage/Homepage.tsx b/src/pages/homepage/Homepage.tsx
index acf0a13..a42267d 100644
--- a/src/pages/homepage/Homepage.tsx
+++ b/src/pages/homepage/Homepage.tsx
@@ -1,13 +1,14 @@
import { Stack } from '@mantine/core';
-import { useLocalStorage } from '@mantine/hooks';
+import { useDisclosure, useLocalStorage } from '@mantine/hooks';
import { useEffect, useState } from 'react';
-import { useQueryAPI } from '../../hooks/useAPI';
+import { useQueryAPI, useWeatherAPI } from '../../hooks/useAPI';
import stackClasses from '../../styles/Stack.module.css';
import Applications from './components/Applications';
import Bookmarks from './components/Bookmarks';
import Header from './components/Header';
import Search from './components/Search';
import SettingsButton from './components/SettingsButton';
+import type { CurrentWeather, OutlookWeather } from './types/weather';
export default function Homepage({ theme }: any) {
const { data, isFetching } = useQueryAPI({ endpoint: 'api/messages' });
@@ -20,6 +21,34 @@ export default function Homepage({ theme }: any) {
key: 'bookmarkFilter',
defaultValue: ['All'],
});
+ const { data: basicWeather } = useWeatherAPI({ endpoint: 'baseline' });
+ const { data: weather } = useWeatherAPI({ endpoint: 'current' });
+ const [current, setCurrent] = useState({
+ icon: '',
+ temperature: 0,
+ highTemp: 0,
+ lowTemp: 0,
+ precipitation: 'N/A',
+ humidity: 0,
+ heatIndex: 0,
+ windSpeed: 0,
+ windDirection: 0,
+ });
+ const [outlook, setOutlook] = useState([
+ {
+ name: '',
+ icon: '',
+ isDayTime: true,
+ temperature: 0,
+ precipitation: 0,
+ windSpeed: '0 mph',
+ windDirection: 'N',
+ },
+ ]);
+ const { data: forecast } = useWeatherAPI({ endpoint: 'forecast' });
+ const { data: hourly } = useWeatherAPI({ endpoint: 'hourly' });
+ const [weatherModalOpened, { open: openWeatherModal, close: closeWeatherModal }] =
+ useDisclosure(false);
const isMobile = window.matchMedia('(max-width: 600px)').matches;
function date() {
const currentDate = new Date();
@@ -59,10 +88,90 @@ export default function Homepage({ theme }: any) {
}
}, [isFetching]);
+ useEffect(() => {
+ if (weather && basicWeather) {
+ setCurrent({
+ icon: weather.features[0].properties.icon?.replace('medium', 'large') || '',
+ temperature: Math.round(
+ (weather.features[0].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: Math.round(
+ (weather.features[0].properties.heatIndex.value * 9) / 5 + 32,
+ ),
+ windSpeed: Math.round(weather.features[0].properties.windSpeed.value / 1.60934),
+ windDirection: weather.features[0].properties.windDirection.value,
+ });
+ }
+ }, [weather, basicWeather]);
+
+ useEffect(() => {
+ if (forecast) {
+ setOutlook([
+ {
+ name: forecast.properties.periods[0].name,
+ icon: forecast.properties.periods[0].icon.replace('medium', 'large'),
+ isDayTime: forecast.properties.periods[0].isDaytime,
+ temperature: forecast.properties.periods[0].temperature,
+ precipitation: Math.round(
+ forecast.properties.periods[0].probabilityOfPrecipitation.value,
+ ),
+ windSpeed: forecast.properties.periods[0].windSpeed,
+ windDirection: forecast.properties.periods[0].windDirection,
+ },
+ {
+ name: forecast.properties.periods[1].name,
+ icon: forecast.properties.periods[1].icon.replace('medium', 'large'),
+ isDayTime: forecast.properties.periods[1].isDaytime,
+ temperature: forecast.properties.periods[1].temperature,
+ precipitation: Math.round(
+ forecast.properties.periods[1].probabilityOfPrecipitation.value,
+ ),
+ windSpeed: forecast.properties.periods[1].windSpeed,
+ windDirection: forecast.properties.periods[1].windDirection,
+ },
+ ]);
+ }
+ }, [forecast]);
+
return (
-
-
+
+
({
- icon: '',
- temperature: 0,
- highTemp: 0,
- lowTemp: 0,
- precipitation: 'N/A',
- humidity: 0,
- heatIndex: 0,
- windSpeed: 0,
- windDirection: 0,
- });
function fixDigits(k: any) {
if (k < 10) {
return `0${k}`;
@@ -107,7 +110,12 @@ function Header({ greet, date, isMobile }: any) {
}}
>
{!isMobile && (
-
+
+
);
}
diff --git a/src/pages/homepage/components/HelpModal.tsx b/src/pages/homepage/components/HelpModal.tsx
index b341c1c..59bd556 100644
--- a/src/pages/homepage/components/HelpModal.tsx
+++ b/src/pages/homepage/components/HelpModal.tsx
@@ -14,7 +14,7 @@ export default function HelpModal({
const [theme, setTheme] = useAtom(appTheme);
return (
+
+
+
+
+ {new Date(weather.startTime).toLocaleString('en-US', {
+ weekday: 'short',
+ month: 'numeric',
+ day: 'numeric',
+ })}
+
+
+ {new Date(weather.startTime).toLocaleTimeString('en-US', {
+ hour: 'numeric',
+ minute: 'numeric',
+ })}
+
+
+
+
+
+
+
+
+
+
+
+
+ {weather.windSpeed}
+
+
+
+
+
+ {weather.temperature}°{weather.temperatureUnit}
+
+
+ );
+}
diff --git a/src/pages/homepage/components/Search.tsx b/src/pages/homepage/components/Search.tsx
index d04d969..145eab4 100644
--- a/src/pages/homepage/components/Search.tsx
+++ b/src/pages/homepage/components/Search.tsx
@@ -1,42 +1,23 @@
import { Stack, TextInput } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
-import { useEffect, useState } from 'react';
-import { useQueryAPI, useWeatherAPI } from '../../../hooks';
+import { useState } from 'react';
+import { useQueryAPI } from '../../../hooks';
import { textInputClasses } from '../../../styles';
import HelpModal from './HelpModal';
import WeatherModal from './WeatherModal';
-export default function Search() {
+export default function Search({
+ current,
+ outlook,
+ hourly,
+ weatherModalOpened,
+ closeWeatherModal,
+ openWeatherModal,
+}: any) {
const { data: providers } = useQueryAPI({ endpoint: 'api/providers' });
- const { data: basicWeather } = useWeatherAPI({ endpoint: 'baseline' });
- const { data: weather } = useWeatherAPI({ endpoint: 'current' });
- const [current, setCurrent] = useState({
- icon: '',
- temperature: 0,
- highTemp: 0,
- lowTemp: 0,
- precipitation: 'N/A',
- humidity: 0,
- heatIndex: 0,
- windSpeed: 0,
- windDirection: 0,
- });
- const [outlook, setOutlook] = useState([
- {
- name: '',
- icon: '',
- isDayTime: true,
- temperature: 0,
- precipitation: 0,
- windSpeed: '0 mph',
- windDirection: 'N',
- },
- ]);
- const { data: forecast } = useWeatherAPI({ endpoint: 'forecast' });
const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] =
useDisclosure(false);
- const [weatherModalOpened, { open: openWeatherModal, close: closeWeatherModal }] =
- useDisclosure(false);
+
const [providerLabel, setProviderLabel] = useState('Google Search');
const [providerURL, setProviderURL] = useState('https://www.google.com/search?q=');
const [value, setValue] = useState('');
@@ -88,59 +69,6 @@ export default function Search() {
return !!pattern.test(str);
}
- useEffect(() => {
- if (weather && basicWeather) {
- setCurrent({
- icon: weather.features[0].properties.icon?.replace('medium', 'large') || '',
- temperature: Math.round(
- (weather.features[0].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: Math.round(
- (weather.features[0].properties.heatIndex.value * 9) / 5 + 32,
- ),
- windSpeed: Math.round(weather.features[0].properties.windSpeed.value / 1.60934),
- windDirection: weather.features[0].properties.windDirection.value,
- });
- }
- }, [weather, basicWeather]);
-
- useEffect(() => {
- if (forecast) {
- setOutlook([
- {
- name: forecast.properties.periods[0].name,
- icon: forecast.properties.periods[0].icon.replace('medium', 'large'),
- isDayTime: forecast.properties.periods[0].isDaytime,
- temperature: forecast.properties.periods[0].temperature,
- precipitation: Math.round(
- forecast.properties.periods[0].probabilityOfPrecipitation.value,
- ),
- windSpeed: forecast.properties.periods[0].windSpeed,
- windDirection: forecast.properties.periods[0].windDirection,
- },
- {
- name: forecast.properties.periods[1].name,
- icon: forecast.properties.periods[1].icon.replace('medium', 'large'),
- isDayTime: forecast.properties.periods[1].isDaytime,
- temperature: forecast.properties.periods[1].temperature,
- precipitation: Math.round(
- forecast.properties.periods[1].probabilityOfPrecipitation.value,
- ),
- windSpeed: forecast.properties.periods[1].windSpeed,
- windDirection: forecast.properties.periods[1].windDirection,
- },
- ]);
- }
- }, [forecast]);
return (
([]);
+ const [sunset, setSunset] = useState(100);
+
+ useEffect(() => {
+ if (hourly) {
+ setHourlyForecast(hourly.properties.periods.slice(1, 13));
+ }
+ }, [hourly]);
+
+ useEffect(() => {
+ if (hourlyForecast) {
+ setSunset(
+ (hourlyForecast.findIndex((item: any) => item.isDaytime === false) / 12) * 100,
+ );
+ }
+ }, [hourlyForecast]);
return (
-
- Currently
-
-
-
-
-
-
- {outlook.map((o: any) => (
-
- {o.name}
+
+
+
+ Currently
-
+
- ))}
-
+
+
+ {outlook.map((o: any) => (
+
+ {o.name}
+
+
+
+
+ ))}
+
+
+
+ 0 && hourlyForecast[0].isDaytime
+ ? `linear-gradient(90deg, rgba(245, 225, 171, 0.85) ${sunset - 5}%, rgba(35, 28, 42, 0.85) ${sunset + 5}%)`
+ : `linear-gradient(90deg, rgba(35, 28, 42, 0.85)${sunset - 5}%, rgba(245, 225, 171, 0.85)${sunset + 5}%)`,
+ padding: '.5rem',
+ borderRadius: '.5rem',
+ }}
+ >
+ {hourlyForecast.length > 0 &&
+ hourlyForecast.map((h: any) => {
+ return ;
+ })}
+
+
diff --git a/src/pages/homepage/types/weather.d.ts b/src/pages/homepage/types/weather.d.ts
index 682f3c9..afc01b5 100644
--- a/src/pages/homepage/types/weather.d.ts
+++ b/src/pages/homepage/types/weather.d.ts
@@ -1,4 +1,4 @@
-interface CurrentWeather {
+export interface CurrentWeather {
icon: string;
temperature: number;
highTemp: number;
@@ -10,7 +10,7 @@ interface CurrentWeather {
windDirection: number;
}
-interface OutlookWeather {
+export interface OutlookWeather {
name: string;
icon: string;
isDayTime: boolean;
diff --git a/vite.config.ts b/vite.config.ts
index cb19c90..cf9f50d 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -2,7 +2,7 @@ import react from '@vitejs/plugin-react';
import { defineConfig, loadEnv } from 'vite';
// https://vitejs.dev/config/
-export default defineConfig(({mode}) => {
+export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd());
return {
plugins: [react()],
@@ -28,5 +28,5 @@ export default defineConfig(({mode}) => {
},
},
},
-}
+ };
});