feat: add weather unit preferences
Some checks failed
CI / Lint, test, typecheck and build (push) Has been cancelled
Some checks failed
CI / Lint, test, typecheck and build (push) Has been cancelled
This commit is contained in:
@@ -6,10 +6,13 @@ import {
|
||||
BellRing,
|
||||
ChevronDown,
|
||||
Clock3,
|
||||
CloudRain,
|
||||
Gauge,
|
||||
Languages,
|
||||
LayoutDashboard,
|
||||
MapPinned,
|
||||
Palette,
|
||||
Ruler,
|
||||
Settings,
|
||||
Smartphone,
|
||||
Thermometer,
|
||||
@@ -36,8 +39,14 @@ import {
|
||||
} from "@/lib/notification-api";
|
||||
import { formatProvinceName, getProvinceForSelection, PROVINCES } from "@/lib/provinces";
|
||||
import { useWeatherStore } from "@/lib/store";
|
||||
import { TEMPERATURE_UNITS, WIND_SPEED_UNITS } from "@/lib/weather-utils";
|
||||
import type { TemperatureUnit, WindSpeedUnit } from "@/types/units";
|
||||
import {
|
||||
DISTANCE_UNITS,
|
||||
PRECIPITATION_UNITS,
|
||||
PRESSURE_UNITS,
|
||||
TEMPERATURE_UNITS,
|
||||
WIND_SPEED_UNITS,
|
||||
} from "@/lib/weather-utils";
|
||||
import type { DistanceUnit, PrecipitationUnit, PressureUnit, TemperatureUnit, WindSpeedUnit } from "@/types/units";
|
||||
|
||||
type NotificationSupportStatus =
|
||||
| "checking"
|
||||
@@ -154,11 +163,17 @@ function NotificationSwitchLabel({
|
||||
|
||||
const windSpeedUnitKeys: Record<
|
||||
WindSpeedUnit,
|
||||
"settings.units.wind.ms" | "settings.units.wind.kmh" | "settings.units.wind.mph"
|
||||
| "settings.units.wind.ms"
|
||||
| "settings.units.wind.kmh"
|
||||
| "settings.units.wind.mph"
|
||||
| "settings.units.wind.kt"
|
||||
| "settings.units.wind.bft"
|
||||
> = {
|
||||
ms: "settings.units.wind.ms",
|
||||
kmh: "settings.units.wind.kmh",
|
||||
mph: "settings.units.wind.mph",
|
||||
kt: "settings.units.wind.kt",
|
||||
bft: "settings.units.wind.bft",
|
||||
};
|
||||
|
||||
const temperatureUnitKeys: Record<TemperatureUnit, "settings.units.temperature.c" | "settings.units.temperature.f"> = {
|
||||
@@ -166,6 +181,33 @@ const temperatureUnitKeys: Record<TemperatureUnit, "settings.units.temperature.c
|
||||
f: "settings.units.temperature.f",
|
||||
};
|
||||
|
||||
const precipitationUnitKeys: Record<
|
||||
PrecipitationUnit,
|
||||
"settings.units.precipitation.mm" | "settings.units.precipitation.cm" | "settings.units.precipitation.in"
|
||||
> = {
|
||||
mm: "settings.units.precipitation.mm",
|
||||
cm: "settings.units.precipitation.cm",
|
||||
in: "settings.units.precipitation.in",
|
||||
};
|
||||
|
||||
const pressureUnitKeys: Record<
|
||||
PressureUnit,
|
||||
| "settings.units.pressure.hpa"
|
||||
| "settings.units.pressure.kpa"
|
||||
| "settings.units.pressure.inhg"
|
||||
| "settings.units.pressure.mmhg"
|
||||
> = {
|
||||
hpa: "settings.units.pressure.hpa",
|
||||
kpa: "settings.units.pressure.kpa",
|
||||
inhg: "settings.units.pressure.inhg",
|
||||
mmhg: "settings.units.pressure.mmhg",
|
||||
};
|
||||
|
||||
const distanceUnitKeys: Record<DistanceUnit, "settings.units.distance.km" | "settings.units.distance.mi"> = {
|
||||
km: "settings.units.distance.km",
|
||||
mi: "settings.units.distance.mi",
|
||||
};
|
||||
|
||||
export function SettingsPage() {
|
||||
const { language, t } = useI18n();
|
||||
const [notificationStatus, setNotificationStatus] = useState<NotificationSupportStatus>("checking");
|
||||
@@ -185,6 +227,9 @@ export function SettingsPage() {
|
||||
const manualProvince = useWeatherStore((state) => state.warningNotificationProvince);
|
||||
const temperatureUnit = useWeatherStore((state) => state.temperatureUnit);
|
||||
const windSpeedUnit = useWeatherStore((state) => state.windSpeedUnit);
|
||||
const precipitationUnit = useWeatherStore((state) => state.precipitationUnit);
|
||||
const pressureUnit = useWeatherStore((state) => state.pressureUnit);
|
||||
const distanceUnit = useWeatherStore((state) => state.distanceUnit);
|
||||
const dashboardSections = useWeatherStore((state) => state.dashboardSections);
|
||||
const appSections = useWeatherStore((state) => state.appSections);
|
||||
const setNotificationsEnabled = useWeatherStore((state) => state.setWarningNotificationsEnabled);
|
||||
@@ -194,6 +239,9 @@ export function SettingsPage() {
|
||||
const setManualProvince = useWeatherStore((state) => state.setWarningNotificationProvince);
|
||||
const setTemperatureUnit = useWeatherStore((state) => state.setTemperatureUnit);
|
||||
const setWindSpeedUnit = useWeatherStore((state) => state.setWindSpeedUnit);
|
||||
const setPrecipitationUnit = useWeatherStore((state) => state.setPrecipitationUnit);
|
||||
const setPressureUnit = useWeatherStore((state) => state.setPressureUnit);
|
||||
const setDistanceUnit = useWeatherStore((state) => state.setDistanceUnit);
|
||||
const setDashboardSectionVisible = useWeatherStore((state) => state.setDashboardSectionVisible);
|
||||
const setAppSectionVisible = useWeatherStore((state) => state.setAppSectionVisible);
|
||||
|
||||
@@ -268,6 +316,9 @@ export function SettingsPage() {
|
||||
countyTeryt: notificationCountyTeryt,
|
||||
temperatureUnit,
|
||||
windSpeedUnit,
|
||||
precipitationUnit,
|
||||
pressureUnit,
|
||||
distanceUnit,
|
||||
}).catch(() => undefined);
|
||||
}
|
||||
|
||||
@@ -289,6 +340,9 @@ export function SettingsPage() {
|
||||
notificationTimezone,
|
||||
selectedRegion,
|
||||
temperatureUnit,
|
||||
precipitationUnit,
|
||||
pressureUnit,
|
||||
distanceUnit,
|
||||
tomorrowBriefEnabled,
|
||||
vapidPublicKey,
|
||||
windSpeedUnit,
|
||||
@@ -376,6 +430,9 @@ export function SettingsPage() {
|
||||
countyTeryt: notificationCountyTeryt,
|
||||
temperatureUnit,
|
||||
windSpeedUnit,
|
||||
precipitationUnit,
|
||||
pressureUnit,
|
||||
distanceUnit,
|
||||
});
|
||||
setNotificationsEnabled(true);
|
||||
setNotificationMessage(t("settings.notifications.saveSuccess"));
|
||||
@@ -444,6 +501,9 @@ export function SettingsPage() {
|
||||
countyTeryt: notificationCountyTeryt,
|
||||
temperatureUnit,
|
||||
windSpeedUnit,
|
||||
precipitationUnit,
|
||||
pressureUnit,
|
||||
distanceUnit,
|
||||
});
|
||||
} catch {
|
||||
setNotificationMessage(t("settings.notifications.saveError"));
|
||||
@@ -470,6 +530,9 @@ export function SettingsPage() {
|
||||
countyTeryt: notificationCountyTeryt,
|
||||
temperatureUnit,
|
||||
windSpeedUnit,
|
||||
precipitationUnit,
|
||||
pressureUnit,
|
||||
distanceUnit,
|
||||
});
|
||||
} catch {
|
||||
setNotificationMessage(t("settings.notifications.saveError"));
|
||||
@@ -554,6 +617,90 @@ export function SettingsPage() {
|
||||
))}
|
||||
</div>
|
||||
</SettingsRow>
|
||||
<SettingsRow
|
||||
icon={CloudRain}
|
||||
title={t("settings.units.precipitation.title")}
|
||||
description={t("settings.units.precipitation.description")}
|
||||
>
|
||||
<div
|
||||
className="surface-control inline-flex rounded-full p-1"
|
||||
role="radiogroup"
|
||||
aria-label={t("settings.units.precipitation.label")}
|
||||
>
|
||||
{PRECIPITATION_UNITS.map((unit) => (
|
||||
<button
|
||||
key={unit}
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked={precipitationUnit === unit}
|
||||
className={`min-w-14 rounded-full border px-3.5 py-1.5 text-sm font-medium transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent ${
|
||||
precipitationUnit === unit
|
||||
? "border-accent/45 bg-accent/10 text-accent shadow-soft"
|
||||
: "border-transparent text-muted hover:bg-surface-muted/70 hover:text-foreground"
|
||||
}`}
|
||||
onClick={() => setPrecipitationUnit(unit)}
|
||||
>
|
||||
{t(precipitationUnitKeys[unit])}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</SettingsRow>
|
||||
<SettingsRow
|
||||
icon={Gauge}
|
||||
title={t("settings.units.pressure.title")}
|
||||
description={t("settings.units.pressure.description")}
|
||||
>
|
||||
<div
|
||||
className="surface-control inline-flex rounded-full p-1"
|
||||
role="radiogroup"
|
||||
aria-label={t("settings.units.pressure.label")}
|
||||
>
|
||||
{PRESSURE_UNITS.map((unit) => (
|
||||
<button
|
||||
key={unit}
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked={pressureUnit === unit}
|
||||
className={`min-w-14 rounded-full border px-3.5 py-1.5 text-sm font-medium transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent ${
|
||||
pressureUnit === unit
|
||||
? "border-accent/45 bg-accent/10 text-accent shadow-soft"
|
||||
: "border-transparent text-muted hover:bg-surface-muted/70 hover:text-foreground"
|
||||
}`}
|
||||
onClick={() => setPressureUnit(unit)}
|
||||
>
|
||||
{t(pressureUnitKeys[unit])}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</SettingsRow>
|
||||
<SettingsRow
|
||||
icon={Ruler}
|
||||
title={t("settings.units.distance.title")}
|
||||
description={t("settings.units.distance.description")}
|
||||
>
|
||||
<div
|
||||
className="surface-control inline-flex rounded-full p-1"
|
||||
role="radiogroup"
|
||||
aria-label={t("settings.units.distance.label")}
|
||||
>
|
||||
{DISTANCE_UNITS.map((unit) => (
|
||||
<button
|
||||
key={unit}
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked={distanceUnit === unit}
|
||||
className={`min-w-14 rounded-full border px-3.5 py-1.5 text-sm font-medium transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent ${
|
||||
distanceUnit === unit
|
||||
? "border-accent/45 bg-accent/10 text-accent shadow-soft"
|
||||
: "border-transparent text-muted hover:bg-surface-muted/70 hover:text-foreground"
|
||||
}`}
|
||||
onClick={() => setDistanceUnit(unit)}
|
||||
>
|
||||
{t(distanceUnitKeys[unit])}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</SettingsRow>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4 sm:p-5">
|
||||
|
||||
Reference in New Issue
Block a user