feat: add weather unit preferences
Some checks failed
CI / Lint, test, typecheck and build (push) Has been cancelled

This commit is contained in:
zv
2026-07-04 20:16:11 +02:00
parent ab6b7b414f
commit 91acdb39b8
27 changed files with 623 additions and 76 deletions

View File

@@ -5,9 +5,15 @@ 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 { formatForecastTemperature } from "@/lib/forecast-utils";
import { useWeatherStore } from "@/lib/store";
import { convertTemperature, formatTemperatureValue, getTemperatureUnitLabel } from "@/lib/weather-utils";
import {
convertPrecipitation,
convertTemperature,
formatTemperatureValue,
getPrecipitationUnitLabel,
getTemperatureUnitLabel,
} from "@/lib/weather-utils";
import type { HourlyForecast } from "@/types/forecast";
const INITIAL_CHART_DIMENSION = { width: 1, height: 1 };
@@ -22,6 +28,15 @@ function formatHour(value: string) {
return value.slice(11, 16);
}
function formatChartPrecipitation(value: number | null, language: "pl" | "en", unit: string) {
if (value === null) return "—";
const formattedValue = new Intl.NumberFormat(language === "pl" ? "pl-PL" : "en-GB", {
minimumFractionDigits: unit === "in" ? 2 : 0,
maximumFractionDigits: unit === "mm" ? (value < 1 ? 2 : 1) : 2,
}).format(value);
return `${formattedValue} ${unit}`;
}
function ChartLegend({ items }: { items: ChartLegendItem[] }) {
return (
<div className="mt-2 flex flex-wrap items-center gap-x-4 gap-y-2 text-[0.72rem] font-medium text-muted">
@@ -46,14 +61,16 @@ function ChartLegend({ items }: { items: ChartLegendItem[] }) {
export function DayForecastCharts({ hours }: { hours: HourlyForecast[] }) {
const { language, t } = useI18n();
const temperatureUnit = useWeatherStore((state) => state.temperatureUnit);
const precipitationUnit = useWeatherStore((state) => state.precipitationUnit);
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,
precipitation: hour.precipitation === null ? null : convertPrecipitation(hour.precipitation, precipitationUnit),
precipitationProbability: hour.precipitationProbability,
}));
const temperatureUnitLabel = getTemperatureUnitLabel(temperatureUnit);
const precipitationUnitLabel = getPrecipitationUnitLabel(precipitationUnit);
const temperatureLegendItems: ChartLegendItem[] = [
{ color: CHART_COLORS.temperature, label: t("forecast.temperature"), marker: "line" },
{ color: CHART_COLORS.feelsLike, label: t("forecast.apparentTemperature"), marker: "dashed-line" },
@@ -153,7 +170,7 @@ export function DayForecastCharts({ hours }: { hours: HourlyForecast[] }) {
axisLine={false}
tickLine={false}
tick={{ fill: "currentColor", fontSize: 11 }}
unit=" mm"
unit={` ${precipitationUnitLabel}`}
/>
<YAxis
yAxisId="probability"
@@ -170,7 +187,11 @@ export function DayForecastCharts({ hours }: { hours: HourlyForecast[] }) {
<ChartTooltip
valueFormatter={(entry) =>
entry.dataKey === "precipitation"
? formatForecastRainfall(typeof entry.value === "number" ? entry.value : null, language)
? formatChartPrecipitation(
typeof entry.value === "number" ? entry.value : null,
language,
precipitationUnitLabel,
)
: `${typeof entry.value === "number" ? entry.value : "—"}%`
}
/>