Add settings page for alert preferences

This commit is contained in:
zv
2026-06-11 18:39:01 +02:00
parent 68fd967f88
commit 531e678922
9 changed files with 344 additions and 10 deletions

View File

@@ -10,4 +10,5 @@ export const NAV_ITEMS = [
{ href: "/", labelKey: "nav.weather" },
{ href: "/warnings", labelKey: "nav.warnings" },
{ href: "/hydro", labelKey: "nav.hydro" },
{ href: "/settings", labelKey: "nav.settings" },
] as const;

View File

@@ -10,6 +10,7 @@ const translations = {
"nav.weather": "Pogoda",
"nav.warnings": "Ostrzeżenia",
"nav.hydro": "Hydro",
"nav.settings": "Ustawienia",
"nav.main": "Główna nawigacja",
"nav.mobile": "Mobilna nawigacja",
"language.label": "Wybierz język",
@@ -18,6 +19,31 @@ const translations = {
"theme.change": "Zmień motyw",
"theme.light": "Włącz jasny motyw",
"theme.dark": "Włącz ciemny motyw",
"settings.section": "Preferencje",
"settings.title": "Ustawienia",
"settings.description": "Zarządzaj językiem, wyglądem i przygotowaniem powiadomień o ostrzeżeniach meteorologicznych IMGW.",
"settings.language.title": "Język",
"settings.language.description": "Zmieniaj język interfejsu. Nazwy stacji i treści IMGW pozostają bez automatycznego tłumaczenia.",
"settings.theme.title": "Motyw",
"settings.theme.description": "Przełącz jasny lub ciemny wygląd aplikacji na tym urządzeniu.",
"settings.notifications.title": "Powiadomienia o ostrzeżeniach meteo",
"settings.notifications.description": "Przygotuj alerty dla nowych ostrzeżeń meteorologicznych IMGW, takich jak burze, upał, silny wiatr lub intensywny deszcz.",
"settings.notifications.regionTitle": "Obszar alertów",
"settings.notifications.regionDescription": "Aktualnie wybrany obszar dla przyszłych powiadomień: {province}.",
"settings.notifications.regionSelected": "Używaj lokalizacji wybranej w aplikacji",
"settings.notifications.regionManual": "Wybierz województwo ręcznie",
"settings.notifications.noProvince": "brak wybranego województwa",
"settings.notifications.deviceTitle": "To urządzenie",
"settings.notifications.enable": "Alerty IMGW",
"settings.notifications.enableDescription": "Preferencja zostanie użyta po dodaniu backendowej subskrypcji Web Push.",
"settings.notifications.requestPermission": "Sprawdź zgodę",
"settings.notifications.statusChecking": "Sprawdzam obsługę powiadomień w tej przeglądarce.",
"settings.notifications.statusUnsupported": "Ta przeglądarka nie udostępnia Web Push dla tej aplikacji.",
"settings.notifications.statusNeedsInstall": "Na iPhonie dodaj aplikację do ekranu początkowego przez Udostępnij → Dodaj do ekranu początkowego, a potem otwórz ją z ikony.",
"settings.notifications.statusDenied": "Powiadomienia są zablokowane w ustawieniach systemu lub przeglądarki.",
"settings.notifications.statusGranted": "To urządzenie ma zgodę na powiadomienia.",
"settings.notifications.statusReady": "Możesz poprosić system o zgodę na powiadomienia.",
"settings.notifications.implementationNote": "To pierwszy etap konfiguracji. Wysyłka po nowych ostrzeżeniach IMGW wymaga jeszcze zapisania subskrypcji Web Push i cyklicznego sprawdzania API IMGW po stronie serwera.",
"pwa.install": "Zainstaluj",
"common.noData": "Brak danych",
"common.loading": "Ładowanie danych",
@@ -196,6 +222,7 @@ const translations = {
"nav.weather": "Weather",
"nav.warnings": "Warnings",
"nav.hydro": "Hydro",
"nav.settings": "Settings",
"nav.main": "Main navigation",
"nav.mobile": "Mobile navigation",
"language.label": "Select language",
@@ -204,6 +231,31 @@ const translations = {
"theme.change": "Change theme",
"theme.light": "Enable light theme",
"theme.dark": "Enable dark theme",
"settings.section": "Preferences",
"settings.title": "Settings",
"settings.description": "Manage language, appearance and preparation for IMGW meteorological warning notifications.",
"settings.language.title": "Language",
"settings.language.description": "Change the interface language. Station names and IMGW content are not translated automatically.",
"settings.theme.title": "Theme",
"settings.theme.description": "Switch the light or dark appearance on this device.",
"settings.notifications.title": "Weather warning notifications",
"settings.notifications.description": "Prepare alerts for new IMGW meteorological warnings, such as thunderstorms, heat, strong wind or heavy rain.",
"settings.notifications.regionTitle": "Alert area",
"settings.notifications.regionDescription": "Current area for future notifications: {province}.",
"settings.notifications.regionSelected": "Use the location selected in the app",
"settings.notifications.regionManual": "Choose province manually",
"settings.notifications.noProvince": "no province selected",
"settings.notifications.deviceTitle": "This device",
"settings.notifications.enable": "IMGW alerts",
"settings.notifications.enableDescription": "This preference will be used after the Web Push backend subscription is added.",
"settings.notifications.requestPermission": "Check permission",
"settings.notifications.statusChecking": "Checking notification support in this browser.",
"settings.notifications.statusUnsupported": "This browser does not provide Web Push for this app.",
"settings.notifications.statusNeedsInstall": "On iPhone, add the app to the Home Screen using Share → Add to Home Screen, then open it from the icon.",
"settings.notifications.statusDenied": "Notifications are blocked in system or browser settings.",
"settings.notifications.statusGranted": "This device has notification permission.",
"settings.notifications.statusReady": "You can ask the system for notification permission.",
"settings.notifications.implementationNote": "This is the first configuration stage. Sending alerts for new IMGW warnings still requires saving a Web Push subscription and polling the IMGW API on the server.",
"pwa.install": "Install",
"common.noData": "No data",
"common.loading": "Loading data",

View File

@@ -103,6 +103,8 @@ const provinceLabels: Record<Province, Record<Language, string>> = {
"zachodniopomorskie": { pl: "zachodniopomorskie", en: "West Pomeranian" },
};
export const PROVINCES = Object.keys(provinceLabels) as Province[];
function simplifyProvinceName(value: string) {
return value
.normalize("NFD")

View File

@@ -3,14 +3,23 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import type { SelectedLocation } from "@/types/location";
import type { Province } from "@/types/province";
type NotificationProvinceMode = "selected" | "manual";
interface WeatherStore {
favorites: string[];
selectedStationId: string | null;
selectedLocation: SelectedLocation | null;
warningNotificationsEnabled: boolean;
warningNotificationProvinceMode: NotificationProvinceMode;
warningNotificationProvince: Province | null;
toggleFavorite: (id: string) => void;
selectStation: (id: string) => void;
selectLocation: (location: SelectedLocation) => void;
setWarningNotificationsEnabled: (enabled: boolean) => void;
setWarningNotificationProvinceMode: (mode: NotificationProvinceMode) => void;
setWarningNotificationProvince: (province: Province | null) => void;
}
export const useWeatherStore = create<WeatherStore>()(
@@ -19,6 +28,9 @@ export const useWeatherStore = create<WeatherStore>()(
favorites: [],
selectedStationId: null,
selectedLocation: null,
warningNotificationsEnabled: false,
warningNotificationProvinceMode: "selected",
warningNotificationProvince: null,
toggleFavorite: (id) =>
set((state) => ({
favorites: state.favorites.includes(id)
@@ -27,6 +39,9 @@ export const useWeatherStore = create<WeatherStore>()(
})),
selectStation: (id) => set({ selectedStationId: id, selectedLocation: null }),
selectLocation: (location) => set({ selectedStationId: location.stationId, selectedLocation: location }),
setWarningNotificationsEnabled: (enabled) => set({ warningNotificationsEnabled: enabled }),
setWarningNotificationProvinceMode: (mode) => set({ warningNotificationProvinceMode: mode }),
setWarningNotificationProvince: (province) => set({ warningNotificationProvince: province }),
}),
{ name: "wtr:preferences" },
),