initial cleanup

This commit is contained in:
terraputix
2026-01-07 23:45:10 +01:00
parent 9b39255e64
commit 1e434e0a84
325 changed files with 1903 additions and 1000 deletions
+188 -79
View File
@@ -1,15 +1,11 @@
<script lang="ts">
import { get } from 'svelte/store';
import { page } from '$app/state';
import { geoLocationNameToRoute } from '$lib/utils/meteo';
import Button from '$lib/components/ui/button/button.svelte';
import { fly, fade } from 'svelte/transition';
import { onMount } from 'svelte';
import WeatherNav from '$lib/components/navigation/weather-nav.svelte';
import LocationSearch from '$lib/components/location/location-search.svelte';
import { storedLocation } from '$lib/stores/settings';
let location = get(storedLocation);
const locationRoute = geoLocationNameToRoute(location.name);
import { get } from 'svelte/store';
import { page } from '$app/stores';
interface Props {
children?: import('svelte').Snippet;
@@ -17,81 +13,194 @@
let { children }: Props = $props();
const links = [
{
title: 'Weather Forecast',
url: '/en/weather',
children: [
{
title: 'Week Prediction',
url:
'/en/weather/week/' +
(location.population
? location.population > 543000
? locationRoute
: locationRoute + '_' + location.id
: locationRoute + '_' + location.id)
},
{ title: 'Model Comparison', url: '/en/weather/compare' },
{ title: '14 Day Weather', url: '/en/weather/14-day' }
]
}
];
let location = $state(get(storedLocation));
let mounted = $state(false);
let currentWeather = $state(null);
let selectedPath = $derived.by(() => {
for (const link of links) {
if (link.children) {
for (const l of link.children) {
if (page.url.pathname.includes(l.url) || page.url.pathname.includes(l.url + '/')) {
return l;
}
}
}
if (page.url.pathname === link.url || page.url.pathname === link.url + '/') {
return link;
}
// Subscribe to location changes
storedLocation.subscribe((value) => {
location = value;
if (mounted) {
loadCurrentWeather();
}
return {};
});
let mobileNavOpened = $state(false);
onMount(() => {
mounted = true;
loadCurrentWeather();
});
const loadCurrentWeather = async () => {
if (!location?.latitude) return;
try {
const response = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${location.latitude}&longitude=${location.longitude}&current=temperature_2m,weather_code&forecast_days=1`
);
const data = await response.json();
currentWeather = data;
} catch (error) {
console.error('Failed to load current weather:', error);
}
};
const getWeatherIcon = (code) => {
const iconMap = {
0: '☀️',
1: '🌤️',
2: '⛅',
3: '☁️',
45: '🌫️',
48: '🌫️',
51: '🌦️',
53: '🌦️',
55: '🌦️',
61: '🌧️',
63: '🌧️',
65: '🌧️',
71: '🌨️',
73: '🌨️',
75: '❄️',
95: '⛈️'
};
return iconMap[code] || '☁️';
};
const getPageTitle = () => {
const path = $page.url.pathname;
if (path.includes('/compare')) return 'Model Comparison';
if (path.includes('/14-day')) return '14 Day Forecast';
if (path.includes('/week')) return 'Week Prediction';
return 'Weather Forecast';
};
</script>
<div class="mb-12 flex flex-col md:mb-24 md:flex-row">
<aside class="w-full md:w-1/6 md:max-w-[400px] md:min-w-[230px]">
<nav class="sticky top-0 flex flex-col p-6 pb-3 md:pr-3 md:pb-6">
<Button
variant="outline"
class="flex justify-start p-3 md:hidden"
onclick={() => {
mobileNavOpened = !mobileNavOpened;
}}
>
<svg
class="lucide lucide-chevrons-up-down mr-2"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m7 15 5 5 5-5" />
<path d="m7 9 5-5 5 5" />
</svg><b>{selectedPath.title}</b>
</Button>
<WeatherNav />
<ul
class={`list-unstyled overflow-hidden duration-500 ${mobileNavOpened ? 'mt-2 max-h-[968px] md:max-h-[unset]' : 'max-h-0 md:max-h-[unset] '}`}
></ul>
</nav>
</aside>
<div
class="lg:max-w-unset flex flex-1 flex-col p-6 pt-0 md:max-w-[calc(100%-230px)] md:pt-6 md:pl-3"
>
{@render children?.()}
<div
class="min-h-screen bg-gradient-to-br from-blue-50 via-white to-cyan-50 dark:from-gray-900 dark:via-gray-800 dark:to-blue-900"
>
<!-- Hero Header Section -->
<div class="relative overflow-hidden bg-gradient-to-r from-blue-600 via-purple-600 to-cyan-600">
<div class="absolute inset-0 bg-black/10"></div>
<div class="relative">
<div class="container mx-auto px-6 py-12">
{#if mounted}
<div class="flex flex-col items-center space-y-6" in:fade={{ duration: 800 }}>
<!-- Page Title -->
<div class="text-center" in:fly={{ y: -20, duration: 600, delay: 200 }}>
<h1 class="mb-2 text-3xl font-bold text-white md:text-4xl">
{getPageTitle()}
</h1>
</div>
<!-- Location Display & Search -->
<div class="w-full max-w-2xl" in:fly={{ y: 20, duration: 600, delay: 400 }}>
<div
class="rounded-2xl bg-white/95 p-6 shadow-2xl backdrop-blur-md dark:bg-gray-800/95"
>
<!-- Current Location Display -->
{#if location}
<div
class="flex flex-col items-center justify-between space-y-4 md:flex-row md:space-y-0 md:space-x-6"
>
<!-- Location Info -->
<div class="flex flex-1 items-center space-x-4">
<div class="flex-shrink-0">
<img
class="h-12 w-12 rounded-full shadow-lg"
src="/images/country-flags/{(
location.country_code || 'united_nations'
).toLowerCase()}.svg"
alt={location.country}
/>
</div>
<div class="flex-1 text-left">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">
{location.name}
</h2>
<p class="text-gray-600 dark:text-gray-300">
{#if location.admin1}{location.admin1},
{/if}{location.country}
</p>
<p class="text-sm text-gray-500 dark:text-gray-400">
{location.latitude.toFixed(2)}°N, {location.longitude.toFixed(2)}°E
{#if location.elevation}{location.elevation.toFixed(0)}m{/if}
</p>
</div>
</div>
<!-- Current Weather -->
{#if currentWeather}
<div class="flex items-center space-x-3" in:fade={{ delay: 800 }}>
<div class="text-center">
<div class="mb-1 text-3xl">
{getWeatherIcon(currentWeather.current.weather_code)}
</div>
<div class="text-2xl font-bold text-gray-900 dark:text-white">
{Math.round(currentWeather.current.temperature_2m)}°C
</div>
</div>
</div>
{/if}
</div>
{/if}
<!-- Location Search -->
<div class="mt-6 border-t border-gray-200 pt-6 dark:border-gray-600">
<LocationSearch
label="🔍 Change location or search for a new city..."
on:location={(event) => {
storedLocation.set(event.detail);
}}
/>
</div>
</div>
</div>
<!-- Stats Row (if location has population) -->
{#if location?.population}
<div
class="flex justify-center space-x-8 text-white/90"
in:fly={{ y: 20, duration: 600, delay: 600 }}
>
<div class="text-center">
<div class="text-sm font-medium">Population</div>
<div class="text-lg font-bold">{location.population.toLocaleString()}</div>
</div>
{#if location.timezone}
<div class="text-center">
<div class="text-sm font-medium">Timezone</div>
<div class="text-lg font-bold">{location.timezone}</div>
</div>
{/if}
</div>
{/if}
</div>
{/if}
</div>
</div>
<!-- Decorative elements -->
<div class="pointer-events-none absolute top-0 left-0 h-full w-full overflow-hidden">
<div class="absolute -top-4 -right-4 h-24 w-24 rounded-full bg-white/10"></div>
<div class="absolute top-1/3 -left-8 h-16 w-16 rounded-full bg-white/5"></div>
<div class="absolute bottom-8 left-1/4 h-12 w-12 rounded-full bg-white/10"></div>
</div>
</div>
<!-- Main Content -->
<div class="container mx-auto px-6 py-8">
{#if mounted}
<div in:fade={{ duration: 600, delay: 400 }}>
{@render children?.()}
</div>
{/if}
</div>
</div>
<style>
:global(.container) {
max-width: 1200px;
}
</style>