"use client"; import { useCallback, useState } from "react"; import { motion } from "framer-motion"; import { CalendarDays, ChevronRight, Clock3, CloudRain, CloudSun, Droplets, ExternalLink, RefreshCw, ThermometerSun, Wind, type LucideIcon } from "lucide-react"; import { DayForecastCharts } from "@/components/charts/day-forecast-charts"; import { DayForecastModal } from "@/components/forecast/day-forecast-modal"; import { ForecastIcon } from "@/components/forecast/forecast-icon"; import { LoadingSkeleton } from "@/components/states/loading-skeleton"; import { EmptyState } from "@/components/states/empty-state"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { useForecast } from "@/hooks/use-forecast"; import { useI18n } from "@/lib/i18n"; import { formatForecastRainfall, formatForecastTemperature, formatForecastWind, getForecastCondition, getHourlyForecastForDay, getUpcomingHourlyForecast, } from "@/lib/forecast-utils"; import type { DailyForecast, HourlyForecast } from "@/types/forecast"; function formatHour(value: string) { return value.slice(11, 16); } function formatDay(value: string, locale: string, todayLabel: string, index: number) { if (index === 0) return todayLabel; return new Intl.DateTimeFormat(locale, { weekday: "short", timeZone: "UTC" }).format(new Date(`${value}T12:00:00Z`)); } function getMaximum(values: Array) { const availableValues = values.filter((value): value is number => value !== null); return availableValues.length ? Math.max(...availableValues) : null; } function getMinimum(values: Array) { const availableValues = values.filter((value): value is number => value !== null); return availableValues.length ? Math.min(...availableValues) : null; } function getTotal(values: Array) { const availableValues = values.filter((value): value is number => value !== null); return availableValues.length ? availableValues.reduce((total, value) => total + value, 0) : null; } function HourlySummaryMetric({ icon: Icon, label, value }: { icon: LucideIcon; label: string; value: string }) { return (

{label}

{value}

); } function HourlyForecastSummary({ hours }: { hours: HourlyForecast[] }) { const { language, t } = useI18n(); const minimumTemperature = getMinimum(hours.map((hour) => hour.temperature)); const maximumTemperature = getMaximum(hours.map((hour) => hour.temperature)); const maximumWind = getMaximum(hours.map((hour) => hour.windSpeed)); const rainfallTotal = getTotal(hours.map((hour) => hour.precipitation)); const maximumRainfallProbability = getMaximum(hours.map((hour) => hour.precipitationProbability)); const temperatureRange = minimumTemperature === null || maximumTemperature === null ? "—" : `${formatForecastTemperature(minimumTemperature, language)} / ${formatForecastTemperature(maximumTemperature, language)}`; return (

{t("forecast.nextHoursOverview")}

); } function DailyForecastRow({ day, index, onSelect }: { day: DailyForecast; index: number; onSelect: (day: DailyForecast) => void }) { const { language, locale, t } = useI18n(); const label = formatDay(day.date, locale, t("forecast.today"), index); return ( onSelect(day)} >

{label}

{getForecastCondition(day.weatherCode, language)} · {formatForecastRainfall(day.precipitation, language)}
{day.precipitationProbability === null ? "—" : `${day.precipitationProbability}%`}

{formatForecastTemperature(day.temperatureMax, language)}{formatForecastTemperature(day.temperatureMin, language)}

); } export function ForecastPanel({ latitude, longitude, locationName }: { latitude?: number; longitude?: number; locationName: string }) { const { language, t } = useI18n(); const { data: forecast, isPending, isError, refetch } = useForecast(latitude, longitude); const [selectedDay, setSelectedDay] = useState(null); const closeDayDetails = useCallback(() => setSelectedDay(null), []); const upcomingHours = forecast ? getUpcomingHourlyForecast(forecast.hourly) : []; const todayHours = forecast?.daily[0] ? getHourlyForecastForDay(forecast.hourly, forecast.daily[0].date) : []; const selectedDayHours = forecast && selectedDay ? getHourlyForecastForDay(forecast.hourly, selectedDay.date) : []; return (

{t("forecast.label")}

{t("forecast.title")}

{t("forecast.description", { location: locationName })}

{!Number.isFinite(latitude) || !Number.isFinite(longitude) || isPending ? (
) : isError || !forecast ? (

{t("forecast.error")}

) : !forecast.hourly.length || !forecast.daily.length ? ( ) : (

{t("forecast.hourly")}

    {upcomingHours.map((hour, index) => (

    {formatHour(hour.time)}

    {formatForecastTemperature(hour.temperature, language)}

    {hour.precipitationProbability === null ? "—" : `${hour.precipitationProbability}%`}

    {formatForecastTemperature(hour.feelsLike, language)}

    {formatForecastWind(hour.windSpeed, language)}

    {formatForecastRainfall(hour.precipitation, language)}

    ))}

{t("forecast.daily")}

    {forecast.daily.map((day, index) => )}
)}

{t("forecast.source")} Open-Meteo

); }