diff --git a/src/components/Footer.astro b/src/components/Footer.astro index 470f5f4..0cf9156 100644 --- a/src/components/Footer.astro +++ b/src/components/Footer.astro @@ -16,14 +16,14 @@ const currentYear = new Date().getFullYear(); transition:animate="none" >
-
+
- +
diff --git a/src/components/Header.astro b/src/components/Header.astro index 3c26b7a..4c51464 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -33,7 +33,7 @@ const currentPath = pathname.slice(1); aria-label="Toggle navigation" >
diff --git a/src/components/ui/sections/LatestPosts.astro b/src/components/ui/sections/LatestPosts.astro index 6b6c618..5c250c6 100644 --- a/src/components/ui/sections/LatestPosts.astro +++ b/src/components/ui/sections/LatestPosts.astro @@ -18,7 +18,7 @@ const recentPosts = posts .slice(0, 3); --- -
+

@@ -70,13 +70,13 @@ const shadowClasses = 'shadow-xs hover:shadow-lg'; }

- +
@@ -85,7 +85,7 @@ const shadowClasses = 'shadow-xs hover:shadow-lg'; - -
+
diff --git a/src/lib/directus.ts b/src/lib/directus.ts index 2dde1c2..23aaca1 100644 --- a/src/lib/directus.ts +++ b/src/lib/directus.ts @@ -2,6 +2,7 @@ import { createDirectus, rest } from '@directus/sdk'; import type { Global, + Weather, Post, Application, Experience, @@ -15,6 +16,7 @@ import { getDirectusURL } from '@lib/directusFunctions'; type Schema = { site_global: Global; + site_weather: Weather; posts: Post[]; site_applications: Application; site_experience: Experience; diff --git a/src/lib/directusTypes.ts b/src/lib/directusTypes.ts index dd0dc77..eb51dab 100644 --- a/src/lib/directusTypes.ts +++ b/src/lib/directusTypes.ts @@ -23,6 +23,13 @@ export type Global = { footer_image_alt: string; }; +export type Weather = { + id: string; + location: string; + latitude: string; + longitude: string; +} + export type Post = { slug: string; title: string; diff --git a/src/pages/404.astro b/src/pages/404.astro index f41f317..ae6e10d 100644 --- a/src/pages/404.astro +++ b/src/pages/404.astro @@ -29,7 +29,7 @@ const global = await directus.request(readSingleton('site_global')); }} >
-
+

+ + { return { blurDataURL, blurHeight, blurWidth, width, height }; } + +export { blurStyle }; diff --git a/src/support/paths.ts b/src/support/paths.ts index a75da6f..d0cd654 100644 --- a/src/support/paths.ts +++ b/src/support/paths.ts @@ -1,6 +1,6 @@ import { join } from 'node:path'; -export function resolveFilePath(path: string) { +function resolveFilePath(path: string) { if (path.startsWith('/')) { return resolveFilePathPublic(path); } @@ -8,12 +8,14 @@ export function resolveFilePath(path: string) { return resolveFilePathInternal(path); } -export function resolveFilePathPublic(path: string) { +function resolveFilePathPublic(path: string) { return join(process.cwd(), path); } -export function resolveFilePathInternal(path: string) { +function resolveFilePathInternal(path: string) { const normalizePath = path.startsWith('@') ? path.replace('@', '') : path; return join(process.cwd(), 'src/', normalizePath); } + +export { resolveFilePath, resolveFilePathPublic, resolveFilePathInternal }; diff --git a/src/support/weather.ts b/src/support/weather.ts new file mode 100644 index 0000000..e756c7e --- /dev/null +++ b/src/support/weather.ts @@ -0,0 +1,67 @@ +interface DayForecast { + date: string; + temp: number; + code: number; + label: string; + icon: string; + dayName: string; +} + +interface WeatherResult { + forecastDays: DayForecast[]; + error: string | null; +} + +const getWeatherInfo = (code: number) => { + if (code === 0) return { label: 'Clear', icon: '01d' }; + if (code >= 1 && code <= 3) return { label: 'Partly Cloudy', icon: '02d' }; + if (code === 45 || code === 48) return { label: 'Foggy', icon: '50d' }; + if (code >= 51 && code <= 55) return { label: 'Drizzle', icon: '09d' }; + if (code >= 61 && code <= 65) return { label: 'Rainy', icon: '10d' }; + if (code === 66 || code === 67) return { label: 'Freezing Rain', icon: '13d' }; + if (code >= 71 && code <= 75) return { label: 'Snowy', icon: '13d' }; + if (code === 77) return { label: 'Snow Grains', icon: '13d' }; + if (code >= 80 && code <= 82) return { label: 'Showers', icon: '09d' }; + if (code === 85 || code === 86) return { label: 'Snow Showers', icon: '13d' }; + if (code >= 95 && code <= 99) return { label: 'Stormy', icon: '11d' }; + + return { label: 'Unknown', icon: '03d' }; +}; + +const getDayName = (dateStr: string) => { + return new Date(dateStr).toLocaleDateString('en-US', { weekday: 'short' }); +}; + +async function getFiveDayForecast(latitude: string, longitude: string): Promise { + const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=weather_code,temperature_2m_max&timezone=auto&temperature_unit=fahrenheit`; + + try { + const response = await fetch(url); + if (!response.ok) throw new Error("Weather service unavailable"); + + const data = await response.json(); + + const forecastDays = data.daily.time.map((date: string, index: number): DayForecast => { + const code = data.daily.weather_code[index]; + const info = getWeatherInfo(code); + + return { + date, + temp: Math.round(data.daily.temperature_2m_max[index]), + code, + label: info.label, + icon: info.icon, + dayName: getDayName(date) + }; + }).slice(0, 5); + + return { forecastDays, error: null }; + } catch (e: unknown) { + return { + forecastDays: [], + error: e instanceof Error ? e.message : "An unexpected error occurred" + }; + } +} + +export { getFiveDayForecast };