From 9a7e66d5d92a20067a566c568f04dc91a7e228ec Mon Sep 17 00:00:00 2001 From: frederic Date: Mon, 16 Feb 2026 01:23:52 +0100 Subject: [PATCH] feat: move nav bar to the side (#6) Co-authored-by: terraputix Reviewed-on: https://gitea.servert.ch/useweb/ombrella/pulls/6 --- package.json | 2 +- .../location/location-search.svelte | 349 +++++++++--------- src/lib/components/navigation/topbar.svelte | 93 +++++ .../components/navigation/weather-nav.svelte | 267 ++++++++------ src/lib/components/ui/popover/index.ts | 19 + .../ui/popover/popover-close.svelte | 7 + .../ui/popover/popover-content.svelte | 34 ++ .../ui/popover/popover-portal.svelte | 7 + .../ui/popover/popover-trigger.svelte | 18 + src/lib/components/ui/popover/popover.svelte | 7 + src/lib/utils/location.ts | 103 ++++++ src/routes/+layout.svelte | 51 ++- src/routes/+page.svelte | 269 +------------- src/routes/+page.ts | 7 + src/routes/layout.css | 116 +++--- src/routes/page.svelte.spec.ts | 7 +- src/routes/weather/+layout.svelte | 209 +---------- src/routes/weather/+layout.ts | 3 +- src/routes/weather/14-day/+layout.ts | 3 +- src/routes/weather/14-day/+page.ts | 15 + .../14-day/{ => [location]}/+page.svelte | 18 +- src/routes/weather/14-day/[location]/+page.ts | 13 + src/routes/weather/compare/+layout.ts | 14 - src/routes/weather/compare/+page.ts | 15 + .../compare/{ => [location]}/+page.svelte | 20 +- .../weather/compare/[location]/+page.ts | 13 + src/routes/weather/week/+page.ts | 14 +- .../weather/week/[location]/+page.svelte | 23 +- src/routes/weather/week/[location]/+page.ts | 90 +---- 29 files changed, 831 insertions(+), 975 deletions(-) create mode 100644 src/lib/components/navigation/topbar.svelte create mode 100644 src/lib/components/ui/popover/index.ts create mode 100644 src/lib/components/ui/popover/popover-close.svelte create mode 100644 src/lib/components/ui/popover/popover-content.svelte create mode 100644 src/lib/components/ui/popover/popover-portal.svelte create mode 100644 src/lib/components/ui/popover/popover-trigger.svelte create mode 100644 src/lib/components/ui/popover/popover.svelte create mode 100644 src/lib/utils/location.ts create mode 100644 src/routes/+page.ts create mode 100644 src/routes/weather/14-day/+page.ts rename src/routes/weather/14-day/{ => [location]}/+page.svelte (95%) create mode 100644 src/routes/weather/14-day/[location]/+page.ts delete mode 100644 src/routes/weather/compare/+layout.ts create mode 100644 src/routes/weather/compare/+page.ts rename src/routes/weather/compare/{ => [location]}/+page.svelte (96%) create mode 100644 src/routes/weather/compare/[location]/+page.ts diff --git a/package.json b/package.json index f6dff6e..ae75d41 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "lint": "prettier --check . && eslint .", "test:unit": "vitest", "test": "npm run test:unit -- --run", - "upgrade:ui": "npx shadcn-svelte@latest add alert button card checkbox dialog input label select separator switch -y -o && prettier --write src/lib/components/ui" + "upgrade:ui": "npx shadcn-svelte@latest add alert button card checkbox dialog input label popover select separator switch -y -o && prettier --write src/lib/components/ui" }, "devDependencies": { "@eslint/compat": "^1.4.0", diff --git a/src/lib/components/location/location-search.svelte b/src/lib/components/location/location-search.svelte index c26032c..11e1934 100644 --- a/src/lib/components/location/location-search.svelte +++ b/src/lib/components/location/location-search.svelte @@ -1,14 +1,14 @@ - - { - e.preventDefault(); - dialogOpen = !dialogOpen; - }} + + - - {label} - - + {label} + - - - - - - Find Your Location -

- Search for a city or use GPS to detect your location -

-
- -
-
-
-
- -
- + { + e.preventDefault(); + focusInput(); + }} + > +
+
+
+
+
-
- -
- {#await results} -
-
-
- Searching... -
-
- {:then results} - {#if results.results && results.results.length === 0} - {#if searchQuery.length < 2} - - - - - - Start typing to search for locations or use GPS to detect your current position - - - {:else} - - - - - - No locations found for "{searchQuery}". Try a different search term. - - - {/if} - {:else if !results.results} - - No locations found - - {:else} -
- {#each results.results || [] as location, i (i)} - - {/each} -
- {/if} - {:catch error} - - Error: {error.message} - - {/await} +
- - - + +
+ {#await results} +
+
+
+ Searching... +
+
+ {:then results} + {#if results.results && results.results.length === 0} + {#if searchQuery.length < 2} +
+ + + + + Start typing to search or use GPS to detect your position + +
+ {:else} + + + No locations found for "{searchQuery}". Try a different term. + + + {/if} + {:else if !results.results} + + No locations found + + {:else} +
+ {#each results.results || [] as location, i (i)} + + {/each} +
+ {/if} + {:catch error} + + Error: {error.message} + + {/await} +
+
+
+ diff --git a/src/lib/components/navigation/topbar.svelte b/src/lib/components/navigation/topbar.svelte new file mode 100644 index 0000000..9b106e3 --- /dev/null +++ b/src/lib/components/navigation/topbar.svelte @@ -0,0 +1,93 @@ + + +
+ + + + + {#if location} + + {/if} + + +
+ + +
+ { + navigateToLocation(event.detail); + }} + /> +
+
+ + diff --git a/src/lib/components/navigation/weather-nav.svelte b/src/lib/components/navigation/weather-nav.svelte index 3952f05..dcb7b01 100644 --- a/src/lib/components/navigation/weather-nav.svelte +++ b/src/lib/components/navigation/weather-nav.svelte @@ -2,30 +2,32 @@ import { resolve } from '$app/paths'; import { page } from '$app/stores'; - import { Button } from '$lib/components/ui/button'; + interface Props { + collapsed?: boolean; + onToggle?: () => void; + onMobileClose?: () => void; + } + + let { collapsed = false, onToggle, onMobileClose }: Props = $props(); const links = [ { - title: 'Current Weather', - url: '/weather/week', - description: 'Current conditions and overview', - icon: '🌡️' + title: '7-Day Forecast', + url: '/weather/week' as const, + icon: 'calendar' }, { title: 'Model Comparison', - url: '/weather/compare', - description: 'Compare multiple weather models', - icon: '📊' + url: '/weather/compare' as const, + icon: 'chart' }, { - title: '14 Day Forecast', - url: '/weather/14-day', - description: 'Extended forecast with uncertainty', - icon: '📅' + title: '14-Day Forecast', + url: '/weather/14-day' as const, + icon: 'extended' } ]; - let mobileNavOpened = $state(false); let currentPath = $derived($page.url.pathname); const isActive = (url: string) => { @@ -33,111 +35,164 @@ }; - + + + + + +
+ +
+ diff --git a/src/lib/components/ui/popover/index.ts b/src/lib/components/ui/popover/index.ts new file mode 100644 index 0000000..63e21bf --- /dev/null +++ b/src/lib/components/ui/popover/index.ts @@ -0,0 +1,19 @@ +import Close from './popover-close.svelte'; +import Content from './popover-content.svelte'; +import Portal from './popover-portal.svelte'; +import Trigger from './popover-trigger.svelte'; +import Root from './popover.svelte'; + +export { + Root, + Content, + Trigger, + Close, + Portal, + // + Root as Popover, + Content as PopoverContent, + Trigger as PopoverTrigger, + Close as PopoverClose, + Portal as PopoverPortal +}; diff --git a/src/lib/components/ui/popover/popover-close.svelte b/src/lib/components/ui/popover/popover-close.svelte new file mode 100644 index 0000000..dc4dec4 --- /dev/null +++ b/src/lib/components/ui/popover/popover-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/popover/popover-content.svelte b/src/lib/components/ui/popover/popover-content.svelte new file mode 100644 index 0000000..3f62623 --- /dev/null +++ b/src/lib/components/ui/popover/popover-content.svelte @@ -0,0 +1,34 @@ + + + + + diff --git a/src/lib/components/ui/popover/popover-portal.svelte b/src/lib/components/ui/popover/popover-portal.svelte new file mode 100644 index 0000000..25efb87 --- /dev/null +++ b/src/lib/components/ui/popover/popover-portal.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/popover/popover-trigger.svelte b/src/lib/components/ui/popover/popover-trigger.svelte new file mode 100644 index 0000000..b3dbc2c --- /dev/null +++ b/src/lib/components/ui/popover/popover-trigger.svelte @@ -0,0 +1,18 @@ + + + diff --git a/src/lib/components/ui/popover/popover.svelte b/src/lib/components/ui/popover/popover.svelte new file mode 100644 index 0000000..f39b867 --- /dev/null +++ b/src/lib/components/ui/popover/popover.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/utils/location.ts b/src/lib/utils/location.ts new file mode 100644 index 0000000..4a2c3d7 --- /dev/null +++ b/src/lib/utils/location.ts @@ -0,0 +1,103 @@ +import { error, redirect } from '@sveltejs/kit'; + +import { type GeoLocation, storedLocation } from '$lib/stores/settings'; + +import { geoLocationNameToRoute } from '$lib/utils/meteo'; + +export function buildLocationRoute(location: GeoLocation): string { + const locationRoute = geoLocationNameToRoute(location.name); + if (location.population && location.population > 543000) { + return locationRoute; + } + return locationRoute + '_' + location.id; +} + +interface ResolveLocationOptions { + urlLocation: string; + routePrefix: string; + event: { + fetch: typeof fetch; + url: URL; + }; +} + +export async function resolveLocationFromRoute({ + urlLocation, + routePrefix, + event +}: ResolveLocationOptions): Promise { + let location: GeoLocation; + + if (urlLocation.includes('N') && urlLocation.includes('E')) { + const parts = urlLocation.split(/N|E/); + const latitude = parseFloat(parts[0]); + const longitude = parseFloat(parts[1]); + + location = { + id: 0, + name: `${latitude}N° ${longitude}E°`, + latitude, + longitude, + elevation: 0, + feature_code: 'COORD', + country_code: undefined, + admin1_id: undefined, + admin3_id: undefined, + admin4_id: undefined, + timezone: 'UTC', + population: undefined, + postcodes: undefined, + country_id: undefined, + country: undefined, + admin1: undefined, + admin3: undefined, + admin4: undefined + }; + } else { + let urlLocationName: string; + let urlLocationId: string | undefined; + + if (urlLocation.includes('_')) { + const split = urlLocation.split('_'); + urlLocationName = split[0]; + urlLocationId = split[1]; + } else if (/^\d+$/.test(urlLocation)) { + urlLocationName = ''; + urlLocationId = urlLocation; + } else { + urlLocationName = urlLocation; + urlLocationId = undefined; + } + + if (urlLocationId) { + const res = await event.fetch( + `https://geocoding-api.open-meteo.com/v1/get?id=${urlLocationId}` + ); + location = await res.json(); + } else { + const res = await event.fetch( + `https://geocoding-api.open-meteo.com/v1/search?name=${urlLocationName}&count=1&language=en&format=json` + ); + const geocodingResponse = await res.json(); + if (geocodingResponse.results) { + location = geocodingResponse.results[0]; + } else { + error(404, 'Location not found'); + } + } + + const locationRoute = geoLocationNameToRoute(location.name); + const canonicalSuffix = + location.population && location.population > 543000 + ? locationRoute + : locationRoute + '_' + location.id; + const canonicalPath = `${routePrefix}${canonicalSuffix}`; + + if (event.url.pathname !== canonicalPath) { + throw redirect(303, canonicalPath); + } + } + + storedLocation.set(location); + return location; +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 1ff71bb..331afe5 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,9 +1,27 @@ @@ -12,6 +30,33 @@ -
- {@render children()} -
+
+ + + + + {#if mobileMenuOpen} + + {/if} + + +
+ + +
+ {@render children()} +
+
+
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index af09426..33e9f0e 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,270 +1,3 @@ - - - Open-Meteo Weather - Advanced Weather Forecasting - + Open-Meteo Weather - -
- -
- {#if mounted} -
-

- Open-Meteo Weather -

-

- Professional weather forecasting with advanced models, detailed comparisons, and extended - predictions -

- - -
- -
-
- {/if} - - -
- {#each features as feature, index (feature.title)} - {#if mounted} -
- - -
- {#if feature.icon === 'calendar'} - - - - {:else if feature.icon === 'trending-up'} - - - - {:else if feature.icon === 'cloud'} - - - - {/if} -
- {feature.title} - - {feature.description} - -
- - - -
-
- {/if} - {/each} -
- - - {#if mounted && location} -
- - - - Current Location: {location.name} - - - {location.country} • {location.latitude.toFixed(2)}°, {location.longitude.toFixed(2)}° - - - - - - -
- {/if} -
- - -
-
- {#if mounted} -
-

- Why Choose Our Weather Service? -

-

- Powered by Open-Meteo API with multiple weather models and advanced visualization -

-
- -
- {#each [{ title: 'Multiple Models', desc: 'Compare ECMWF, GFS, and more', icon: 'layers' }, { title: 'Extended Forecasts', desc: 'Up to 14 days ahead', icon: 'clock' }, { title: 'Interactive Charts', desc: 'Apache ECharts visualization', icon: 'bar-chart' }, { title: 'Real-time Data', desc: 'Always up-to-date', icon: 'refresh' }] as feature, index (feature.title)} -
-
- - {#if feature.icon === 'layers'} - - {:else if feature.icon === 'clock'} - - {:else if feature.icon === 'bar-chart'} - - {:else if feature.icon === 'refresh'} - - {/if} - -
-

- {feature.title} -

-

{feature.desc}

-
- {/each} -
- {/if} -
-
-
- - diff --git a/src/routes/+page.ts b/src/routes/+page.ts new file mode 100644 index 0000000..db2ed9b --- /dev/null +++ b/src/routes/+page.ts @@ -0,0 +1,7 @@ +import { redirect } from '@sveltejs/kit'; + +import type { PageLoad } from './$types'; + +export const load = (async () => { + throw redirect(303, '/weather/week/'); +}) satisfies PageLoad; diff --git a/src/routes/layout.css b/src/routes/layout.css index b37c9b3..33a587d 100644 --- a/src/routes/layout.css +++ b/src/routes/layout.css @@ -6,71 +6,75 @@ :root { --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.129 0.042 264.695); + --background: oklch(0.985 0.002 90); + --foreground: oklch(0.205 0.02 60); --card: oklch(1 0 0); - --card-foreground: oklch(0.129 0.042 264.695); + --card-foreground: oklch(0.205 0.02 60); --popover: oklch(1 0 0); - --popover-foreground: oklch(0.129 0.042 264.695); - --primary: oklch(0.208 0.042 265.755); - --primary-foreground: oklch(0.984 0.003 247.858); - --secondary: oklch(0.968 0.007 247.896); - --secondary-foreground: oklch(0.208 0.042 265.755); - --muted: oklch(0.968 0.007 247.896); - --muted-foreground: oklch(0.554 0.046 257.417); - --accent: oklch(0.968 0.007 247.896); - --accent-foreground: oklch(0.208 0.042 265.755); + --popover-foreground: oklch(0.205 0.02 60); + --primary: oklch(0.65 0.17 55); + --primary-foreground: oklch(1 0 0); + --secondary: oklch(0.965 0.01 85); + --secondary-foreground: oklch(0.25 0.02 60); + --muted: oklch(0.96 0.008 85); + --muted-foreground: oklch(0.5 0.02 60); + --accent: oklch(0.96 0.008 85); + --accent-foreground: oklch(1 0 0); --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.929 0.013 255.508); - --input: oklch(0.929 0.013 255.508); - --ring: oklch(0.704 0.04 256.788); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.984 0.003 247.858); - --sidebar-foreground: oklch(0.129 0.042 264.695); - --sidebar-primary: oklch(0.208 0.042 265.755); - --sidebar-primary-foreground: oklch(0.984 0.003 247.858); - --sidebar-accent: oklch(0.968 0.007 247.896); - --sidebar-accent-foreground: oklch(0.208 0.042 265.755); - --sidebar-border: oklch(0.929 0.013 255.508); - --sidebar-ring: oklch(0.704 0.04 256.788); + --border: oklch(0.91 0.01 80); + --input: oklch(0.91 0.01 80); + --ring: oklch(0.65 0.17 55); + --chart-1: oklch(0.65 0.17 55); + --chart-2: oklch(0.62 0.16 250); + --chart-3: oklch(0.75 0.15 75); + --chart-4: oklch(0.55 0.12 250); + --chart-5: oklch(0.8 0.14 65); + --sidebar-foreground: oklch(0.3 0.02 60); + --sidebar-primary: oklch(0.65 0.17 55); + --sidebar-primary-foreground: oklch(1 0 0); + --sidebar-accent: oklch(0.95 0.03 70); + --sidebar-accent-foreground: oklch(0.3 0.02 60); + --sidebar-border: oklch(0.92 0.01 80); + --sidebar-ring: oklch(0.65 0.17 55); + --sidebar: oklch(0.99 0.003 85); + --topbar-bg: oklch(1 0 0); + --topbar-border: oklch(0.92 0.01 80); } .dark { - --background: oklch(0.129 0.042 264.695); - --foreground: oklch(0.984 0.003 247.858); - --card: oklch(0.208 0.042 265.755); - --card-foreground: oklch(0.984 0.003 247.858); - --popover: oklch(0.208 0.042 265.755); - --popover-foreground: oklch(0.984 0.003 247.858); - --primary: oklch(0.929 0.013 255.508); - --primary-foreground: oklch(0.208 0.042 265.755); - --secondary: oklch(0.279 0.041 260.031); - --secondary-foreground: oklch(0.984 0.003 247.858); - --muted: oklch(0.279 0.041 260.031); - --muted-foreground: oklch(0.704 0.04 256.788); - --accent: oklch(0.279 0.041 260.031); - --accent-foreground: oklch(0.984 0.003 247.858); + --background: oklch(0.17 0.015 60); + --foreground: oklch(0.96 0.005 85); + --card: oklch(0.22 0.015 60); + --card-foreground: oklch(0.96 0.005 85); + --popover: oklch(0.22 0.015 60); + --popover-foreground: oklch(0.96 0.005 85); + --primary: oklch(0.72 0.17 55); + --primary-foreground: oklch(0.15 0.02 60); + --secondary: oklch(0.26 0.015 60); + --secondary-foreground: oklch(0.96 0.005 85); + --muted: oklch(0.26 0.015 60); + --muted-foreground: oklch(0.65 0.02 60); + --accent: oklch(0.65 0.16 250); + --accent-foreground: oklch(0.96 0.005 85); --destructive: oklch(0.704 0.191 22.216); --border: oklch(1 0 0 / 10%); --input: oklch(1 0 0 / 15%); - --ring: oklch(0.551 0.027 264.364); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.208 0.042 265.755); - --sidebar-foreground: oklch(0.984 0.003 247.858); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.984 0.003 247.858); - --sidebar-accent: oklch(0.279 0.041 260.031); - --sidebar-accent-foreground: oklch(0.984 0.003 247.858); + --ring: oklch(0.72 0.17 55); + --chart-1: oklch(0.72 0.17 55); + --chart-2: oklch(0.65 0.16 250); + --chart-3: oklch(0.8 0.14 65); + --chart-4: oklch(0.6 0.2 300); + --chart-5: oklch(0.7 0.22 20); + --sidebar-foreground: oklch(0.96 0.005 85); + --sidebar-primary: oklch(0.72 0.17 55); + --sidebar-primary-foreground: oklch(0.15 0.02 60); + --sidebar-accent: oklch(0.26 0.02 55); + --sidebar-accent-foreground: oklch(0.96 0.005 85); --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.551 0.027 264.364); + --sidebar-ring: oklch(0.72 0.17 55); + --sidebar: oklch(0.19 0.015 60); + --topbar-bg: oklch(0.2 0.015 60); + --topbar-border: oklch(1 0 0 / 10%); } @theme inline { @@ -109,6 +113,8 @@ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); + --color-topbar: var(--topbar-bg); + --color-topbar-border: var(--topbar-border); } @layer base { diff --git a/src/routes/page.svelte.spec.ts b/src/routes/page.svelte.spec.ts index 5b110b3..08ddddc 100644 --- a/src/routes/page.svelte.spec.ts +++ b/src/routes/page.svelte.spec.ts @@ -1,14 +1,13 @@ import { describe, expect, it } from 'vitest'; import { render } from 'vitest-browser-svelte'; -import { page } from 'vitest/browser'; import Page from './+page.svelte'; describe('/+page.svelte', () => { - it('should render h1', async () => { + it('should render the redirect page with correct title', async () => { render(Page); - const heading = page.getByRole('heading', { level: 1 }); - await expect.element(heading).toBeInTheDocument(); + const title = document.querySelector('title'); + expect(title?.textContent).toBe('Open-Meteo Weather'); }); }); diff --git a/src/routes/weather/+layout.svelte b/src/routes/weather/+layout.svelte index c6723cb..8e8c880 100644 --- a/src/routes/weather/+layout.svelte +++ b/src/routes/weather/+layout.svelte @@ -1,216 +1,9 @@ - - -
- -
-
-
-
- {#if mounted} -
- -
-

- {getPageTitle()} -

-
- - -
-
- - {#if location} -
- -
-
- {location.country} -
-
-

- {location.name} -

-

- {#if location.admin1}{location.admin1}, - {/if}{location.country} -

-

- {location.latitude.toFixed(2)}°N, {location.longitude.toFixed(2)}°E - {#if location.elevation}• {location.elevation.toFixed(0)}m{/if} -

-
-
- - - {#if currentWeather} -
-
-
- {getWeatherIcon(currentWeather.current.weather_code)} -
-
- {Math.round(currentWeather.current.temperature_2m)}°C -
-
-
- {/if} -
- {/if} - - -
- { - storedLocation.set(event.detail); - }} - /> -
-
-
- - - {#if location?.population} -
-
-
Population
-
{location.population.toLocaleString()}
-
- {#if location.timezone} -
-
Timezone
-
{location.timezone}
-
- {/if} -
- {/if} -
- {/if} -
-
- - -
-
-
-
-
-
- - -
- {#if mounted} -
- {@render children?.()} -
- {/if} -
-
- - +{@render children?.()} diff --git a/src/routes/weather/+layout.ts b/src/routes/weather/+layout.ts index d2a2971..d12525b 100644 --- a/src/routes/weather/+layout.ts +++ b/src/routes/weather/+layout.ts @@ -4,9 +4,8 @@ import { storedLocation } from '$lib/stores/settings'; import type { LayoutLoad } from './$types'; -const location = get(storedLocation); - export const load: LayoutLoad = async () => { + const location = get(storedLocation); return { title: `Weather ${location.name}`, location: location diff --git a/src/routes/weather/14-day/+layout.ts b/src/routes/weather/14-day/+layout.ts index 55c9dc1..4ab5514 100644 --- a/src/routes/weather/14-day/+layout.ts +++ b/src/routes/weather/14-day/+layout.ts @@ -4,9 +4,8 @@ import { storedLocation } from '$lib/stores/settings'; import type { LayoutLoad } from './$types'; -const location = get(storedLocation); - export const load: LayoutLoad = async () => { + const location = get(storedLocation); return { heroTitle: `14 Day Weather ${location.name}`, heroDescription: location.admin1 ?? '' + ' ' + location.country diff --git a/src/routes/weather/14-day/+page.ts b/src/routes/weather/14-day/+page.ts new file mode 100644 index 0000000..a860658 --- /dev/null +++ b/src/routes/weather/14-day/+page.ts @@ -0,0 +1,15 @@ +import { get } from 'svelte/store'; + +import { redirect } from '@sveltejs/kit'; + +import { storedLocation } from '$lib/stores/settings'; + +import { buildLocationRoute } from '$lib/utils/location'; + +import type { PageLoad } from './$types'; + +export const load = (async () => { + const location = get(storedLocation); + const locationRoute = buildLocationRoute(location); + throw redirect(303, '/weather/14-day/' + locationRoute); +}) satisfies PageLoad; diff --git a/src/routes/weather/14-day/+page.svelte b/src/routes/weather/14-day/[location]/+page.svelte similarity index 95% rename from src/routes/weather/14-day/+page.svelte rename to src/routes/weather/14-day/[location]/+page.svelte index 1500798..94c2ea9 100644 --- a/src/routes/weather/14-day/+page.svelte +++ b/src/routes/weather/14-day/[location]/+page.svelte @@ -1,8 +1,7 @@