mirror of
https://git.coastit.co.za/caitlin/CRM_ENVR.git
synced 2026-07-10 03:05:43 +02:00
200 lines
8.1 KiB
TypeScript
200 lines
8.1 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import { useTheme } from "next-themes"
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { Label } from "@/components/ui/label"
|
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
|
import { cn } from "@/lib/utils"
|
|
import { useWebsiteTheme } from "@/providers/website-theme-provider"
|
|
import { Sun, Moon, Monitor, Check } from "lucide-react"
|
|
|
|
const COLOR_THEME_KEY = "crm-color-theme"
|
|
|
|
const modeOptions = [
|
|
{ value: "light", icon: Sun, label: "Light" },
|
|
{ value: "dark", icon: Moon, label: "Dark" },
|
|
{ value: "system", icon: Monitor, label: "System" },
|
|
]
|
|
|
|
const themeOptions = [
|
|
{ value: "default", label: "Default", color: "bg-blue-600", ring: "ring-blue-600" },
|
|
{ value: "ocean", label: "Ocean", color: "bg-teal-500", ring: "ring-teal-500" },
|
|
{ value: "forest", label: "Forest", color: "bg-green-600", ring: "ring-green-600" },
|
|
{ value: "sunset", label: "Sunset", color: "bg-orange-500", ring: "ring-orange-500" },
|
|
{ value: "midnight", label: "Midnight", color: "bg-indigo-600", ring: "ring-indigo-600" },
|
|
{ value: "rose", label: "Rose", color: "bg-pink-600", ring: "ring-pink-600" },
|
|
{ value: "amber", label: "Amber", color: "bg-amber-500", ring: "ring-amber-500" },
|
|
{ value: "violet", label: "Violet", color: "bg-violet-600", ring: "ring-violet-600" },
|
|
{ value: "slate", label: "Slate", color: "bg-slate-500", ring: "ring-slate-500" },
|
|
{ value: "ruby", label: "Ruby", color: "bg-red-600", ring: "ring-red-600" },
|
|
]
|
|
|
|
function getStoredColorTheme(): string {
|
|
if (typeof window === "undefined") return "default"
|
|
return localStorage.getItem(COLOR_THEME_KEY) || "default"
|
|
}
|
|
|
|
function applyColorTheme(theme: string) {
|
|
document.documentElement.className = document.documentElement.className
|
|
.replace(/\b(default|ocean|forest|sunset|midnight|rose|amber|violet|slate|ruby)\b/g, "")
|
|
.trim()
|
|
if (theme !== "default") {
|
|
document.documentElement.classList.add(theme)
|
|
}
|
|
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)
|
|
|
|
useEffect(() => {
|
|
setMounted(true)
|
|
setColorTheme(getStoredColorTheme())
|
|
applyColorTheme(getStoredColorTheme())
|
|
}, [])
|
|
|
|
function handleColorChange(value: string) {
|
|
setColorTheme(value)
|
|
applyColorTheme(value)
|
|
}
|
|
|
|
if (!mounted) return null
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Theme Mode</CardTitle>
|
|
<CardDescription>
|
|
Choose your preferred appearance mode.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<RadioGroup value={theme || "dark"} onValueChange={setTheme} className="grid grid-cols-3 gap-4">
|
|
{modeOptions.map(({ value, icon: Icon, label }) => (
|
|
<div key={value}>
|
|
<RadioGroupItem value={value} id={`mode-${value}`} className="peer sr-only" />
|
|
<Label
|
|
htmlFor={`mode-${value}`}
|
|
className={cn(
|
|
"flex flex-col items-center gap-3 rounded-lg border-2 p-4 hover:bg-accent cursor-pointer transition-all",
|
|
"peer-data-[state=checked]:border-primary peer-data-[state=checked]:bg-primary/5"
|
|
)}
|
|
>
|
|
<Icon className="h-6 w-6" />
|
|
<span className="text-sm font-medium">{label}</span>
|
|
</Label>
|
|
</div>
|
|
))}
|
|
</RadioGroup>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Color Theme</CardTitle>
|
|
<CardDescription>
|
|
Select a color accent for the dashboard.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<RadioGroup value={colorTheme} onValueChange={handleColorChange} className="grid grid-cols-5 gap-4">
|
|
{themeOptions.map(({ value, label, color, ring }) => (
|
|
<div key={value}>
|
|
<RadioGroupItem value={value} id={`color-${value}`} className="peer sr-only" />
|
|
<Label
|
|
htmlFor={`color-${value}`}
|
|
className={cn(
|
|
"flex flex-col items-center gap-3 rounded-lg border-2 p-4 hover:bg-accent cursor-pointer transition-all",
|
|
"peer-data-[state=checked]:border-primary peer-data-[state=checked]:bg-primary/5"
|
|
)}
|
|
>
|
|
<div className={cn("h-8 w-8 rounded-full", color, ring, "ring-2 ring-offset-2 ring-offset-background")} />
|
|
<span className="text-sm font-medium">{label}</span>
|
|
</Label>
|
|
</div>
|
|
))}
|
|
</RadioGroup>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Website Themes</CardTitle>
|
|
<CardDescription>
|
|
Select your website's visual theme.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4">
|
|
{websiteThemes.map((wt) => {
|
|
const isActive = websiteTheme === wt.id
|
|
return (
|
|
<button
|
|
key={wt.id}
|
|
type="button"
|
|
onClick={() => setWebsiteTheme(wt.id)}
|
|
className={cn(
|
|
"group relative flex flex-col items-start gap-3 rounded-xl border-2 p-4 text-left transition-all",
|
|
"hover:bg-accent cursor-pointer",
|
|
isActive
|
|
? "border-primary bg-primary/5 shadow-sm"
|
|
: "border-border hover:border-muted-foreground/30"
|
|
)}
|
|
>
|
|
{isActive && (
|
|
<span className="absolute right-2 top-2 flex h-5 w-5 items-center justify-center rounded-full bg-primary text-[10px] text-primary-foreground">
|
|
<Check className="h-3 w-3" />
|
|
</span>
|
|
)}
|
|
|
|
<div className="flex h-20 w-full items-center justify-center overflow-hidden rounded-lg border border-border bg-background">
|
|
<div className="flex h-full w-full">
|
|
<div className="flex w-1/3 flex-col gap-0.5 bg-[#0a0a0f] p-1.5">
|
|
<div className="h-1 w-full rounded-sm bg-[#1a1a24]" />
|
|
<div className="h-1 w-2/3 rounded-sm bg-[#1a1a24]" />
|
|
<div className="mt-auto h-1.5 w-full rounded-sm bg-[#1a1a24]" />
|
|
</div>
|
|
<div className="flex flex-1 flex-col">
|
|
<div className="flex h-5 items-center gap-1 bg-[#CC0000] px-1.5">
|
|
<div className="h-1.5 w-1.5 rounded-full bg-white/30" />
|
|
<div className="h-1.5 w-1.5 rounded-full bg-white/30" />
|
|
<div className="h-1.5 w-1.5 rounded-full bg-white/30" />
|
|
</div>
|
|
<div className="flex flex-1 items-center justify-center bg-[#141414]">
|
|
<div className="h-2 w-2 rounded-full bg-[#CC0000]/40" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex w-full items-center justify-between gap-2">
|
|
<span className="text-sm font-medium">{wt.name}</span>
|
|
{isActive && (
|
|
<span className="rounded-full bg-primary/10 px-2 py-0.5 text-[10px] font-semibold text-primary">
|
|
Current
|
|
</span>
|
|
)}
|
|
</div>
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|