feat: move nav bar to the side (#6)
Co-authored-by: terraputix <terraputix@mailbox.org> Reviewed-on: useweb/ombrella#6
This commit is contained in:
+1
-268
@@ -1,270 +1,3 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { get } from 'svelte/store';
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
|
||||
import { storedLocation } from '$lib/stores/settings';
|
||||
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle
|
||||
} from '$lib/components/ui/card';
|
||||
|
||||
let mounted = $state(false);
|
||||
let location = get(storedLocation);
|
||||
|
||||
onMount(() => {
|
||||
mounted = true;
|
||||
});
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: 'Week Prediction',
|
||||
description:
|
||||
'Get detailed hourly weather forecasts for the next 7 days with interactive charts and temperature gradients.',
|
||||
href: `/weather`,
|
||||
icon: 'calendar',
|
||||
gradient: 'from-blue-500 to-cyan-500'
|
||||
},
|
||||
{
|
||||
title: 'Model Comparison',
|
||||
description:
|
||||
'Compare multiple weather models side-by-side to understand forecast uncertainty and accuracy.',
|
||||
href: '/weather/compare',
|
||||
icon: 'trending-up',
|
||||
gradient: 'from-purple-500 to-pink-500'
|
||||
},
|
||||
{
|
||||
title: '14 Day Weather',
|
||||
description:
|
||||
'Extended forecast with ensemble model spreads showing temperature ranges and uncertainty.',
|
||||
href: '/weather/14-day',
|
||||
icon: 'cloud',
|
||||
gradient: 'from-green-500 to-blue-500'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Open-Meteo Weather - Advanced Weather Forecasting</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Professional weather forecasting with multiple models, extended forecasts, and detailed comparisons. Powered by Open-Meteo API."
|
||||
/>
|
||||
<title>Open-Meteo Weather</title>
|
||||
</svelte:head>
|
||||
|
||||
<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 Section -->
|
||||
<div class="container mx-auto px-6 pt-20 pb-16">
|
||||
{#if mounted}
|
||||
<div class="mb-16 text-center" in:fade={{ duration: 800, delay: 200 }}>
|
||||
<h1
|
||||
class="mb-6 bg-gradient-to-r from-blue-600 via-purple-600 to-cyan-600 bg-clip-text text-5xl font-bold text-transparent md:text-7xl"
|
||||
>
|
||||
Open-Meteo Weather
|
||||
</h1>
|
||||
<p class="mx-auto mb-8 max-w-3xl text-xl text-gray-600 md:text-2xl dark:text-gray-300">
|
||||
Professional weather forecasting with advanced models, detailed comparisons, and extended
|
||||
predictions
|
||||
</p>
|
||||
|
||||
<!-- Quick Access Button -->
|
||||
<div in:fly={{ y: 20, duration: 600, delay: 600 }}>
|
||||
<Button
|
||||
href="/weather"
|
||||
size="lg"
|
||||
class="bg-gradient-to-r from-blue-600 to-purple-600 px-8 py-3 text-lg text-white shadow-lg transition-all duration-300 hover:from-blue-700 hover:to-purple-700 hover:shadow-xl"
|
||||
>
|
||||
View Current Weather
|
||||
<svg class="ml-2 h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 7l5 5m0 0l-5 5m5-5H6"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Features Grid -->
|
||||
<div class="mb-20 grid gap-8 md:grid-cols-3">
|
||||
{#each features as feature, index (feature.title)}
|
||||
{#if mounted}
|
||||
<div in:fly={{ y: 30, duration: 600, delay: 300 + index * 150 }}>
|
||||
<Card
|
||||
class="h-full border-0 bg-white/80 shadow-lg backdrop-blur-sm transition-all duration-300 hover:-translate-y-1 hover:shadow-xl dark:bg-gray-800/80"
|
||||
>
|
||||
<CardHeader>
|
||||
<div
|
||||
class="h-12 w-12 rounded-xl bg-gradient-to-r {feature.gradient} mb-4 flex items-center justify-center"
|
||||
>
|
||||
{#if feature.icon === 'calendar'}
|
||||
<svg
|
||||
class="h-6 w-6 text-white"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
{:else if feature.icon === 'trending-up'}
|
||||
<svg
|
||||
class="h-6 w-6 text-white"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"
|
||||
/>
|
||||
</svg>
|
||||
{:else if feature.icon === 'cloud'}
|
||||
<svg
|
||||
class="h-6 w-6 text-white"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
<CardTitle class="mb-2 text-xl">{feature.title}</CardTitle>
|
||||
<CardDescription class="text-gray-600 dark:text-gray-300">
|
||||
{feature.description}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="pt-0">
|
||||
<Button
|
||||
href={feature.href}
|
||||
variant="outline"
|
||||
class="w-full hover:bg-gradient-to-r hover:{feature.gradient} transition-all duration-300 hover:border-transparent hover:text-white"
|
||||
>
|
||||
Explore {feature.title}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- Current Weather for Selected Location -->
|
||||
{#if mounted && location}
|
||||
<div class="mx-auto max-w-2xl" in:fade={{ duration: 600, delay: 800 }}>
|
||||
<Card
|
||||
class="border-blue-200 bg-gradient-to-r from-blue-500/10 to-purple-500/10 dark:border-blue-700"
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle class="text-center text-2xl">
|
||||
Current Location: {location.name}
|
||||
</CardTitle>
|
||||
<CardDescription class="text-center">
|
||||
{location.country} • {location.latitude.toFixed(2)}°, {location.longitude.toFixed(2)}°
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent class="text-center">
|
||||
<Button href="/weather" variant="default" class="bg-blue-600 hover:bg-blue-700">
|
||||
View Detailed Forecast
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Features Overview Section -->
|
||||
<div class="bg-white/50 py-20 dark:bg-gray-800/50">
|
||||
<div class="container mx-auto px-6">
|
||||
{#if mounted}
|
||||
<div class="mb-16 text-center" in:fade={{ duration: 600, delay: 1000 }}>
|
||||
<h2 class="mb-4 text-4xl font-bold text-gray-800 dark:text-white">
|
||||
Why Choose Our Weather Service?
|
||||
</h2>
|
||||
<p class="mx-auto max-w-2xl text-xl text-gray-600 dark:text-gray-300">
|
||||
Powered by Open-Meteo API with multiple weather models and advanced visualization
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-4">
|
||||
{#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)}
|
||||
<div class="text-center" in:fly={{ y: 20, duration: 500, delay: 1200 + index * 100 }}>
|
||||
<div
|
||||
class="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gradient-to-r from-blue-500 to-purple-500"
|
||||
>
|
||||
<svg
|
||||
class="h-8 w-8 text-white"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
{#if feature.icon === 'layers'}
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"
|
||||
/>
|
||||
{:else if feature.icon === 'clock'}
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
{:else if feature.icon === 'bar-chart'}
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
|
||||
/>
|
||||
{:else if feature.icon === 'refresh'}
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
||||
/>
|
||||
{/if}
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="mb-2 text-lg font-semibold text-gray-800 dark:text-white">
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p class="text-gray-600 dark:text-gray-300">{feature.desc}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(.container) {
|
||||
max-width: 1200px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user