feat: add configurable wind units

This commit is contained in:
zv
2026-06-13 13:36:36 +02:00
parent a8d4d1e23c
commit 7ad95550eb
21 changed files with 152 additions and 38 deletions

View File

@@ -15,6 +15,7 @@ import {
formatForecastWind,
getForecastCondition,
} from "@/lib/forecast-utils";
import { useWeatherStore } from "@/lib/store";
import type { DailyForecast, ForecastSource, HourlyForecast } from "@/types/forecast";
function formatHour(value: string | null) {
@@ -53,6 +54,7 @@ export function DayForecastModal({
onClose: () => void;
}) {
const { language, locale, t } = useI18n();
const windSpeedUnit = useWeatherStore((state) => state.windSpeedUnit);
const closeButtonRef = useRef<HTMLButtonElement>(null);
const portalRoot = typeof document === "undefined" ? null : document.body;
const maximumWind = useMemo(() => getMaximumWind(hours), [hours]);
@@ -135,7 +137,7 @@ export function DayForecastModal({
</div>
<div className="grid grid-cols-2 gap-2 sm:min-w-[22rem]">
<DayMetric icon={Droplets} label={t("forecast.precipitation")} value={formatForecastRainfall(day.precipitation, language)} />
<DayMetric icon={Wind} label={t("forecast.maxWind")} value={formatForecastWind(maximumWind, language)} />
<DayMetric icon={Wind} label={t("forecast.maxWind")} value={formatForecastWind(maximumWind, language, windSpeedUnit)} />
<DayMetric icon={Sunrise} label={t("forecast.sunrise")} value={formatHour(day.sunrise)} />
<DayMetric icon={Sunset} label={t("forecast.sunset")} value={formatHour(day.sunset)} />
</div>

View File

@@ -21,6 +21,7 @@ import {
getHourlyForecastForDay,
getUpcomingHourlyForecast,
} from "@/lib/forecast-utils";
import { useWeatherStore } from "@/lib/store";
import type { DailyForecast, HourlyForecast } from "@/types/forecast";
function formatHour(value: string) {
@@ -59,6 +60,7 @@ function HourlySummaryMetric({ icon: Icon, label, value }: { icon: LucideIcon; l
function HourlyForecastSummary({ hours }: { hours: HourlyForecast[] }) {
const { language, t } = useI18n();
const windSpeedUnit = useWeatherStore((state) => state.windSpeedUnit);
const minimumTemperature = getMinimum(hours.map((hour) => hour.temperature));
const maximumTemperature = getMaximum(hours.map((hour) => hour.temperature));
const maximumWind = getMaximum(hours.map((hour) => hour.windSpeed));
@@ -73,7 +75,7 @@ function HourlyForecastSummary({ hours }: { hours: HourlyForecast[] }) {
<p className="text-[0.68rem] font-semibold uppercase tracking-[0.14em] text-muted">{t("forecast.nextHoursOverview")}</p>
<div className="mt-3 grid grid-cols-4 gap-2">
<HourlySummaryMetric icon={ThermometerSun} label={t("forecast.temperatureRange")} value={temperatureRange} />
<HourlySummaryMetric icon={Wind} label={t("forecast.maxWind")} value={formatForecastWind(maximumWind, language)} />
<HourlySummaryMetric icon={Wind} label={t("forecast.maxWind")} value={formatForecastWind(maximumWind, language, windSpeedUnit)} />
<HourlySummaryMetric icon={CloudRain} label={t("forecast.rainfallTotal")} value={formatForecastRainfall(rainfallTotal, language)} />
<HourlySummaryMetric icon={Droplets} label={t("forecast.maxProbability")} value={maximumRainfallProbability === null ? "—" : `${maximumRainfallProbability}%`} />
</div>
@@ -113,6 +115,7 @@ function DailyForecastRow({ day, index, onSelect }: { day: DailyForecast; index:
export function ForecastPanel({ latitude, longitude, locationName }: { latitude?: number; longitude?: number; locationName: string }) {
const { language, t } = useI18n();
const windSpeedUnit = useWeatherStore((state) => state.windSpeedUnit);
const { data: forecast, isPending, isError, refetch } = useForecast(latitude, longitude);
const [selectedDay, setSelectedDay] = useState<DailyForecast | null>(null);
const closeDayDetails = useCallback(() => setSelectedDay(null), []);
@@ -167,7 +170,7 @@ export function ForecastPanel({ latitude, longitude, locationName }: { latitude?
</p>
<p className="flex items-center justify-center gap-1" title={t("weather.wind")}>
<Wind className="size-3 text-accent" />
{formatForecastWind(hour.windSpeed, language)}
{formatForecastWind(hour.windSpeed, language, windSpeedUnit)}
</p>
<p className="flex items-center justify-center gap-1" title={t("forecast.precipitation")}>
<CloudRain className="size-3 text-accent" />