Co-authored-by: terraputix <terraputix@mailbox.org> Reviewed-on: useweb/open-meteo-blue#4
271 lines
8.9 KiB
Svelte
271 lines
8.9 KiB
Svelte
<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."
|
|
/>
|
|
</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>
|