feat: add app section visibility settings
All checks were successful
CI / Lint, typecheck and build (push) Successful in 9m54s
All checks were successful
CI / Lint, typecheck and build (push) Successful in 9m54s
This commit is contained in:
@@ -3,16 +3,27 @@
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { CloudSun, Droplets, Settings, TriangleAlert } from "lucide-react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import { isAppNavItemVisible } from "@/lib/app-sections";
|
||||
import { NAV_ITEMS } from "@/lib/constants";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { InstallPWAButton } from "@/components/ui/install-pwa-button";
|
||||
import { useI18n } from "@/lib/i18n";
|
||||
import { useWeatherStore } from "@/lib/store";
|
||||
|
||||
const icons = [CloudSun, TriangleAlert, Droplets, Settings];
|
||||
const iconsByHref: Record<string, LucideIcon> = {
|
||||
"/": CloudSun,
|
||||
"/warnings": TriangleAlert,
|
||||
"/hydro": Droplets,
|
||||
"/settings": Settings,
|
||||
};
|
||||
|
||||
export function AppShell({ children }: { children: React.ReactNode }) {
|
||||
const pathname = usePathname();
|
||||
const { t } = useI18n();
|
||||
const appSections = useWeatherStore((state) => state.appSections);
|
||||
const visibleNavItems = NAV_ITEMS.filter((item) => isAppNavItemVisible(item.href, appSections));
|
||||
|
||||
return (
|
||||
<div className="min-h-screen overflow-x-hidden bg-background">
|
||||
<header className="app-header-safe sticky top-0 z-40 border-b border-border/70 bg-surface">
|
||||
@@ -21,7 +32,7 @@ export function AppShell({ children }: { children: React.ReactNode }) {
|
||||
wtr<span className="text-accent">.</span>
|
||||
</Link>
|
||||
<nav aria-label={t("nav.main")} className="hidden items-center gap-1 md:flex">
|
||||
{NAV_ITEMS.map((item) => {
|
||||
{visibleNavItems.map((item) => {
|
||||
const active = item.href === "/" ? pathname === "/" : pathname.startsWith(item.href);
|
||||
return (
|
||||
<Link key={item.href} href={item.href} className={cn("rounded-control px-4 py-2 text-sm font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent", active ? "bg-foreground text-background shadow-soft" : "text-muted hover:bg-surface-muted/70")}>
|
||||
@@ -37,8 +48,8 @@ export function AppShell({ children }: { children: React.ReactNode }) {
|
||||
</header>
|
||||
<main className="app-main-safe mx-auto max-w-7xl px-4 pt-5 sm:px-6 sm:pt-8 lg:px-8">{children}</main>
|
||||
<nav aria-label={t("nav.mobile")} className="mobile-nav-safe fixed z-50 flex justify-around rounded-panel border border-border/70 bg-surface p-1.5 shadow-card md:hidden">
|
||||
{NAV_ITEMS.map((item, index) => {
|
||||
const Icon = icons[index];
|
||||
{visibleNavItems.map((item) => {
|
||||
const Icon = iconsByHref[item.href] ?? CloudSun;
|
||||
const active = item.href === "/" ? pathname === "/" : pathname.startsWith(item.href);
|
||||
return (
|
||||
<Link key={item.href} href={item.href} className={cn("flex min-w-0 flex-1 flex-col items-center gap-1 rounded-card px-2 py-2 text-[0.68rem] font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent", active ? "bg-foreground text-background" : "text-muted")}>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { LanguageToggle } from "@/components/ui/language-toggle";
|
||||
import { ThemeToggle } from "@/components/ui/theme-toggle";
|
||||
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 { useI18n } from "@/lib/i18n";
|
||||
@@ -133,6 +134,7 @@ export function SettingsPage() {
|
||||
const temperatureUnit = useWeatherStore((state) => state.temperatureUnit);
|
||||
const windSpeedUnit = useWeatherStore((state) => state.windSpeedUnit);
|
||||
const dashboardSections = useWeatherStore((state) => state.dashboardSections);
|
||||
const appSections = useWeatherStore((state) => state.appSections);
|
||||
const setNotificationsEnabled = useWeatherStore((state) => state.setWarningNotificationsEnabled);
|
||||
const setMorningBriefEnabled = useWeatherStore((state) => state.setMorningBriefNotificationsEnabled);
|
||||
const setTomorrowBriefEnabled = useWeatherStore((state) => state.setTomorrowBriefNotificationsEnabled);
|
||||
@@ -141,6 +143,7 @@ export function SettingsPage() {
|
||||
const setTemperatureUnit = useWeatherStore((state) => state.setTemperatureUnit);
|
||||
const setWindSpeedUnit = useWeatherStore((state) => state.setWindSpeedUnit);
|
||||
const setDashboardSectionVisible = useWeatherStore((state) => state.setDashboardSectionVisible);
|
||||
const setAppSectionVisible = useWeatherStore((state) => state.setAppSectionVisible);
|
||||
|
||||
const selectedStation = stations?.find((station) => station.id === selectedStationId)
|
||||
?? stations?.find((station) => station.id === DEFAULT_STATION_ID)
|
||||
@@ -463,6 +466,31 @@ export function SettingsPage() {
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4 sm:p-5">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="rounded-control border border-border/70 bg-surface-muted/70 p-2 text-accent">
|
||||
<Settings className="size-4" aria-hidden="true" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-sm font-semibold">{t("settings.appSections.title")}</h2>
|
||||
<p className="mt-1 max-w-2xl text-sm leading-6 text-muted">{t("settings.appSections.description")}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-2">
|
||||
{APP_SECTION_SETTINGS.map((section) => (
|
||||
<NotificationSwitchLabel
|
||||
key={section.id}
|
||||
icon={Settings}
|
||||
title={t(section.titleKey)}
|
||||
description={t(section.descriptionKey)}
|
||||
checked={appSections[section.id]}
|
||||
onChange={(checked) => setAppSectionVisible(section.id, checked)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="p-4 sm:p-5">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="rounded-control border border-warning/30 bg-warning/10 p-2 text-warning">
|
||||
|
||||
Reference in New Issue
Block a user