feat: show current-day forecast charts on dashboard
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { CalendarDays, ChevronRight, Clock3, CloudSun, Droplets, ExternalLink, RefreshCw } 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";
|
||||
@@ -65,6 +66,7 @@ export function ForecastPanel({ latitude, longitude, locationName }: { latitude?
|
||||
const [selectedDay, setSelectedDay] = useState<DailyForecast | null>(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 (
|
||||
@@ -88,33 +90,36 @@ export function ForecastPanel({ latitude, longitude, locationName }: { latitude?
|
||||
) : !forecast.hourly.length || !forecast.daily.length ? (
|
||||
<EmptyState title={t("forecast.emptyTitle")} description={t("forecast.emptyDescription")} />
|
||||
) : (
|
||||
<div className="grid items-start gap-3 lg:grid-cols-[1.35fr_1fr]">
|
||||
<Card className="overflow-hidden p-4 sm:p-5">
|
||||
<h3 className="flex items-center gap-2 text-sm font-semibold"><Clock3 className="size-4 text-sky-600 dark:text-sky-300" />{t("forecast.hourly")}</h3>
|
||||
<div className="weather-scrollbar -mx-4 mt-4 overflow-x-auto px-4 pb-2 sm:-mx-5 sm:px-5">
|
||||
<ul className="flex min-w-max gap-2">
|
||||
{upcomingHours.map((hour, index) => (
|
||||
<motion.li
|
||||
key={hour.time}
|
||||
initial={{ opacity: 0, y: 8 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: Math.min(index * 0.018, 0.3), duration: 0.25 }}
|
||||
className="w-[4.6rem] rounded-2xl border border-white/35 bg-white/25 px-2 py-3 text-center dark:border-white/10 dark:bg-white/5"
|
||||
title={getForecastCondition(hour.weatherCode, language)}
|
||||
>
|
||||
<p className="text-xs font-medium text-slate-600 dark:text-slate-300">{formatHour(hour.time)}</p>
|
||||
<ForecastIcon code={hour.weatherCode} className="mx-auto my-3 size-6 text-sky-600 dark:text-sky-300" />
|
||||
<p className="text-lg font-semibold tracking-tight">{formatForecastTemperature(hour.temperature, language)}</p>
|
||||
<p className="mt-2 flex items-center justify-center gap-1 text-[0.66rem] text-sky-700 dark:text-sky-300"><Droplets className="size-3" />{hour.precipitationProbability === null ? "—" : `${hour.precipitationProbability}%`}</p>
|
||||
</motion.li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="p-4 sm:p-5">
|
||||
<h3 className="flex items-center gap-2 text-sm font-semibold"><CalendarDays className="size-4 text-sky-600 dark:text-sky-300" />{t("forecast.daily")}</h3>
|
||||
<ul className="mt-2">{forecast.daily.map((day, index) => <DailyForecastRow day={day} index={index} key={day.date} onSelect={setSelectedDay} />)}</ul>
|
||||
</Card>
|
||||
<div className="space-y-3">
|
||||
<div className="grid items-start gap-3 lg:grid-cols-[1.35fr_1fr]">
|
||||
<Card className="overflow-hidden p-4 sm:p-5">
|
||||
<h3 className="flex items-center gap-2 text-sm font-semibold"><Clock3 className="size-4 text-sky-600 dark:text-sky-300" />{t("forecast.hourly")}</h3>
|
||||
<div className="weather-scrollbar -mx-4 mt-4 overflow-x-auto px-4 pb-2 sm:-mx-5 sm:px-5">
|
||||
<ul className="flex min-w-max gap-2">
|
||||
{upcomingHours.map((hour, index) => (
|
||||
<motion.li
|
||||
key={hour.time}
|
||||
initial={{ opacity: 0, y: 8 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: Math.min(index * 0.018, 0.3), duration: 0.25 }}
|
||||
className="w-[4.6rem] rounded-2xl border border-white/35 bg-white/25 px-2 py-3 text-center dark:border-white/10 dark:bg-white/5"
|
||||
title={getForecastCondition(hour.weatherCode, language)}
|
||||
>
|
||||
<p className="text-xs font-medium text-slate-600 dark:text-slate-300">{formatHour(hour.time)}</p>
|
||||
<ForecastIcon code={hour.weatherCode} className="mx-auto my-3 size-6 text-sky-600 dark:text-sky-300" />
|
||||
<p className="text-lg font-semibold tracking-tight">{formatForecastTemperature(hour.temperature, language)}</p>
|
||||
<p className="mt-2 flex items-center justify-center gap-1 text-[0.66rem] text-sky-700 dark:text-sky-300"><Droplets className="size-3" />{hour.precipitationProbability === null ? "—" : `${hour.precipitationProbability}%`}</p>
|
||||
</motion.li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="p-4 sm:p-5">
|
||||
<h3 className="flex items-center gap-2 text-sm font-semibold"><CalendarDays className="size-4 text-sky-600 dark:text-sky-300" />{t("forecast.daily")}</h3>
|
||||
<ul className="mt-2">{forecast.daily.map((day, index) => <DailyForecastRow day={day} index={index} key={day.date} onSelect={setSelectedDay} />)}</ul>
|
||||
</Card>
|
||||
</div>
|
||||
<DayForecastCharts hours={todayHours} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user