diff --git a/src/app/api/settings/website-theme/route.ts b/src/app/api/settings/website-theme/route.ts
new file mode 100644
index 0000000..4996f7d
--- /dev/null
+++ b/src/app/api/settings/website-theme/route.ts
@@ -0,0 +1,41 @@
+import { NextRequest, NextResponse } from "next/server"
+import { getSessionUser } from "@/lib/auth"
+import { query } from "@/lib/db"
+
+export async function GET() {
+ try {
+ const user = await getSessionUser()
+ if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
+
+ const result = await query(
+ `SELECT preferences->>'website_theme' AS website_theme FROM users WHERE id = $1`,
+ [user.id],
+ )
+
+ const websiteTheme = result.rows[0]?.website_theme || "spidey"
+ return NextResponse.json({ websiteTheme })
+ } catch (error) {
+ console.error("Website theme GET error:", error)
+ return NextResponse.json({ error: "Failed to load website theme" }, { status: 500 })
+ }
+}
+
+export async function PUT(request: NextRequest) {
+ try {
+ const user = await getSessionUser()
+ if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
+
+ const body = await request.json()
+ const theme = body.websiteTheme || "spidey"
+
+ await query(
+ `UPDATE users SET preferences = preferences || $2::jsonb WHERE id = $1`,
+ [user.id, JSON.stringify({ website_theme: theme })],
+ )
+
+ return NextResponse.json({ success: true, websiteTheme: theme })
+ } catch (error) {
+ console.error("Website theme PUT error:", error)
+ return NextResponse.json({ error: "Failed to save website theme" }, { status: 500 })
+ }
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 08e274e..a64d357 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,6 +1,7 @@
import type { Metadata, Viewport } from "next"
import { Inter } from "next/font/google"
import { ThemeProvider } from "@/providers/theme-provider"
+import { WebsiteThemeProvider } from "@/providers/website-theme-provider"
import { Toaster } from "@/components/ui/sonner"
import "./globals.css"
@@ -31,8 +32,10 @@ export default function RootLayout({
- {children}
-
+
+ {children}
+
+
diff --git a/src/components/settings/theme-settings.tsx b/src/components/settings/theme-settings.tsx
index 3879277..22ceb70 100644
--- a/src/components/settings/theme-settings.tsx
+++ b/src/components/settings/theme-settings.tsx
@@ -6,7 +6,8 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { cn } from "@/lib/utils"
-import { Sun, Moon, Monitor } from "lucide-react"
+import { useWebsiteTheme } from "@/providers/website-theme-provider"
+import { Sun, Moon, Monitor, Check } from "lucide-react"
const COLOR_THEME_KEY = "crm-color-theme"
@@ -44,8 +45,17 @@ function applyColorTheme(theme: string) {
localStorage.setItem(COLOR_THEME_KEY, theme)
}
+const websiteThemes = [
+ {
+ id: "spidey",
+ name: "Spidey",
+ description: "Current website theme",
+ },
+]
+
export function ThemeSettings() {
const { theme, setTheme } = useTheme()
+ const { websiteTheme, setWebsiteTheme } = useWebsiteTheme()
const [colorTheme, setColorTheme] = useState("default")
const [mounted, setMounted] = useState(false)
@@ -119,6 +129,71 @@ export function ThemeSettings() {
+
+
+
+ Website Themes
+
+ Select your website's visual theme.
+
+
+
+
+ {websiteThemes.map((wt) => {
+ const isActive = websiteTheme === wt.id
+ return (
+
+ )
+ })}
+
+
+
)
}
diff --git a/src/providers/website-theme-provider.tsx b/src/providers/website-theme-provider.tsx
new file mode 100644
index 0000000..f35f7c8
--- /dev/null
+++ b/src/providers/website-theme-provider.tsx
@@ -0,0 +1,92 @@
+"use client"
+
+import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from "react"
+
+const WEBSITE_THEME_KEY = "crm-website-theme"
+
+const themeClasses: Record = {
+ spidey: "",
+}
+
+interface WebsiteThemeContextValue {
+ websiteTheme: string
+ setWebsiteTheme: (theme: string) => Promise
+ loading: boolean
+}
+
+const WebsiteThemeContext = createContext(null)
+
+function applyWebsiteTheme(theme: string) {
+ const prefix = "theme-"
+ const root = document.documentElement
+ const classes = root.className.split(" ").filter((c) => !c.startsWith(prefix))
+ const cls = themeClasses[theme]
+ if (cls) {
+ classes.push(cls)
+ }
+ root.className = classes.join(" ").trim()
+}
+
+function getStoredTheme(): string | null {
+ if (typeof window === "undefined") return null
+ return localStorage.getItem(WEBSITE_THEME_KEY)
+}
+
+function storeTheme(theme: string) {
+ if (typeof window === "undefined") return
+ localStorage.setItem(WEBSITE_THEME_KEY, theme)
+}
+
+export function WebsiteThemeProvider({ children }: { children: ReactNode }) {
+ const [websiteTheme, setWebsiteThemeState] = useState("spidey")
+ const [loading, setLoading] = useState(true)
+
+ useEffect(() => {
+ const stored = getStoredTheme()
+ if (stored) {
+ setWebsiteThemeState(stored)
+ applyWebsiteTheme(stored)
+ setLoading(false)
+ return
+ }
+ fetch("/api/settings/website-theme")
+ .then((res) => (res.ok ? res.json() : null))
+ .then((data) => {
+ const theme = data?.websiteTheme || "spidey"
+ setWebsiteThemeState(theme)
+ storeTheme(theme)
+ applyWebsiteTheme(theme)
+ })
+ .catch(() => {
+ applyWebsiteTheme("spidey")
+ })
+ .finally(() => setLoading(false))
+ }, [])
+
+ const setWebsiteTheme = useCallback(async (theme: string) => {
+ setWebsiteThemeState(theme)
+ storeTheme(theme)
+ applyWebsiteTheme(theme)
+ try {
+ await fetch("/api/settings/website-theme", {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ websiteTheme: theme }),
+ })
+ } catch {
+ console.warn("Failed to persist website theme")
+ }
+ }, [])
+
+ return (
+
+ {children}
+
+ )
+}
+
+export function useWebsiteTheme() {
+ const ctx = useContext(WebsiteThemeContext)
+ if (!ctx) throw new Error("useWebsiteTheme must be used within WebsiteThemeProvider")
+ return ctx
+}