import { h, ref, onMounted, defineComponent } from 'vue'; import { UIcon } from '#components'; import type { DashboardModule } from '../types/module'; // Map WMO weather codes to simple descriptions and Lucide icons const getWeatherInfo = (code: number) => { if (code === 0) return { label: 'Clear', icon: 'i-wi-day-sunny', color: 'text-yellow-400' }; if (code >= 1 && code < 4) return { label: 'Partly Cloudy', icon: 'i-wi-day-cloudy', color: 'text-gray-300' }; if (code >= 4 && code < 45) return { label: 'Cloudy', icon: 'i-wi-cloud', color: 'text-gray-300' }; if (code === 45 || code === 48) return { label: 'Fog', icon: 'i-wi-fog', color: 'text-gray-400' }; if (code >= 51 && code <= 55) return { label: 'Drizzle', icon: 'i-wi-rain-mix', color: 'text-blue-300' }; if (code >= 61 && code <= 65) return { label: 'Rain', icon: 'i-wi-rain', color: 'text-blue-400' }; if (code >= 71 && code <= 75) return { label: 'Snow', icon: 'i-wi-snow', color: 'text-white' }; if (code >= 80 && code <= 82) return { label: 'Showers', icon: 'i-wi-rain-wind', color: 'text-blue-500' }; if (code >= 95) return { label: 'Storm', icon: 'i-wi-storm-showers', color: 'text-purple-400' }; return { label: 'Unknown', icon: 'i-wi-na', color: 'text-gray-400' }; }; export const WeatherModule: DashboardModule = { name: 'weather', width: 2, height: 1, render(params) { const locationName = params?.location || (params?.lat && params?.lng ? `${params.lat}, ${params.lng}` : 'London'); const inputLat = params?.lat; const inputLng = params?.lng; return h(defineComponent({ setup() { const temp = ref(null); const condition = ref<{label: string, icon: string, color: string} | null>(null); const loading = ref(true); const error = ref(false); onMounted(async () => { try { let latitude = inputLat; let longitude = inputLng; // 1. Get coordinates for location if lat/lng are not provided if (latitude === undefined || longitude === undefined) { const geoRes = await $fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(locationName)}&count=1`); if (!geoRes.results || geoRes.results.length === 0) { throw new Error('Location not found'); } latitude = geoRes.results[0].latitude; longitude = geoRes.results[0].longitude; } // 2. Fetch current weather const weatherRes = await $fetch(`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t_weather=true`); temp.value = Math.round(weatherRes.current_weather.temperature); condition.value = getWeatherInfo(weatherRes.current_weather.weathercode); } catch (err) { console.error(err); error.value = true; } finally { loading.value = false; } }); return () => { let content; if (loading.value) { content = h('div', { class: 'text-white/50 text-sm animate-pulse' }, 'Loading weather...'); } else if (error.value || !condition.value) { content = h('div', { class: 'text-red-400/80 text-sm' }, 'Failed to load weather'); } else { // The resolved weather info content = h('div', { class: 'flex items-center gap-4 w-full justify-center' }, [ h(UIcon, { name: condition.value.icon, class: `${condition.value.color} w-20 h-20 drop-shadow-lg` }), h('div', { class: 'flex flex-col' }, [ h('span', { class: 'text-3xl font-light tracking-tight text-shadow text-white' }, `${temp.value}°C`), h('span', { class: 'text-sm text-white/50 font-medium tracking-wide' }, condition.value.label) ]) ]); } return h( 'div', { class: 'w-full h-full flex flex-col items-center justify-center p-5 rounded-2xl bg-white/10 backdrop-blur-md border border-white/10 shadow-lg group transition-all relative overflow-hidden' }, [ h('div', { class: 'absolute top-3 left-4 text-[10px] text-white/40 uppercase tracking-widest font-bold' }, locationName), content ] ); }; } })); } };