From 72c8ed2b9575b46f6c8fea0d357a705f9c08df61 Mon Sep 17 00:00:00 2001 From: Nemenwq Date: Sat, 28 Mar 2026 16:22:56 +0100 Subject: [PATCH 1/2] Build theme settings page (dark/light/system) with persistence --- .../dashboard/settings/preferences/page.tsx | 59 +++++++++- src/components/ClientProviders.tsx | 13 ++- src/components/ThemeToggle.tsx | 29 +++-- src/contexts/ThemeProvider.tsx | 101 ++++++++++++++++++ 4 files changed, 180 insertions(+), 22 deletions(-) create mode 100644 src/contexts/ThemeProvider.tsx diff --git a/src/app/dashboard/settings/preferences/page.tsx b/src/app/dashboard/settings/preferences/page.tsx index eba20dc..c9c2e3b 100644 --- a/src/app/dashboard/settings/preferences/page.tsx +++ b/src/app/dashboard/settings/preferences/page.tsx @@ -1,15 +1,17 @@ "use client"; import { useState, useEffect } from "react"; -import { Globe, Clock, DollarSign, Save, X, AlertCircle, CheckCircle2 } from "lucide-react"; +import { Globe, Clock, DollarSign, Save, X, AlertCircle, CheckCircle2, Sun, Moon, Monitor } from "lucide-react"; import { Button } from "@/components/ui/Button"; import { mockAudit } from "@/lib/mock-audit"; import { SettingsSectionSkeleton } from "@/components/ui/Skeleton"; +import { useTheme, ThemeMode } from "@/contexts/ThemeProvider"; interface PreferencesData { locale: string; timezone: string; currencyFormat: string; + theme: ThemeMode; } const LOCALES = [ @@ -46,6 +48,7 @@ const DEFAULT: PreferencesData = { locale: "en-US", timezone: "UTC", currencyFormat: "USD", + theme: "system", }; export default function PreferencesPage() { @@ -162,6 +165,60 @@ export default function PreferencesPage() { +
+
+
+ +
+
+

Appearance

+

Theme and visual display preferences

+
+
+ +
+
+ + {editing ? ( +
+ + + +
+ ) : ( +

+ {saved.theme === "light" && "Light"} + {saved.theme === "dark" && "Dark"} + {saved.theme === "system" && "System"} +

+ )} +
+
+
+
diff --git a/src/components/ClientProviders.tsx b/src/components/ClientProviders.tsx index 9eb7c8a..b91a84c 100644 --- a/src/components/ClientProviders.tsx +++ b/src/components/ClientProviders.tsx @@ -4,13 +4,16 @@ import { ReactNode } from "react"; import { AuthProvider } from "@/contexts"; import { WalletProvider } from "@/contexts"; import { I18nProvider } from "@/contexts/I18nContext"; +import { ThemeProvider } from "@/contexts/ThemeProvider"; export function ClientProviders({ children }: { children: ReactNode }) { return ( - - - {children} - - + + + + {children} + + + ); } diff --git a/src/components/ThemeToggle.tsx b/src/components/ThemeToggle.tsx index 6ecd6c9..e23edd5 100644 --- a/src/components/ThemeToggle.tsx +++ b/src/components/ThemeToggle.tsx @@ -1,32 +1,29 @@ "use client"; -import { useEffect, useState } from "react"; +import { useTheme } from "@/contexts/ThemeProvider"; export function ThemeToggle() { - const [isDark, setIsDark] = useState(true); - - useEffect(() => { - const stored = localStorage.getItem("nw-theme"); - const dark = stored !== "light"; - setIsDark(dark); - document.documentElement.classList.toggle("dark", dark); - }, []); + const { theme, resolvedTheme, setTheme } = useTheme(); function toggle() { - const next = !isDark; - setIsDark(next); - document.documentElement.classList.toggle("dark", next); - localStorage.setItem("nw-theme", next ? "dark" : "light"); + if (theme === "light") { + setTheme("dark"); + } else if (theme === "dark") { + setTheme("light"); + } else { + // If system, toggle to opposite of current resolved theme + setTheme(resolvedTheme === "light" ? "dark" : "light"); + } } return (