Compare commits
2 Commits
ec4de03cf5
...
ffa595451e
| Author | SHA1 | Date | |
|---|---|---|---|
| ffa595451e | |||
| bd581739f9 |
@@ -3,7 +3,6 @@
|
|||||||
This is the current website appearance captured as a reusable CSS theme.
|
This is the current website appearance captured as a reusable CSS theme.
|
||||||
============================================================================ */
|
============================================================================ */
|
||||||
|
|
||||||
:root,
|
|
||||||
.theme-spidey {
|
.theme-spidey {
|
||||||
--background: 222.2 84% 4.9%;
|
--background: 222.2 84% 4.9%;
|
||||||
--foreground: 210 40% 98%;
|
--foreground: 210 40% 98%;
|
||||||
@@ -34,3 +33,37 @@
|
|||||||
--sidebar-border: 217.2 32.6% 17.5%;
|
--sidebar-border: 217.2 32.6% 17.5%;
|
||||||
--sidebar-ring: 0 100% 53%;
|
--sidebar-ring: 0 100% 53%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.theme-spidey body::before {
|
||||||
|
content: "";
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background-image:
|
||||||
|
repeating-conic-gradient(
|
||||||
|
from 10deg,
|
||||||
|
transparent 0deg 10deg,
|
||||||
|
rgba(220, 38, 38, 0.04) 10deg 12deg
|
||||||
|
),
|
||||||
|
repeating-radial-gradient(
|
||||||
|
circle at 50% 50%,
|
||||||
|
transparent 0,
|
||||||
|
transparent 30px,
|
||||||
|
rgba(37, 99, 235, 0.03) 30px,
|
||||||
|
transparent 32px
|
||||||
|
),
|
||||||
|
radial-gradient(2px 2px at 10% 20%, rgba(220,38,38,0.35) 0%, transparent 100%),
|
||||||
|
radial-gradient(2px 2px at 30% 80%, rgba(37,99,235,0.35) 0%, transparent 100%),
|
||||||
|
radial-gradient(2px 2px at 50% 30%, rgba(220,38,38,0.3) 0%, transparent 100%),
|
||||||
|
radial-gradient(2px 2px at 70% 60%, rgba(37,99,235,0.3) 0%, transparent 100%),
|
||||||
|
radial-gradient(2px 2px at 85% 15%, rgba(220,38,38,0.35) 0%, transparent 100%),
|
||||||
|
radial-gradient(2px 2px at 20% 50%, rgba(37,99,235,0.25) 0%, transparent 100%),
|
||||||
|
radial-gradient(2px 2px at 65% 85%, rgba(220,38,38,0.25) 0%, transparent 100%),
|
||||||
|
radial-gradient(2px 2px at 40% 10%, rgba(37,99,235,0.3) 0%, transparent 100%),
|
||||||
|
radial-gradient(2px 2px at 90% 75%, rgba(220,38,38,0.25) 0%, transparent 100%),
|
||||||
|
radial-gradient(2px 2px at 5% 60%, rgba(37,99,235,0.3) 0%, transparent 100%),
|
||||||
|
radial-gradient(2px 2px at 55% 55%, rgba(220,38,38,0.2) 0%, transparent 100%);
|
||||||
|
background-size: 200% 200%;
|
||||||
|
animation: drift 60s linear infinite;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/* ============================================================================
|
||||||
|
Starforce Theme — Dark cosmic with crimson accents and neon green glow
|
||||||
|
Captures the current starfield + drift + glow effects used live.
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
.theme-starforce {
|
||||||
|
--background: 0 0% 10%;
|
||||||
|
--foreground: 210 40% 98%;
|
||||||
|
--card: 0 0% 9%;
|
||||||
|
--card-foreground: 210 40% 98%;
|
||||||
|
--popover: 222.2 84% 11%;
|
||||||
|
--popover-foreground: 210 40% 98%;
|
||||||
|
--primary: 0 100% 53%;
|
||||||
|
--primary-foreground: 222.2 47.4% 11.2%;
|
||||||
|
--secondary: 217.2 32.6% 17.5%;
|
||||||
|
--secondary-foreground: 210 40% 98%;
|
||||||
|
--muted: 217.2 32.6% 17.5%;
|
||||||
|
--muted-foreground: 215 20.2% 65.1%;
|
||||||
|
--accent: 217.2 32.6% 17.5%;
|
||||||
|
--accent-foreground: 210 40% 98%;
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
--border: 217.2 32.6% 17.5%;
|
||||||
|
--input: 217.2 32.6% 17.5%;
|
||||||
|
--ring: 0 100% 53%;
|
||||||
|
--radius: 0.5rem;
|
||||||
|
--sidebar: 0 0% 11%;
|
||||||
|
--sidebar-foreground: 210 40% 98%;
|
||||||
|
--sidebar-primary: 145 65% 50%;
|
||||||
|
--sidebar-primary-foreground: 0 0% 100%;
|
||||||
|
--sidebar-accent: 217.2 32.6% 17.5%;
|
||||||
|
--sidebar-accent-foreground: 210 40% 98%;
|
||||||
|
--sidebar-border: 217.2 32.6% 17.5%;
|
||||||
|
--sidebar-ring: 0 100% 53%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-starforce body::before {
|
||||||
|
content: "";
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background-image:
|
||||||
|
radial-gradient(1px 1px at 10% 20%, rgba(100,100,120,0.4) 0%, transparent 100%),
|
||||||
|
radial-gradient(1px 1px at 20% 40%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
||||||
|
radial-gradient(1.5px 1.5px at 30% 10%, rgba(80,80,100,0.5) 0%, transparent 100%),
|
||||||
|
radial-gradient(1px 1px at 40% 60%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
||||||
|
radial-gradient(1px 1px at 50% 80%, rgba(90,90,110,0.4) 0%, transparent 100%),
|
||||||
|
radial-gradient(1.5px 1.5px at 60% 30%, rgba(80,80,100,0.5) 0%, transparent 100%),
|
||||||
|
radial-gradient(1px 1px at 70% 50%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
||||||
|
radial-gradient(1px 1px at 80% 90%, rgba(90,90,110,0.4) 0%, transparent 100%),
|
||||||
|
radial-gradient(1.5px 1.5px at 90% 15%, rgba(80,80,100,0.5) 0%, transparent 100%),
|
||||||
|
radial-gradient(1px 1px at 15% 70%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
||||||
|
radial-gradient(1px 1px at 25% 90%, rgba(90,90,110,0.4) 0%, transparent 100%),
|
||||||
|
radial-gradient(1.5px 1.5px at 35% 35%, rgba(80,80,100,0.5) 0%, transparent 100%),
|
||||||
|
radial-gradient(1px 1px at 45% 15%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
||||||
|
radial-gradient(1px 1px at 55% 75%, rgba(90,90,110,0.4) 0%, transparent 100%),
|
||||||
|
radial-gradient(1.5px 1.5px at 65% 55%, rgba(80,80,100,0.5) 0%, transparent 100%),
|
||||||
|
radial-gradient(1px 1px at 75% 25%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
||||||
|
radial-gradient(1px 1px at 85% 65%, rgba(90,90,110,0.4) 0%, transparent 100%),
|
||||||
|
radial-gradient(1.5px 1.5px at 95% 45%, rgba(80,80,100,0.5) 0%, transparent 100%);
|
||||||
|
background-size: 200% 200%;
|
||||||
|
animation: drift 60s linear infinite;
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"theme": {
|
||||||
|
"id": "starforce",
|
||||||
|
"name": "Starforce",
|
||||||
|
"description": "Dark cosmic theme with crimson accents, starfield background, and neon green glow effects",
|
||||||
|
"type": "dark",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"colors": {
|
||||||
|
"background": "hsl(0, 0%, 10%)",
|
||||||
|
"foreground": "hsl(210, 40%, 98%)",
|
||||||
|
"card": "hsl(0, 0%, 9%)",
|
||||||
|
"cardForeground": "hsl(210, 40%, 98%)",
|
||||||
|
"popover": "hsl(222.2, 84%, 11%)",
|
||||||
|
"popoverForeground": "hsl(210, 40%, 98%)",
|
||||||
|
"primary": "hsl(0, 100%, 53%)",
|
||||||
|
"primaryForeground": "hsl(222.2, 47.4%, 11.2%)",
|
||||||
|
"secondary": "hsl(217.2, 32.6%, 17.5%)",
|
||||||
|
"secondaryForeground": "hsl(210, 40%, 98%)",
|
||||||
|
"muted": "hsl(217.2, 32.6%, 17.5%)",
|
||||||
|
"mutedForeground": "hsl(215, 20.2%, 65.1%)",
|
||||||
|
"accent": "hsl(217.2, 32.6%, 17.5%)",
|
||||||
|
"accentForeground": "hsl(210, 40%, 98%)",
|
||||||
|
"destructive": "hsl(0, 62.8%, 30.6%)",
|
||||||
|
"destructiveForeground": "hsl(210, 40%, 98%)",
|
||||||
|
"border": "hsl(217.2, 32.6%, 17.5%)",
|
||||||
|
"input": "hsl(217.2, 32.6%, 17.5%)",
|
||||||
|
"ring": "hsl(0, 100%, 53%)",
|
||||||
|
"sidebar": "hsl(0, 0%, 11%)",
|
||||||
|
"sidebarForeground": "hsl(210, 40%, 98%)",
|
||||||
|
"sidebarPrimary": "hsl(145, 65%, 50%)",
|
||||||
|
"sidebarPrimaryForeground": "hsl(0, 0%, 100%)",
|
||||||
|
"sidebarAccent": "hsl(217.2, 32.6%, 17.5%)",
|
||||||
|
"sidebarAccentForeground": "hsl(210, 40%, 98%)",
|
||||||
|
"sidebarBorder": "hsl(217.2, 32.6%, 17.5%)",
|
||||||
|
"sidebarRing": "hsl(0, 100%, 53%)"
|
||||||
|
},
|
||||||
|
"borderRadius": "0.5rem",
|
||||||
|
"typography": {
|
||||||
|
"fontFamily": "Inter, sans-serif",
|
||||||
|
"headingFont": "Inter, sans-serif"
|
||||||
|
},
|
||||||
|
"keyColors": {
|
||||||
|
"primary": "#FF0000",
|
||||||
|
"background": "#1a1a1a",
|
||||||
|
"card": "#171717",
|
||||||
|
"sidebar": "#1c1c1c",
|
||||||
|
"border": "#2a2a35",
|
||||||
|
"text": "#e8e8ef",
|
||||||
|
"mutedText": "#888888",
|
||||||
|
"glow": "#39ff14"
|
||||||
|
},
|
||||||
|
"effects": {
|
||||||
|
"starfield": true,
|
||||||
|
"starTwinkle": true,
|
||||||
|
"starDrift": true,
|
||||||
|
"textGlow": "#39ff14"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,8 +26,10 @@ import {
|
|||||||
import { DashboardStats } from "@/types"
|
import { DashboardStats } from "@/types"
|
||||||
import { CrossedLightsabers } from "@/components/dashboard/crossed-lightsabers"
|
import { CrossedLightsabers } from "@/components/dashboard/crossed-lightsabers"
|
||||||
import { StarField } from "@/components/dashboard/star-field"
|
import { StarField } from "@/components/dashboard/star-field"
|
||||||
|
import { useWebsiteTheme } from "@/providers/website-theme-provider"
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
|
const { websiteTheme: theme } = useWebsiteTheme()
|
||||||
const [period, setPeriod] = useState("6months")
|
const [period, setPeriod] = useState("6months")
|
||||||
const [stats, setStats] = useState<DashboardStats | null>(null)
|
const [stats, setStats] = useState<DashboardStats | null>(null)
|
||||||
const pollingRef = useRef<NodeJS.Timeout | null>(null)
|
const pollingRef = useRef<NodeJS.Timeout | null>(null)
|
||||||
@@ -65,20 +67,22 @@ export default function DashboardPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 relative">
|
<div className="space-y-6 relative">
|
||||||
<StarField />
|
{theme === "starforce" && <StarField />}
|
||||||
<div className="relative z-[1] space-y-6">
|
<div className="relative z-[1] space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={<span style={{fontFamily:"'Audiowide',sans-serif"}}>Dashboard</span>}
|
title={<span style={{fontFamily:"'Audiowide',sans-serif"}}>Dashboard</span>}
|
||||||
description="Overview of your sales pipeline"
|
description="Overview of your sales pipeline"
|
||||||
>
|
>
|
||||||
<span
|
{theme === "starforce" && (
|
||||||
className="pointer-events-none select-none text-5xl text-gray-600 dark:text-[#b0f0c0] tracking-wider font-bold text-glow whitespace-nowrap"
|
<span
|
||||||
style={{ fontFamily: "'Cormorant Garamond', serif" }}
|
className="pointer-events-none select-none text-5xl text-gray-600 dark:text-[#b0f0c0] tracking-wider font-bold text-glow whitespace-nowrap"
|
||||||
>
|
style={{ fontFamily: "'Cormorant Garamond', serif" }}
|
||||||
Trust the Force, Find your Path
|
>
|
||||||
</span>
|
Trust the Force, Find your Path
|
||||||
<CrossedLightsabers />
|
</span>
|
||||||
|
)}
|
||||||
|
{theme === "starforce" && <CrossedLightsabers />}
|
||||||
<Select value={period} onValueChange={setPeriod}>
|
<Select value={period} onValueChange={setPeriod}>
|
||||||
<SelectTrigger className="h-9 w-[160px]">
|
<SelectTrigger className="h-9 w-[160px]">
|
||||||
<ListFilter className="mr-2 h-4 w-4" />
|
<ListFilter className="mr-2 h-4 w-4" />
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export async function GET() {
|
|||||||
[user.id],
|
[user.id],
|
||||||
)
|
)
|
||||||
|
|
||||||
const websiteTheme = result.rows[0]?.website_theme || "spidey"
|
const websiteTheme = result.rows[0]?.website_theme || "starforce"
|
||||||
return NextResponse.json({ websiteTheme })
|
return NextResponse.json({ websiteTheme })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Website theme GET error:", error)
|
console.error("Website theme GET error:", error)
|
||||||
@@ -26,7 +26,7 @@ export async function PUT(request: NextRequest) {
|
|||||||
if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
||||||
|
|
||||||
const body = await request.json()
|
const body = await request.json()
|
||||||
const theme = body.websiteTheme || "spidey"
|
const theme = body.websiteTheme || "starforce"
|
||||||
|
|
||||||
await query(
|
await query(
|
||||||
`UPDATE users SET preferences = preferences || $2::jsonb WHERE id = $1`,
|
`UPDATE users SET preferences = preferences || $2::jsonb WHERE id = $1`,
|
||||||
|
|||||||
@@ -406,35 +406,6 @@ main {
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body::before {
|
|
||||||
content: "";
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
z-index: -1;
|
|
||||||
pointer-events: none;
|
|
||||||
background-image:
|
|
||||||
radial-gradient(1px 1px at 10% 20%, rgba(100,100,120,0.4) 0%, transparent 100%),
|
|
||||||
radial-gradient(1px 1px at 20% 40%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
|
||||||
radial-gradient(1.5px 1.5px at 30% 10%, rgba(80,80,100,0.5) 0%, transparent 100%),
|
|
||||||
radial-gradient(1px 1px at 40% 60%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
|
||||||
radial-gradient(1px 1px at 50% 80%, rgba(90,90,110,0.4) 0%, transparent 100%),
|
|
||||||
radial-gradient(1.5px 1.5px at 60% 30%, rgba(80,80,100,0.5) 0%, transparent 100%),
|
|
||||||
radial-gradient(1px 1px at 70% 50%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
|
||||||
radial-gradient(1px 1px at 80% 90%, rgba(90,90,110,0.4) 0%, transparent 100%),
|
|
||||||
radial-gradient(1.5px 1.5px at 90% 15%, rgba(80,80,100,0.5) 0%, transparent 100%),
|
|
||||||
radial-gradient(1px 1px at 15% 70%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
|
||||||
radial-gradient(1px 1px at 25% 90%, rgba(90,90,110,0.4) 0%, transparent 100%),
|
|
||||||
radial-gradient(1.5px 1.5px at 35% 35%, rgba(80,80,100,0.5) 0%, transparent 100%),
|
|
||||||
radial-gradient(1px 1px at 45% 15%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
|
||||||
radial-gradient(1px 1px at 55% 75%, rgba(90,90,110,0.4) 0%, transparent 100%),
|
|
||||||
radial-gradient(1.5px 1.5px at 65% 55%, rgba(80,80,100,0.5) 0%, transparent 100%),
|
|
||||||
radial-gradient(1px 1px at 75% 25%, rgba(100,100,120,0.3) 0%, transparent 100%),
|
|
||||||
radial-gradient(1px 1px at 85% 65%, rgba(90,90,110,0.4) 0%, transparent 100%),
|
|
||||||
radial-gradient(1.5px 1.5px at 95% 45%, rgba(80,80,100,0.5) 0%, transparent 100%);
|
|
||||||
background-size: 200% 200%;
|
|
||||||
animation: drift 60s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes drift {
|
@keyframes drift {
|
||||||
0% { background-position: 0% 0%; }
|
0% { background-position: 0% 0%; }
|
||||||
100% { background-position: 100% 100%; }
|
100% { background-position: 100% 100%; }
|
||||||
|
|||||||
+7
-2
@@ -1,8 +1,11 @@
|
|||||||
import type { Metadata, Viewport } from "next"
|
import type { Metadata, Viewport } from "next"
|
||||||
import { Inter } from "next/font/google"
|
import { Inter } from "next/font/google"
|
||||||
import { ThemeProvider } from "@/providers/theme-provider"
|
import { ThemeProvider } from "@/providers/theme-provider"
|
||||||
|
import { WebsiteThemeProvider } from "@/providers/website-theme-provider"
|
||||||
import { Toaster } from "@/components/ui/sonner"
|
import { Toaster } from "@/components/ui/sonner"
|
||||||
import "./globals.css"
|
import "./globals.css"
|
||||||
|
import "../../Web_Backgrounds/spidey-theme.css"
|
||||||
|
import "../../Web_Backgrounds/starforce-theme.css"
|
||||||
|
|
||||||
const inter = Inter({
|
const inter = Inter({
|
||||||
variable: "--font-inter",
|
variable: "--font-inter",
|
||||||
@@ -31,8 +34,10 @@ export default function RootLayout({
|
|||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning>
|
||||||
<body className={`${inter.variable} min-h-screen antialiased`}>
|
<body className={`${inter.variable} min-h-screen antialiased`}>
|
||||||
<ThemeProvider attribute="class" defaultTheme="dark" disableTransitionOnChange>
|
<ThemeProvider attribute="class" defaultTheme="dark" disableTransitionOnChange>
|
||||||
{children}
|
<WebsiteThemeProvider>
|
||||||
<Toaster />
|
{children}
|
||||||
|
<Toaster />
|
||||||
|
</WebsiteThemeProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import {
|
|||||||
Trash2,
|
Trash2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { CrmIcon } from "./crm-icon";
|
import { CrmIcon } from "./crm-icon";
|
||||||
|
import { CaptainAmericaShield } from "@/components/shared/captain-america-shield";
|
||||||
|
import { useWebsiteTheme } from "@/providers/website-theme-provider";
|
||||||
|
|
||||||
interface TopbarProps {
|
interface TopbarProps {
|
||||||
onMenuClick: () => void;
|
onMenuClick: () => void;
|
||||||
@@ -41,6 +43,7 @@ interface TopbarProps {
|
|||||||
export function Topbar({ onMenuClick }: TopbarProps) {
|
export function Topbar({ onMenuClick }: TopbarProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { theme, setTheme } = useTheme();
|
const { theme, setTheme } = useTheme();
|
||||||
|
const { websiteTheme } = useWebsiteTheme();
|
||||||
const { user, logout } = useUser();
|
const { user, logout } = useUser();
|
||||||
const { notifications, unreadCount, markAsRead, markAllAsRead, dismiss } =
|
const { notifications, unreadCount, markAsRead, markAllAsRead, dismiss } =
|
||||||
useNotifications();
|
useNotifications();
|
||||||
@@ -74,7 +77,7 @@ export function Topbar({ onMenuClick }: TopbarProps) {
|
|||||||
<header className="sticky top-0 z-20 flex h-16 items-center gap-4 border-b bg-card dark:bg-[#141414] px-4 lg:px-6 relative overflow-hidden">
|
<header className="sticky top-0 z-20 flex h-16 items-center gap-4 border-b bg-card dark:bg-[#141414] px-4 lg:px-6 relative overflow-hidden">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className="flex items-center gap-2 mr-2 shrink-0">
|
<div className="flex items-center gap-2 mr-2 shrink-0">
|
||||||
<CrmIcon />
|
{websiteTheme === "spidey" ? <CaptainAmericaShield /> : <CrmIcon />}
|
||||||
<span className="text-[#CC0000] dark:text-[#FF1111] font-bold text-sm tracking-wide">
|
<span className="text-[#CC0000] dark:text-[#FF1111] font-bold text-sm tracking-wide">
|
||||||
CRM
|
CRM
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
|
|||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { Sun, Moon, Monitor } from "lucide-react"
|
import { Sun, Moon, Monitor, Sparkles, Shield } from "lucide-react"
|
||||||
|
import { useWebsiteTheme } from "@/providers/website-theme-provider"
|
||||||
|
|
||||||
const COLOR_THEME_KEY = "crm-color-theme"
|
const COLOR_THEME_KEY = "crm-color-theme"
|
||||||
|
|
||||||
@@ -29,6 +30,11 @@ const themeOptions = [
|
|||||||
{ value: "ruby", label: "Ruby", color: "bg-red-600", ring: "ring-red-600" },
|
{ value: "ruby", label: "Ruby", color: "bg-red-600", ring: "ring-red-600" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const backgroundOptions = [
|
||||||
|
{ value: "spidey", label: "Spidey", icon: Shield, color: "bg-red-600", ring: "ring-red-600", desc: "Dark theme with red accents" },
|
||||||
|
{ value: "starforce", label: "StarForce", icon: Sparkles, color: "bg-red-700", ring: "ring-red-700", desc: "Cosmic dark with starfield & green glow" },
|
||||||
|
]
|
||||||
|
|
||||||
function getStoredColorTheme(): string {
|
function getStoredColorTheme(): string {
|
||||||
if (typeof window === "undefined") return "default"
|
if (typeof window === "undefined") return "default"
|
||||||
return localStorage.getItem(COLOR_THEME_KEY) || "default"
|
return localStorage.getItem(COLOR_THEME_KEY) || "default"
|
||||||
@@ -46,6 +52,7 @@ function applyColorTheme(theme: string) {
|
|||||||
|
|
||||||
export function ThemeSettings() {
|
export function ThemeSettings() {
|
||||||
const { theme, setTheme } = useTheme()
|
const { theme, setTheme } = useTheme()
|
||||||
|
const { websiteTheme, setWebsiteTheme } = useWebsiteTheme()
|
||||||
const [colorTheme, setColorTheme] = useState("default")
|
const [colorTheme, setColorTheme] = useState("default")
|
||||||
const [mounted, setMounted] = useState(false)
|
const [mounted, setMounted] = useState(false)
|
||||||
|
|
||||||
@@ -119,6 +126,36 @@ export function ThemeSettings() {
|
|||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Color Theme for Background</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Choose the website background theme.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<RadioGroup value={websiteTheme} onValueChange={setWebsiteTheme} className="grid grid-cols-2 gap-4">
|
||||||
|
{backgroundOptions.map(({ value, label, icon: Icon, color, ring, desc }) => (
|
||||||
|
<div key={value}>
|
||||||
|
<RadioGroupItem value={value} id={`bg-${value}`} className="peer sr-only" />
|
||||||
|
<Label
|
||||||
|
htmlFor={`bg-${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" />
|
||||||
|
<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>
|
||||||
|
<span className="text-xs text-muted-foreground">{desc}</span>
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
export function CaptainAmericaShield() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="28"
|
||||||
|
height="28"
|
||||||
|
viewBox="0 0 100 100"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="shield-clip">
|
||||||
|
<polygon points="50,95 5,60 5,30 50,5 95,30 95,60" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g clip-path="url(#shield-clip)">
|
||||||
|
<circle cx="50" cy="45" r="45" fill="#c62828" />
|
||||||
|
<circle cx="50" cy="45" r="35" fill="#efefef" />
|
||||||
|
<circle cx="50" cy="45" r="25" fill="#c62828" />
|
||||||
|
<circle cx="50" cy="45" r="15" fill="#1565c0" />
|
||||||
|
<polygon points="50,32 56,47 72,48 60,58 64,75 50,64 36,75 40,58 28,48 44,47" fill="#efefef" />
|
||||||
|
</g>
|
||||||
|
<polygon
|
||||||
|
points="50,95 5,60 5,30 50,5 95,30 95,60"
|
||||||
|
fill="none"
|
||||||
|
stroke="#c62828"
|
||||||
|
stroke-width="1.5"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,7 +5,8 @@ import { createContext, useContext, useState, useEffect, ReactNode, useCallback
|
|||||||
const WEBSITE_THEME_KEY = "crm-website-theme"
|
const WEBSITE_THEME_KEY = "crm-website-theme"
|
||||||
|
|
||||||
const themeClasses: Record<string, string> = {
|
const themeClasses: Record<string, string> = {
|
||||||
spidey: "",
|
spidey: "theme-spidey",
|
||||||
|
starforce: "theme-starforce",
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WebsiteThemeContextValue {
|
interface WebsiteThemeContextValue {
|
||||||
@@ -38,7 +39,7 @@ function storeTheme(theme: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function WebsiteThemeProvider({ children }: { children: ReactNode }) {
|
export function WebsiteThemeProvider({ children }: { children: ReactNode }) {
|
||||||
const [websiteTheme, setWebsiteThemeState] = useState<string>("spidey")
|
const [websiteTheme, setWebsiteThemeState] = useState<string>("starforce")
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -52,13 +53,13 @@ export function WebsiteThemeProvider({ children }: { children: ReactNode }) {
|
|||||||
fetch("/api/settings/website-theme")
|
fetch("/api/settings/website-theme")
|
||||||
.then((res) => (res.ok ? res.json() : null))
|
.then((res) => (res.ok ? res.json() : null))
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
const theme = data?.websiteTheme || "spidey"
|
const theme = data?.websiteTheme || "starforce"
|
||||||
setWebsiteThemeState(theme)
|
setWebsiteThemeState(theme)
|
||||||
storeTheme(theme)
|
storeTheme(theme)
|
||||||
applyWebsiteTheme(theme)
|
applyWebsiteTheme(theme)
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
applyWebsiteTheme("spidey")
|
applyWebsiteTheme("starforce")
|
||||||
})
|
})
|
||||||
.finally(() => setLoading(false))
|
.finally(() => setLoading(false))
|
||||||
}, [])
|
}, [])
|
||||||
|
|||||||
Reference in New Issue
Block a user