Files
wtr/components/charts/day-forecast-charts.tsx
2026-06-14 21:00:05 +02:00

169 lines
6.4 KiB
TypeScript

"use client";
import { Bar, CartesianGrid, ComposedChart, Legend, Line, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
import { Card } from "@/components/ui/card";
import { ChartTooltip } from "@/components/charts/chart-tooltip";
import { CHART_COLORS } from "@/lib/chart-theme";
import { useI18n } from "@/lib/i18n";
import { formatForecastRainfall, formatForecastTemperature } from "@/lib/forecast-utils";
import { useWeatherStore } from "@/lib/store";
import { convertTemperature, formatTemperatureValue, getTemperatureUnitLabel } from "@/lib/weather-utils";
import type { HourlyForecast } from "@/types/forecast";
const INITIAL_CHART_DIMENSION = { width: 1, height: 1 };
function formatHour(value: string) {
return value.slice(11, 16);
}
export function DayForecastCharts({ hours }: { hours: HourlyForecast[] }) {
const { language, t } = useI18n();
const temperatureUnit = useWeatherStore((state) => state.temperatureUnit);
const rows = hours.map((hour) => ({
time: formatHour(hour.time),
temperature: hour.temperature === null ? null : convertTemperature(hour.temperature, temperatureUnit),
feelsLike: hour.feelsLike === null ? null : convertTemperature(hour.feelsLike, temperatureUnit),
precipitation: hour.precipitation,
precipitationProbability: hour.precipitationProbability,
}));
const temperatureUnitLabel = getTemperatureUnitLabel(temperatureUnit);
return (
<div className="grid gap-3 lg:grid-cols-2">
<Card className="p-4 sm:p-5">
<h3 className="text-sm font-semibold">{t("forecast.temperatureChart")}</h3>
<p className="mt-1 text-xs leading-5 text-muted">{t("forecast.temperatureChartDescription")}</p>
<div className="mt-4 h-56 min-w-0">
<ResponsiveContainer
width="100%"
height="100%"
minWidth={0}
minHeight={0}
initialDimension={INITIAL_CHART_DIMENSION}
>
<ComposedChart data={rows} margin={{ left: -20, right: 8, top: 8 }}>
<CartesianGrid stroke={CHART_COLORS.grid} strokeDasharray="4 4" vertical={false} />
<XAxis
dataKey="time"
axisLine={false}
tickLine={false}
interval={3}
tick={{ fill: "currentColor", fontSize: 11 }}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fill: "currentColor", fontSize: 11 }}
unit={temperatureUnitLabel}
/>
<Tooltip
cursor={{ stroke: CHART_COLORS.grid, strokeDasharray: "4 4" }}
content={
<ChartTooltip
valueFormatter={(entry) =>
typeof entry.value === "number"
? formatTemperatureValue(entry.value, language, temperatureUnit)
: formatForecastTemperature(null, language, temperatureUnit)
}
/>
}
/>
<Legend wrapperStyle={{ fontSize: "0.72rem" }} />
<Line
type="monotone"
dataKey="temperature"
name={t("forecast.temperature")}
stroke={CHART_COLORS.temperature}
strokeWidth={3}
dot={false}
connectNulls
/>
<Line
type="monotone"
dataKey="feelsLike"
name={t("forecast.apparentTemperature")}
stroke={CHART_COLORS.feelsLike}
strokeWidth={2}
strokeDasharray="5 4"
dot={false}
connectNulls
/>
</ComposedChart>
</ResponsiveContainer>
</div>
</Card>
<Card className="p-4 sm:p-5">
<h3 className="text-sm font-semibold">{t("forecast.rainfallChart")}</h3>
<p className="mt-1 text-xs leading-5 text-muted">{t("forecast.rainfallChartDescription")}</p>
<div className="mt-4 h-56 min-w-0">
<ResponsiveContainer
width="100%"
height="100%"
minWidth={0}
minHeight={0}
initialDimension={INITIAL_CHART_DIMENSION}
>
<ComposedChart data={rows} margin={{ left: -20, right: -10, top: 8 }}>
<CartesianGrid stroke={CHART_COLORS.grid} strokeDasharray="4 4" vertical={false} />
<XAxis
dataKey="time"
axisLine={false}
tickLine={false}
interval={3}
tick={{ fill: "currentColor", fontSize: 11 }}
/>
<YAxis
yAxisId="rainfall"
axisLine={false}
tickLine={false}
tick={{ fill: "currentColor", fontSize: 11 }}
unit=" mm"
/>
<YAxis
yAxisId="probability"
orientation="right"
domain={[0, 100]}
axisLine={false}
tickLine={false}
tick={{ fill: "currentColor", fontSize: 11 }}
unit="%"
/>
<Tooltip
cursor={{ fill: "hsl(var(--border) / 0.18)" }}
content={
<ChartTooltip
valueFormatter={(entry) =>
entry.dataKey === "precipitation"
? formatForecastRainfall(typeof entry.value === "number" ? entry.value : null, language)
: `${typeof entry.value === "number" ? entry.value : "—"}%`
}
/>
}
/>
<Legend wrapperStyle={{ fontSize: "0.72rem" }} />
<Bar
yAxisId="rainfall"
dataKey="precipitation"
name={t("forecast.precipitation")}
fill={CHART_COLORS.rainfall}
radius={[5, 5, 0, 0]}
/>
<Line
yAxisId="probability"
type="monotone"
dataKey="precipitationProbability"
name={t("forecast.precipitationProbability")}
stroke={CHART_COLORS.probability}
strokeWidth={2}
dot={false}
connectNulls
/>
</ComposedChart>
</ResponsiveContainer>
</div>
</Card>
</div>
);
}