feat: allow dashboard section reordering
This commit is contained in:
@@ -1,12 +1,30 @@
|
||||
import type { TranslationKey } from "@/lib/i18n";
|
||||
|
||||
export const DASHBOARD_SECTION_SETTINGS = [
|
||||
{
|
||||
id: "warnings",
|
||||
titleKey: "settings.dashboardSections.warnings.title",
|
||||
descriptionKey: "settings.dashboardSections.warnings.description",
|
||||
defaultVisible: true,
|
||||
},
|
||||
{
|
||||
id: "weatherBrief",
|
||||
titleKey: "settings.dashboardSections.weatherBrief.title",
|
||||
descriptionKey: "settings.dashboardSections.weatherBrief.description",
|
||||
defaultVisible: true,
|
||||
},
|
||||
{
|
||||
id: "forecast",
|
||||
titleKey: "settings.dashboardSections.forecast.title",
|
||||
descriptionKey: "settings.dashboardSections.forecast.description",
|
||||
defaultVisible: true,
|
||||
},
|
||||
{
|
||||
id: "favorites",
|
||||
titleKey: "settings.dashboardSections.favorites.title",
|
||||
descriptionKey: "settings.dashboardSections.favorites.description",
|
||||
defaultVisible: true,
|
||||
},
|
||||
{
|
||||
id: "featuredStations",
|
||||
titleKey: "settings.dashboardSections.featuredStations.title",
|
||||
@@ -23,6 +41,11 @@ export const DASHBOARD_SECTION_SETTINGS = [
|
||||
export type DashboardSectionId = (typeof DASHBOARD_SECTION_SETTINGS)[number]["id"];
|
||||
|
||||
export type DashboardSectionVisibility = Record<DashboardSectionId, boolean>;
|
||||
export type DashboardSectionOrder = DashboardSectionId[];
|
||||
|
||||
export const DEFAULT_DASHBOARD_SECTION_ORDER = DASHBOARD_SECTION_SETTINGS.map(
|
||||
(section) => section.id,
|
||||
) as DashboardSectionOrder;
|
||||
|
||||
export const DEFAULT_DASHBOARD_SECTION_VISIBILITY = DASHBOARD_SECTION_SETTINGS.reduce(
|
||||
(visibility, section) => ({
|
||||
@@ -42,3 +65,14 @@ export function normalizeDashboardSectionVisibility(value: unknown): DashboardSe
|
||||
{} as DashboardSectionVisibility,
|
||||
);
|
||||
}
|
||||
|
||||
export function normalizeDashboardSectionOrder(value: unknown): DashboardSectionOrder {
|
||||
const allowedIds = new Set<DashboardSectionId>(DASHBOARD_SECTION_SETTINGS.map((section) => section.id));
|
||||
const stored = Array.isArray(value)
|
||||
? value.filter((sectionId): sectionId is DashboardSectionId => allowedIds.has(sectionId as DashboardSectionId))
|
||||
: [];
|
||||
return [
|
||||
...stored,
|
||||
...DEFAULT_DASHBOARD_SECTION_ORDER.filter((sectionId) => !stored.includes(sectionId)),
|
||||
] as DashboardSectionOrder;
|
||||
}
|
||||
|
||||
23
lib/i18n.tsx
23
lib/i18n.tsx
@@ -28,10 +28,20 @@ const translations = {
|
||||
"settings.theme.title": "Motyw",
|
||||
"settings.theme.description": "Przełącz jasny lub ciemny wygląd aplikacji na tym urządzeniu.",
|
||||
"settings.dashboardSections.title": "Strona główna",
|
||||
"settings.dashboardSections.description": "Wybierz, które dodatkowe sekcje mają być widoczne na dashboardzie.",
|
||||
"settings.dashboardSections.description": "Wybierz widoczność i kolejność sekcji pod główną kartą pogody.",
|
||||
"settings.dashboardSections.moveUp": "Przenieś sekcję {section} wyżej",
|
||||
"settings.dashboardSections.moveDown": "Przenieś sekcję {section} niżej",
|
||||
"settings.dashboardSections.warnings.title": "Ostrzeżenia",
|
||||
"settings.dashboardSections.warnings.description":
|
||||
"Pokazuje aktywne i nadchodzące ostrzeżenia dla wybranej lokalizacji. Na stronie głównej sekcja pojawia się tylko wtedy, gdy jest aktywne ostrzeżenie meteo.",
|
||||
"settings.dashboardSections.weatherBrief.title": "Brief dnia",
|
||||
"settings.dashboardSections.weatherBrief.description":
|
||||
"Pokazuje deterministyczne podsumowanie dnia i krótką prognozę na jutro.",
|
||||
"settings.dashboardSections.forecast.title": "Prognoza 7-dniowa",
|
||||
"settings.dashboardSections.forecast.description":
|
||||
"Pokazuje najbliższe godziny, prognozę tygodniową i wykresy dnia.",
|
||||
"settings.dashboardSections.favorites.title": "Ulubione",
|
||||
"settings.dashboardSections.favorites.description": "Pokazuje zapisane lokalizacje i stacje na stronie głównej.",
|
||||
"settings.dashboardSections.featuredStations.title": "Szybki wybór",
|
||||
"settings.dashboardSections.featuredStations.description":
|
||||
"Pokazuje listę popularnych lokalizacji na dole strony głównej.",
|
||||
@@ -340,10 +350,19 @@ const translations = {
|
||||
"settings.theme.title": "Theme",
|
||||
"settings.theme.description": "Switch the light or dark appearance on this device.",
|
||||
"settings.dashboardSections.title": "Home screen",
|
||||
"settings.dashboardSections.description": "Choose which additional sections are visible on the dashboard.",
|
||||
"settings.dashboardSections.description": "Choose visibility and order for sections below the main weather card.",
|
||||
"settings.dashboardSections.moveUp": "Move {section} up",
|
||||
"settings.dashboardSections.moveDown": "Move {section} down",
|
||||
"settings.dashboardSections.warnings.title": "Warnings",
|
||||
"settings.dashboardSections.warnings.description":
|
||||
"Shows active and upcoming warnings for the selected location. On the home screen, this section appears only when an active weather warning exists.",
|
||||
"settings.dashboardSections.weatherBrief.title": "Daily brief",
|
||||
"settings.dashboardSections.weatherBrief.description":
|
||||
"Shows the deterministic daily summary and a short forecast for tomorrow.",
|
||||
"settings.dashboardSections.forecast.title": "7-day forecast",
|
||||
"settings.dashboardSections.forecast.description": "Shows the next hours, weekly forecast and daily charts.",
|
||||
"settings.dashboardSections.favorites.title": "Favorites",
|
||||
"settings.dashboardSections.favorites.description": "Shows saved places and stations on the home screen.",
|
||||
"settings.dashboardSections.featuredStations.title": "Quick select",
|
||||
"settings.dashboardSections.featuredStations.description":
|
||||
"Shows the popular locations list at the bottom of the home screen.",
|
||||
|
||||
20
lib/store.ts
20
lib/store.ts
@@ -12,9 +12,12 @@ import {
|
||||
type AppSectionVisibility,
|
||||
} from "@/lib/app-sections";
|
||||
import {
|
||||
DEFAULT_DASHBOARD_SECTION_ORDER,
|
||||
DEFAULT_DASHBOARD_SECTION_VISIBILITY,
|
||||
normalizeDashboardSectionOrder,
|
||||
normalizeDashboardSectionVisibility,
|
||||
type DashboardSectionId,
|
||||
type DashboardSectionOrder,
|
||||
type DashboardSectionVisibility,
|
||||
} from "@/lib/dashboard-sections";
|
||||
import {
|
||||
@@ -49,6 +52,7 @@ interface WeatherStore {
|
||||
pressureUnit: PressureUnit;
|
||||
distanceUnit: DistanceUnit;
|
||||
dashboardSections: DashboardSectionVisibility;
|
||||
dashboardSectionOrder: DashboardSectionOrder;
|
||||
appSections: AppSectionVisibility;
|
||||
toggleFavorite: (id: string) => void;
|
||||
selectStation: (id: string) => void;
|
||||
@@ -64,6 +68,7 @@ interface WeatherStore {
|
||||
setPressureUnit: (unit: PressureUnit) => void;
|
||||
setDistanceUnit: (unit: DistanceUnit) => void;
|
||||
setDashboardSectionVisible: (section: DashboardSectionId, visible: boolean) => void;
|
||||
moveDashboardSection: (section: DashboardSectionId, direction: -1 | 1) => void;
|
||||
setAppSectionVisible: (section: AppSectionId, visible: boolean) => void;
|
||||
}
|
||||
|
||||
@@ -84,6 +89,7 @@ export const useWeatherStore = create<WeatherStore>()(
|
||||
pressureUnit: DEFAULT_PRESSURE_UNIT,
|
||||
distanceUnit: DEFAULT_DISTANCE_UNIT,
|
||||
dashboardSections: DEFAULT_DASHBOARD_SECTION_VISIBILITY,
|
||||
dashboardSectionOrder: DEFAULT_DASHBOARD_SECTION_ORDER,
|
||||
appSections: DEFAULT_APP_SECTION_VISIBILITY,
|
||||
toggleFavorite: (id) =>
|
||||
set((state) => ({
|
||||
@@ -107,6 +113,16 @@ export const useWeatherStore = create<WeatherStore>()(
|
||||
set((state) => ({
|
||||
dashboardSections: { ...state.dashboardSections, [section]: visible },
|
||||
})),
|
||||
moveDashboardSection: (section, direction) =>
|
||||
set((state) => {
|
||||
const order = normalizeDashboardSectionOrder(state.dashboardSectionOrder);
|
||||
const index = order.indexOf(section);
|
||||
const nextIndex = index + direction;
|
||||
if (index === -1 || nextIndex < 0 || nextIndex >= order.length) return state;
|
||||
const nextOrder = [...order];
|
||||
[nextOrder[index], nextOrder[nextIndex]] = [nextOrder[nextIndex], nextOrder[index]];
|
||||
return { dashboardSectionOrder: nextOrder };
|
||||
}),
|
||||
setAppSectionVisible: (section, visible) =>
|
||||
set((state) => ({
|
||||
appSections: { ...state.appSections, [section]: visible },
|
||||
@@ -122,6 +138,7 @@ export const useWeatherStore = create<WeatherStore>()(
|
||||
| undefined;
|
||||
if (!state) return persisted;
|
||||
const dashboardSections = normalizeDashboardSectionVisibility(state.dashboardSections);
|
||||
const dashboardSectionOrder = normalizeDashboardSectionOrder(state.dashboardSectionOrder);
|
||||
const appSections = normalizeAppSectionVisibility(state.appSections);
|
||||
const unitPreferences = {
|
||||
temperatureUnit: isTemperatureUnit(state.temperatureUnit) ? state.temperatureUnit : DEFAULT_TEMPERATURE_UNIT,
|
||||
@@ -132,13 +149,14 @@ export const useWeatherStore = create<WeatherStore>()(
|
||||
pressureUnit: isPressureUnit(state.pressureUnit) ? state.pressureUnit : DEFAULT_PRESSURE_UNIT,
|
||||
distanceUnit: isDistanceUnit(state.distanceUnit) ? state.distanceUnit : DEFAULT_DISTANCE_UNIT,
|
||||
};
|
||||
if (!location) return { ...state, ...unitPreferences, dashboardSections, appSections };
|
||||
if (!location) return { ...state, ...unitPreferences, dashboardSections, dashboardSectionOrder, appSections };
|
||||
const countryCode = location.countryCode ?? (location.province ? "PL" : null);
|
||||
const region = location.region ?? getWeatherRegionForCountryCode(countryCode);
|
||||
return {
|
||||
...state,
|
||||
...unitPreferences,
|
||||
dashboardSections,
|
||||
dashboardSectionOrder,
|
||||
appSections,
|
||||
selectedLocation: {
|
||||
...location,
|
||||
|
||||
Reference in New Issue
Block a user