"use client"; import { useCallback, useState } from "react"; import { motion } from "framer-motion"; import { CalendarDays, ChevronRight, Clock3, CloudRain, CloudSun, Droplets, 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 { ForecastSources } from "@/components/forecast/forecast-sources"; 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, getDailyForecastForHour, getEffectiveHourlyWeatherCode, getForecastCondition, getHourlyForecastForDay, getUpcomingHourlyForecast, isForecastNight, } from "@/lib/forecast-utils"; import { useWeatherStore } from "@/lib/store"; import type { DailyForecast, HourlyForecast } from "@/types/forecast"; import type { WeatherRegion } from "@/types/weather-region"; 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 temperatureUnit = useWeatherStore((state) => state.temperatureUnit); const windSpeedUnit = useWeatherStore((state) => state.windSpeedUnit); const precipitationUnit = useWeatherStore((state) => state.precipitationUnit); 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, temperatureUnit)} / ${formatForecastTemperature(maximumTemperature, language, temperatureUnit)}`; return (

{t("forecast.nextHoursOverview")}

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

{label}

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

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

); } export function ForecastPanel({ latitude, longitude, region = "PL", locationName, }: { latitude?: number; longitude?: number; region?: WeatherRegion; locationName: string; }) { const { language, t } = useI18n(); const temperatureUnit = useWeatherStore((state) => state.temperatureUnit); const windSpeedUnit = useWeatherStore((state) => state.windSpeedUnit); const precipitationUnit = useWeatherStore((state) => state.precipitationUnit); const { data: forecast, isPending, isError, refetch } = useForecast(latitude, longitude, region); 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) => { const day = getDailyForecastForHour(forecast.daily, hour.time); const weatherCode = getEffectiveHourlyWeatherCode(hour); return (

    {formatHour(hour.time)}

    {formatForecastTemperature(hour.temperature, language, temperatureUnit)}

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

    {formatForecastTemperature(hour.feelsLike, language, temperatureUnit)}

    {formatForecastWind(hour.windSpeed, language, windSpeedUnit)}

    {formatForecastRainfall(hour.precipitation, language, precipitationUnit)}

    ); })}

{t("forecast.daily")}

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