feat: allow dashboard section reordering
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
ArrowDown,
|
||||
ArrowUp,
|
||||
Bell,
|
||||
BellRing,
|
||||
ChevronDown,
|
||||
@@ -27,7 +29,11 @@ import { useMeteoStationPositions } from "@/hooks/use-meteo-stations";
|
||||
import { useWeatherStations } from "@/hooks/use-weather-stations";
|
||||
import { APP_SECTION_SETTINGS } from "@/lib/app-sections";
|
||||
import { DEFAULT_STATION_ID } from "@/lib/constants";
|
||||
import { DASHBOARD_SECTION_SETTINGS } from "@/lib/dashboard-sections";
|
||||
import {
|
||||
DASHBOARD_SECTION_SETTINGS,
|
||||
normalizeDashboardSectionOrder,
|
||||
normalizeDashboardSectionVisibility,
|
||||
} from "@/lib/dashboard-sections";
|
||||
import { useI18n } from "@/lib/i18n";
|
||||
import { locateSynopStations } from "@/lib/location-utils";
|
||||
import {
|
||||
@@ -230,7 +236,8 @@ export function SettingsPage() {
|
||||
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 dashboardSections = normalizeDashboardSectionVisibility(useWeatherStore((state) => state.dashboardSections));
|
||||
const dashboardSectionOrder = normalizeDashboardSectionOrder(useWeatherStore((state) => state.dashboardSectionOrder));
|
||||
const appSections = useWeatherStore((state) => state.appSections);
|
||||
const setNotificationsEnabled = useWeatherStore((state) => state.setWarningNotificationsEnabled);
|
||||
const setMorningBriefEnabled = useWeatherStore((state) => state.setMorningBriefNotificationsEnabled);
|
||||
@@ -243,6 +250,7 @@ export function SettingsPage() {
|
||||
const setPressureUnit = useWeatherStore((state) => state.setPressureUnit);
|
||||
const setDistanceUnit = useWeatherStore((state) => state.setDistanceUnit);
|
||||
const setDashboardSectionVisible = useWeatherStore((state) => state.setDashboardSectionVisible);
|
||||
const moveDashboardSection = useWeatherStore((state) => state.moveDashboardSection);
|
||||
const setAppSectionVisible = useWeatherStore((state) => state.setAppSectionVisible);
|
||||
|
||||
const selectedStation =
|
||||
@@ -277,6 +285,9 @@ export function SettingsPage() {
|
||||
? formatProvinceName(effectiveProvince, language)
|
||||
: t("settings.notifications.noProvince");
|
||||
const manualProvinceValue = manualProvince ?? selectedProvince ?? "mazowieckie";
|
||||
const orderedDashboardSections = dashboardSectionOrder
|
||||
.map((sectionId) => DASHBOARD_SECTION_SETTINGS.find((section) => section.id === sectionId))
|
||||
.filter((section): section is (typeof DASHBOARD_SECTION_SETTINGS)[number] => Boolean(section));
|
||||
|
||||
useEffect(() => {
|
||||
const abortController = new AbortController();
|
||||
@@ -714,17 +725,58 @@ export function SettingsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-2">
|
||||
{DASHBOARD_SECTION_SETTINGS.map((section) => (
|
||||
<NotificationSwitchLabel
|
||||
key={section.id}
|
||||
icon={LayoutDashboard}
|
||||
title={t(section.titleKey)}
|
||||
description={t(section.descriptionKey)}
|
||||
checked={dashboardSections[section.id]}
|
||||
onChange={(checked) => setDashboardSectionVisible(section.id, checked)}
|
||||
/>
|
||||
))}
|
||||
<div className="mt-4 space-y-2">
|
||||
{orderedDashboardSections.map((section, index) => {
|
||||
const sectionTitle = t(section.titleKey);
|
||||
const checked = dashboardSections[section.id];
|
||||
return (
|
||||
<div
|
||||
key={section.id}
|
||||
className={`rounded-card border px-3 py-3 transition-colors ${
|
||||
checked ? "border-accent/35 bg-accent/5" : "border-border/60 bg-surface"
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<label className="flex min-w-0 flex-1 cursor-pointer items-center justify-between gap-4">
|
||||
<span className="min-w-0">
|
||||
<span className="block text-sm font-semibold">{sectionTitle}</span>
|
||||
<span className="mt-1 block text-sm leading-6 text-muted">{t(section.descriptionKey)}</span>
|
||||
</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checked}
|
||||
onChange={(event) => setDashboardSectionVisible(section.id, event.target.checked)}
|
||||
className="sr-only"
|
||||
/>
|
||||
<SwitchIndicator checked={checked} />
|
||||
</label>
|
||||
|
||||
<div className="flex items-center gap-2 self-end sm:self-center" aria-label={sectionTitle}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="icon"
|
||||
className="size-9"
|
||||
disabled={index === 0}
|
||||
aria-label={t("settings.dashboardSections.moveUp", { section: sectionTitle })}
|
||||
onClick={() => moveDashboardSection(section.id, -1)}
|
||||
>
|
||||
<ArrowUp className="size-4" aria-hidden="true" />
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="icon"
|
||||
className="size-9"
|
||||
disabled={index === orderedDashboardSections.length - 1}
|
||||
aria-label={t("settings.dashboardSections.moveDown", { section: sectionTitle })}
|
||||
onClick={() => moveDashboardSection(section.id, 1)}
|
||||
>
|
||||
<ArrowDown className="size-4" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user