Fixed Light Mode

This commit is contained in:
2026-06-29 15:26:44 +02:00
parent 3c0e7d76ca
commit 61e9fac352
20 changed files with 258 additions and 232 deletions
+6 -3
View File
@@ -24,8 +24,10 @@ import {
SelectValue, SelectValue,
} from "@/components/ui/select" } from "@/components/ui/select"
import { DashboardStats } from "@/types" import { DashboardStats } from "@/types"
import { useWebsiteTheme } from "@/providers/website-theme-provider"
export default function DashboardPage() { export default function DashboardPage() {
const { websiteTheme } = 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)
@@ -84,7 +86,7 @@ export default function DashboardPage() {
</PageHeader> </PageHeader>
</div> </div>
<p className="text-xs font-semibold tracking-widest uppercase text-[#888888] dark:text-[#666666]">Pipeline Overview</p> <p className="text-xs font-semibold tracking-widest uppercase text-[#8A9078] dark:text-[#666666]">Pipeline Overview</p>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6"> <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6">
{stats {stats
@@ -95,7 +97,7 @@ export default function DashboardPage() {
} }
</div> </div>
<p className="text-xs font-semibold tracking-widest uppercase text-[#888888] dark:text-[#666666]">Analytics</p> <p className="text-xs font-semibold tracking-widest uppercase text-[#8A9078] dark:text-[#666666]">Analytics</p>
<div className="grid gap-6 lg:grid-cols-2"> <div className="grid gap-6 lg:grid-cols-2">
<LeadStatusChart data={stats?.statusDistribution ?? []} /> <LeadStatusChart data={stats?.statusDistribution ?? []} />
@@ -104,12 +106,13 @@ export default function DashboardPage() {
<RecentLeadsTable leads={stats?.recentLeads ?? []} /> <RecentLeadsTable leads={stats?.recentLeads ?? []} />
</div> </div>
{/* Daily Bugle watermark */} {websiteTheme === "spidey" && (
<div className="absolute top-12 right-4 pointer-events-none select-none z-0 opacity-[0.09] dark:opacity-[0.15]"> <div className="absolute top-12 right-4 pointer-events-none select-none z-0 opacity-[0.09] dark:opacity-[0.15]">
<span className="text-[96px] font-['Bangers',cursive] leading-none text-[#CC0000] dark:text-[#FF1111] tracking-wider"> <span className="text-[96px] font-['Bangers',cursive] leading-none text-[#CC0000] dark:text-[#FF1111] tracking-wider">
DAILY BUGLE DAILY BUGLE
</span> </span>
</div> </div>
)}
</div> </div>
) )
} }
+40 -40
View File
@@ -53,24 +53,24 @@ export default function CallRoomPage({ params }: { params: Promise<{ roomId: str
if (!joined) { if (!joined) {
return ( return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#CC0000]/10 to-[#990000]/10 p-4"> <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#C84B4B]/10 to-[#C84B4B]/10 p-4">
<div className="bg-white dark:bg-[#141414] rounded-2xl p-8 w-full max-w-md border border-[#E0E0E0] dark:border-[#CC0000]/20 shadow-lg text-center"> <div className="bg-white dark:bg-[#141414] rounded-2xl p-8 w-full max-w-md border border-[#D4D8CC] dark:border-[#CC0000]/20 shadow-lg text-center">
{connectionTimeout ? ( {connectionTimeout ? (
<> <>
<h1 className="text-2xl font-bold text-[#111111] dark:text-white mb-3">This call is no longer available.</h1> <h1 className="text-2xl font-bold text-[#2D3020] dark:text-white mb-3">This call is no longer available.</h1>
</> </>
) : !isReady ? ( ) : !isReady ? (
<> <>
<div className="flex flex-col items-center gap-4 py-6"> <div className="flex flex-col items-center gap-4 py-6">
<Loader2 className="h-8 w-8 animate-spin text-[#CC0000]" /> <Loader2 className="h-8 w-8 animate-spin text-[#C84B4B]" />
<p className="text-[#888888] text-sm">Connecting to call...</p> <p className="text-[#8A9078] text-sm">Connecting to call...</p>
</div> </div>
</> </>
) : ( ) : (
<> <>
<h1 className="text-2xl font-bold text-[#111111] dark:text-white mb-3">Join Call</h1> <h1 className="text-2xl font-bold text-[#2D3020] dark:text-white mb-3">Join Call</h1>
{micError && ( {micError && (
<p className="text-[#CC0000] text-xs mb-4">{micError}</p> <p className="text-[#C84B4B] text-xs mb-4">{micError}</p>
)} )}
<input <input
type="text" type="text"
@@ -78,12 +78,12 @@ export default function CallRoomPage({ params }: { params: Promise<{ roomId: str
onChange={(e) => setDisplayName(e.target.value)} onChange={(e) => setDisplayName(e.target.value)}
onKeyDown={(e) => { if (e.key === "Enter") handleJoin() }} onKeyDown={(e) => { if (e.key === "Enter") handleJoin() }}
placeholder="Enter your name" placeholder="Enter your name"
className="bg-[#F5F5F5] dark:bg-[#1A1A1A] border border-[#E0E0E0] dark:border-[#333333] text-[#111111] dark:text-white placeholder-[#AAAAAA] dark:placeholder-[#555555] rounded-xl px-4 py-2.5 text-sm w-full mb-4 outline-none transition-colors focus:border-[#CC0000] dark:focus:border-[#FF4444]" className="bg-[#FAFAF6] dark:bg-[#1A1A1A] border border-[#D4D8CC] dark:border-[#333333] text-[#2D3020] dark:text-white placeholder-[#8A9078] dark:placeholder-[#555555] rounded-xl px-4 py-2.5 text-sm w-full mb-4 outline-none transition-colors focus:border-[#C84B4B] dark:focus:border-[#FF4444]"
/> />
<button <button
onClick={handleJoin} onClick={handleJoin}
disabled={!displayName.trim()} disabled={!displayName.trim()}
className="w-full bg-[#CC0000] hover:bg-[#990000] disabled:bg-[#CCCCCC] disabled:cursor-not-allowed dark:bg-[#FF1111] dark:hover:bg-[#CC0000] dark:disabled:bg-[#333333] text-white font-semibold text-sm rounded-xl py-2.5 flex items-center justify-center gap-2 transition-all duration-200" className="w-full bg-[#C84B4B] hover:bg-[#C84B4B]/80 disabled:bg-[#8A9078] disabled:cursor-not-allowed dark:bg-[#FF1111] dark:hover:bg-[#CC0000] dark:disabled:bg-[#333333] text-white font-semibold text-sm rounded-xl py-2.5 flex items-center justify-center gap-2 transition-all duration-200"
> >
<Phone className="h-4 w-4" /> <Phone className="h-4 w-4" />
Join Call Join Call
@@ -96,14 +96,14 @@ export default function CallRoomPage({ params }: { params: Promise<{ roomId: str
} }
return ( return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#CC0000]/10 to-[#990000]/10 p-4"> <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#C84B4B]/10 to-[#C84B4B]/10 p-4">
<div className="bg-white dark:bg-[#141414] rounded-2xl p-8 w-full max-w-md border border-[#E0E0E0] dark:border-[#CC0000]/20 shadow-lg text-center"> <div className="bg-white dark:bg-[#141414] rounded-2xl p-8 w-full max-w-md border border-[#D4D8CC] dark:border-[#CC0000]/20 shadow-lg text-center">
{callState === "calling" && ( {callState === "calling" && (
<div> <div>
<h1 className="text-2xl font-bold text-[#111111] dark:text-white mb-4">Calling</h1> <h1 className="text-2xl font-bold text-[#2D3020] dark:text-white mb-4">Calling</h1>
<p className="text-[#888888] text-sm animate-pulse">Connecting to call room...</p> <p className="text-[#8A9078] text-sm animate-pulse">Connecting to call room...</p>
<div className="flex justify-center mt-6"> <div className="flex justify-center mt-6">
<button onClick={handleEnd} className="w-14 h-14 rounded-full bg-[#CC0000] hover:bg-[#990000] dark:bg-[#FF1111] flex items-center justify-center text-white font-bold transition-all duration-200"> <button onClick={handleEnd} className="w-14 h-14 rounded-full bg-[#C84B4B] hover:bg-[#C84B4B]/80 dark:bg-[#FF1111] flex items-center justify-center text-white font-bold transition-all duration-200">
<PhoneOff className="h-5 w-5" /> <PhoneOff className="h-5 w-5" />
</button> </button>
</div> </div>
@@ -112,21 +112,21 @@ export default function CallRoomPage({ params }: { params: Promise<{ roomId: str
{callState === "waiting" && ( {callState === "waiting" && (
<div> <div>
<h1 className="text-2xl font-bold text-[#111111] dark:text-white mb-4">Waiting for participant</h1> <h1 className="text-2xl font-bold text-[#2D3020] dark:text-white mb-4">Waiting for participant</h1>
<p className="text-[#888888] text-sm animate-pulse">Share the call link to invite someone...</p> <p className="text-[#8A9078] text-sm animate-pulse">Share the call link to invite someone...</p>
{participants.length > 0 && ( {participants.length > 0 && (
<div className="mt-4 bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-xl p-3"> <div className="mt-4 bg-[#FAFAF6] dark:bg-[#1A1A1A] rounded-xl p-3">
<div className="flex items-center gap-2 text-xs text-[#888888] mb-2"> <div className="flex items-center gap-2 text-xs text-[#8A9078] mb-2">
<Users className="h-3 w-3" /> <Users className="h-3 w-3" />
Participants ({participants.length}) Participants ({participants.length})
</div> </div>
{participants.map((p) => ( {participants.map((p) => (
<div key={p.id} className="text-sm text-[#111111] dark:text-white text-left">{p.label}</div> <div key={p.id} className="text-sm text-[#2D3020] dark:text-white text-left">{p.label}</div>
))} ))}
</div> </div>
)} )}
<div className="flex justify-center mt-6"> <div className="flex justify-center mt-6">
<button onClick={handleEnd} className="w-14 h-14 rounded-full bg-[#CC0000] hover:bg-[#990000] dark:bg-[#FF1111] flex items-center justify-center text-white font-bold transition-all duration-200"> <button onClick={handleEnd} className="w-14 h-14 rounded-full bg-[#C84B4B] hover:bg-[#C84B4B]/80 dark:bg-[#FF1111] flex items-center justify-center text-white font-bold transition-all duration-200">
<PhoneOff className="h-5 w-5" /> <PhoneOff className="h-5 w-5" />
</button> </button>
</div> </div>
@@ -135,21 +135,21 @@ export default function CallRoomPage({ params }: { params: Promise<{ roomId: str
{callState === "participant_joined" && ( {callState === "participant_joined" && (
<div> <div>
<h1 className="text-2xl font-bold text-[#111111] dark:text-white mb-4">Participant joined</h1> <h1 className="text-2xl font-bold text-[#2D3020] dark:text-white mb-4">Participant joined</h1>
{participants.length > 0 && ( {participants.length > 0 && (
<div className="mb-4 bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-xl p-3"> <div className="mb-4 bg-[#FAFAF6] dark:bg-[#1A1A1A] rounded-xl p-3">
<div className="flex items-center gap-2 text-xs text-[#888888] mb-2"> <div className="flex items-center gap-2 text-xs text-[#8A9078] mb-2">
<Users className="h-3 w-3" /> <Users className="h-3 w-3" />
Participants ({participants.length}) Participants ({participants.length})
</div> </div>
{participants.map((p) => ( {participants.map((p) => (
<div key={p.id} className="text-sm text-[#111111] dark:text-white text-left">{p.label}</div> <div key={p.id} className="text-sm text-[#2D3020] dark:text-white text-left">{p.label}</div>
))} ))}
</div> </div>
)} )}
<p className="text-[#888888] text-sm animate-pulse">Connecting...</p> <p className="text-[#8A9078] text-sm animate-pulse">Connecting...</p>
<div className="flex justify-center mt-6"> <div className="flex justify-center mt-6">
<button onClick={handleEnd} className="w-14 h-14 rounded-full bg-[#CC0000] hover:bg-[#990000] dark:bg-[#FF1111] flex items-center justify-center text-white font-bold transition-all duration-200"> <button onClick={handleEnd} className="w-14 h-14 rounded-full bg-[#C84B4B] hover:bg-[#C84B4B]/80 dark:bg-[#FF1111] flex items-center justify-center text-white font-bold transition-all duration-200">
<PhoneOff className="h-5 w-5" /> <PhoneOff className="h-5 w-5" />
</button> </button>
</div> </div>
@@ -158,10 +158,10 @@ export default function CallRoomPage({ params }: { params: Promise<{ roomId: str
{callState === "connecting" && ( {callState === "connecting" && (
<div> <div>
<h1 className="text-2xl font-bold text-[#111111] dark:text-white mb-4">Connecting</h1> <h1 className="text-2xl font-bold text-[#2D3020] dark:text-white mb-4">Connecting</h1>
<p className="text-[#888888] text-sm animate-pulse">Establishing secure connection...</p> <p className="text-[#8A9078] text-sm animate-pulse">Establishing secure connection...</p>
<div className="flex justify-center mt-6"> <div className="flex justify-center mt-6">
<button onClick={handleEnd} className="w-14 h-14 rounded-full bg-[#CC0000] hover:bg-[#990000] dark:bg-[#FF1111] flex items-center justify-center text-white font-bold transition-all duration-200"> <button onClick={handleEnd} className="w-14 h-14 rounded-full bg-[#C84B4B] hover:bg-[#C84B4B]/80 dark:bg-[#FF1111] flex items-center justify-center text-white font-bold transition-all duration-200">
<PhoneOff className="h-5 w-5" /> <PhoneOff className="h-5 w-5" />
</button> </button>
</div> </div>
@@ -170,37 +170,37 @@ export default function CallRoomPage({ params }: { params: Promise<{ roomId: str
{callState === "connected" && ( {callState === "connected" && (
<div> <div>
<h1 className="text-2xl font-bold text-[#111111] dark:text-white mb-1">Connected</h1> <h1 className="text-2xl font-bold text-[#2D3020] dark:text-white mb-1">Connected</h1>
{participants.length > 0 && ( {participants.length > 0 && (
<div className="mb-4 bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-xl p-3"> <div className="mb-4 bg-[#FAFAF6] dark:bg-[#1A1A1A] rounded-xl p-3">
<div className="flex items-center gap-2 text-xs text-[#888888] mb-2"> <div className="flex items-center gap-2 text-xs text-[#8A9078] mb-2">
<Users className="h-3 w-3" /> <Users className="h-3 w-3" />
Participants ({participants.length}) Participants ({participants.length})
</div> </div>
{participants.map((p) => ( {participants.map((p) => (
<div key={p.id} className="text-sm text-[#111111] dark:text-white text-left">{p.label}</div> <div key={p.id} className="text-sm text-[#2D3020] dark:text-white text-left">{p.label}</div>
))} ))}
</div> </div>
)} )}
<div className="text-[#CC0000] font-mono text-lg mb-6"> <div className="text-[#C84B4B] font-mono text-lg mb-6">
{formatDuration(callDuration)} {formatDuration(callDuration)}
</div> </div>
<div className="flex justify-center gap-4"> <div className="flex justify-center gap-4">
<button <button
onClick={toggleSpeaker} onClick={toggleSpeaker}
className={`w-14 h-14 rounded-full flex items-center justify-center text-white font-bold transition-all duration-200 ${isSpeakerOn ? 'bg-[#0033CC] dark:bg-[#1144FF]' : 'bg-[#444444] dark:bg-[#333333]'}`} className={`w-14 h-14 rounded-full flex items-center justify-center text-white font-bold transition-all duration-200 ${isSpeakerOn ? 'bg-[#5A8FC4] dark:bg-[#1144FF]' : 'bg-[#8A9078] dark:bg-[#333333]'}`}
> >
{isSpeakerOn ? <Volume2 className="h-5 w-5" /> : <VolumeX className="h-5 w-5" />} {isSpeakerOn ? <Volume2 className="h-5 w-5" /> : <VolumeX className="h-5 w-5" />}
</button> </button>
<button <button
onClick={toggleMute} onClick={toggleMute}
className={`w-14 h-14 rounded-full flex items-center justify-center text-white font-bold transition-all duration-200 ${isMuted ? 'bg-[#0033CC] dark:bg-[#1144FF]' : 'bg-[#444444] dark:bg-[#333333]'}`} className={`w-14 h-14 rounded-full flex items-center justify-center text-white font-bold transition-all duration-200 ${isMuted ? 'bg-[#5A8FC4] dark:bg-[#1144FF]' : 'bg-[#8A9078] dark:bg-[#333333]'}`}
> >
{isMuted ? <MicOff className="h-5 w-5" /> : <Mic className="h-5 w-5" />} {isMuted ? <MicOff className="h-5 w-5" /> : <Mic className="h-5 w-5" />}
</button> </button>
<button <button
onClick={handleEnd} onClick={handleEnd}
className="w-14 h-14 rounded-full bg-[#CC0000] hover:bg-[#990000] dark:bg-[#FF1111] flex items-center justify-center text-white font-bold transition-all duration-200" className="w-14 h-14 rounded-full bg-[#C84B4B] hover:bg-[#C84B4B]/80 dark:bg-[#FF1111] flex items-center justify-center text-white font-bold transition-all duration-200"
> >
<PhoneOff className="h-5 w-5" /> <PhoneOff className="h-5 w-5" />
</button> </button>
@@ -210,15 +210,15 @@ export default function CallRoomPage({ params }: { params: Promise<{ roomId: str
{(callState === "ended" || callState === "idle") && ( {(callState === "ended" || callState === "idle") && (
<div> <div>
<h1 className="text-2xl font-bold text-[#111111] dark:text-white mb-4">Call ended</h1> <h1 className="text-2xl font-bold text-[#2D3020] dark:text-white mb-4">Call ended</h1>
{callDuration > 0 && ( {callDuration > 0 && (
<p className="text-[#888888] text-sm mb-6">Duration: {formatDuration(callDuration)}</p> <p className="text-[#8A9078] text-sm mb-6">Duration: {formatDuration(callDuration)}</p>
)} )}
</div> </div>
)} )}
{error && ( {error && (
<p className="text-[#CC0000] text-xs mt-4">{error}</p> <p className="text-[#C84B4B] text-xs mt-4">{error}</p>
)} )}
</div> </div>
</div> </div>
+33 -27
View File
@@ -76,35 +76,41 @@
} }
:root { :root {
--background: 210 40% 96%; --background: 60 20% 95%;
--foreground: 222 47% 11%; --foreground: 71 20% 16%;
--card: 214 32% 91%; --card: 60 29% 97%;
--card-foreground: 222 47% 11%; --card-foreground: 71 20% 16%;
--popover: 214 32% 91%; --popover: 60 29% 97%;
--popover-foreground: 222 47% 11%; --popover-foreground: 71 20% 16%;
--primary: 221 83% 53%; --primary: 0 53% 54%;
--primary-foreground: 210 40% 96%; --primary-foreground: 0 0% 100%;
--secondary: 214 100% 97%; --secondary: 210 47% 56%;
--secondary-foreground: 224 76% 48%; --secondary-foreground: 0 0% 100%;
--muted: 210 40% 96%; --muted: 75 10% 93%;
--muted-foreground: 213 24% 65%; --muted-foreground: 75 10% 52%;
--accent: 214 100% 97%; --accent: 159 32% 43%;
--accent-foreground: 224 76% 48%; --accent-foreground: 0 0% 100%;
--destructive: 0 84.2% 60.2%; --destructive: 0 53% 54%;
--destructive-foreground: 210 40% 98%; --destructive-foreground: 0 0% 100%;
--border: 213 27% 84%; --border: 80 13% 82%;
--input: 213 27% 84%; --input: 80 13% 82%;
--ring: 221 83% 53%; --ring: 0 53% 54%;
--sidebar: 214 32% 91%; --sidebar: 80 17% 92%;
--sidebar-foreground: 222 47% 11%; --sidebar-foreground: 71 20% 16%;
--sidebar-primary: 221 83% 53%; --sidebar-primary: 0 53% 54%;
--sidebar-primary-foreground: 210 40% 96%; --sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 214 100% 97%; --sidebar-accent: 80 17% 92%;
--sidebar-accent-foreground: 224 76% 48%; --sidebar-accent-foreground: 71 20% 16%;
--sidebar-border: 213 27% 84%; --sidebar-border: 80 13% 82%;
--sidebar-ring: 221 83% 53%; --sidebar-ring: 0 53% 54%;
--radius: 0.5rem; --radius: 0.5rem;
--theme-primary: hsl(var(--primary)); --theme-primary: hsl(var(--primary));
--color-blue: 210 47% 56%;
--color-teal: 159 32% 43%;
--color-red-tint: 0 62% 95%;
--color-blue-tint: 208 54% 93%;
--color-teal-tint: 157 35% 91%;
--color-avatar: 102 21% 52%;
--event-call: 160 84% 39%; --event-call: 160 84% 39%;
--event-follow_up: 38 92% 48%; --event-follow_up: 38 92% 48%;
--event-website_creation: 263 70% 50%; --event-website_creation: 263 70% 50%;
+12 -12
View File
@@ -7,9 +7,9 @@ interface Props {
function ErrorPage({ message }: { message: string }) { function ErrorPage({ message }: { message: string }) {
return ( return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#CC0000]/10 to-[#990000]/10 p-4"> <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#C84B4B]/10 to-[#C84B4B]/10 p-4">
<div className="bg-white dark:bg-[#141414] rounded-2xl p-8 w-full max-w-md border border-[#E0E0E0] dark:border-[#CC0000]/20 shadow-lg text-center"> <div className="bg-white dark:bg-[#141414] rounded-2xl p-8 w-full max-w-md border border-[#D4D8CC] dark:border-[#CC0000]/20 shadow-lg text-center">
<h1 className="text-2xl font-bold text-[#111111] dark:text-white mb-3"> <h1 className="text-2xl font-bold text-[#2D3020] dark:text-white mb-3">
{message} {message}
</h1> </h1>
</div> </div>
@@ -42,24 +42,24 @@ export default async function JoinPage({ params }: Props) {
} }
return ( return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#CC0000]/10 to-[#990000]/10 p-4"> <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-[#C84B4B]/10 to-[#C84B4B]/10 p-4">
<div className="bg-white dark:bg-[#141414] rounded-2xl p-8 w-full max-w-md border border-[#E0E0E0] dark:border-[#CC0000]/20 shadow-lg text-center"> <div className="bg-white dark:bg-[#141414] rounded-2xl p-8 w-full max-w-md border border-[#D4D8CC] dark:border-[#CC0000]/20 shadow-lg text-center">
<h1 className="text-2xl font-bold text-[#111111] dark:text-white mb-3"> <h1 className="text-2xl font-bold text-[#2D3020] dark:text-white mb-3">
You&apos;re Invited! You&apos;re Invited!
</h1> </h1>
<p className="text-[#888888] text-sm mb-6"> <p className="text-[#8A9078] text-sm mb-6">
Someone wants to connect with you on our platform. Someone wants to connect with you on our platform.
</p> </p>
<div className="bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-xl p-4 mb-6 text-left"> <div className="bg-[#FAFAF6] dark:bg-[#1A1A1A] rounded-xl p-4 mb-6 text-left">
<p className="text-xs text-[#888888] mb-1">Contact Number</p> <p className="text-xs text-[#8A9078] mb-1">Contact Number</p>
<p className="text-sm font-medium text-[#111111] dark:text-white">{invite.phone}</p> <p className="text-sm font-medium text-[#2D3020] dark:text-white">{invite.phone}</p>
</div> </div>
<p className="text-xs text-[#888888] mb-6"> <p className="text-xs text-[#8A9078] mb-6">
Create an account to start making free voice calls to this person and others on the platform. Create an account to start making free voice calls to this person and others on the platform.
</p> </p>
<a <a
href="/register" href="/register"
className="block w-full bg-[#CC0000] hover:bg-[#990000] dark:bg-[#FF1111] dark:hover:bg-[#CC0000] text-white font-semibold text-sm rounded-xl py-3 transition-all duration-200" className="block w-full bg-[#C84B4B] hover:bg-[#C84B4B]/80 dark:bg-[#FF1111] dark:hover:bg-[#CC0000] text-white font-semibold text-sm rounded-xl py-3 transition-all duration-200"
> >
Create Account Create Account
</a> </a>
+34 -34
View File
@@ -162,11 +162,11 @@ export default function VoiceCallModal({ open, onClose }: VoiceCallModalProps) {
className="fixed inset-0 z-50 bg-black/60 backdrop-blur-sm flex items-center justify-center" className="fixed inset-0 z-50 bg-black/60 backdrop-blur-sm flex items-center justify-center"
onClick={(e) => { if (e.target === e.currentTarget) onClose() }} onClick={(e) => { if (e.target === e.currentTarget) onClose() }}
> >
<div className="bg-white dark:bg-[#141414] rounded-2xl p-6 w-full max-w-md border border-[#E0E0E0] dark:border-[#CC0000]/20 shadow-[0_8px_40px_rgba(0,0,0,0.18)] relative"> <div className="bg-white dark:bg-[#141414] rounded-2xl p-6 w-full max-w-md border border-[#D4D8CC] dark:border-[#CC0000]/20 shadow-[0_8px_40px_rgba(0,0,0,0.18)] relative">
<button <button
type="button" type="button"
onClick={onClose} onClick={onClose}
className="absolute top-4 right-4 text-[#888888] hover:text-[#111111] dark:hover:text-white transition-colors" className="absolute top-4 right-4 text-[#8A9078] hover:text-[#2D3020] dark:hover:text-white transition-colors"
> >
<X className="h-5 w-5" /> <X className="h-5 w-5" />
</button> </button>
@@ -174,27 +174,27 @@ export default function VoiceCallModal({ open, onClose }: VoiceCallModalProps) {
{callLink ? ( {callLink ? (
shareSent ? ( shareSent ? (
<> <>
<p className="text-sm text-[#00AA00] font-semibold text-center mb-3">Call link copied.</p> <p className="text-sm text-[#4A9078] font-semibold text-center mb-3">Call link copied.</p>
<h2 className="font-bold text-lg text-[#111111] dark:text-white">Waiting for participant</h2> <h2 className="font-bold text-lg text-[#2D3020] dark:text-white">Waiting for participant</h2>
<p className="text-[#888888] text-sm mt-1 mb-4"> <p className="text-[#8A9078] text-sm mt-1 mb-4">
The recipient must receive and open the call link. The recipient must receive and open the call link.
</p> </p>
<div className="bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-xl p-3 mb-4 break-all text-xs text-[#111111] dark:text-white"> <div className="bg-card dark:bg-[#1A1A1A] rounded-xl p-3 mb-4 break-all text-xs text-foreground dark:text-white">
{callLink} {callLink}
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
<button <button
onClick={copyLink} onClick={copyLink}
className="flex-1 flex items-center justify-center gap-2 bg-[#444444] hover:bg-[#555555] dark:bg-[#333333] dark:hover:bg-[#444444] text-white font-semibold text-sm rounded-xl py-2.5 transition-all duration-200" className="flex-1 flex items-center justify-center gap-2 bg-[#8A9078] hover:bg-[#8A9078]/80 dark:bg-[#333333] dark:hover:bg-[#444444] text-white font-semibold text-sm rounded-xl py-2.5 transition-all duration-200"
> >
<Copy className="h-4 w-4" /> <Copy className="h-4 w-4" />
Copy URL Copy URL
</button> </button>
<button <button
onClick={() => { setShareSent(false); setShareError(null) }} onClick={() => { setShareSent(false); setShareError(null) }}
className="flex-1 flex items-center justify-center gap-2 bg-[#CC0000] hover:bg-[#990000] dark:bg-[#FF1111] dark:hover:bg-[#CC0000] text-white font-semibold text-sm rounded-xl py-2.5 transition-all duration-200" className="flex-1 flex items-center justify-center gap-2 bg-[#C84B4B] hover:bg-[#C84B4B]/80 dark:bg-[#FF1111] dark:hover:bg-[#CC0000] text-white font-semibold text-sm rounded-xl py-2.5 transition-all duration-200"
> >
<MessageSquare className="h-4 w-4" /> <MessageSquare className="h-4 w-4" />
WhatsApp WhatsApp
@@ -205,24 +205,24 @@ export default function VoiceCallModal({ open, onClose }: VoiceCallModalProps) {
href={callLink} href={callLink}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="block w-full mt-4 bg-[#444444] hover:bg-[#555555] dark:bg-[#333333] dark:hover:bg-[#444444] text-white font-semibold text-sm rounded-xl py-2.5 text-center transition-all duration-200" className="block w-full mt-4 bg-muted-foreground hover:bg-muted-foreground/80 dark:bg-[#333333] dark:hover:bg-[#444444] text-white font-semibold text-sm rounded-xl py-2.5 text-center transition-all duration-200"
> >
Open Call Open Call
</a> </a>
</> </>
) : ( ) : (
<> <>
<h2 className="font-bold text-lg text-[#111111] dark:text-white">Choose how to notify this person</h2> <h2 className="font-bold text-lg text-[#2D3020] dark:text-white">Choose how to notify this person</h2>
<p className="text-[#888888] text-sm mt-1 mb-4"> <p className="text-[#8A9078] text-sm mt-1 mb-4">
Select a method to send the call link Select a method to send the call link
</p> </p>
<div className="bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-xl p-3 mb-4 break-all text-xs text-[#111111] dark:text-white"> <div className="bg-[#FAFAF6] dark:bg-[#1A1A1A] rounded-xl p-3 mb-4 break-all text-xs text-[#2D3020] dark:text-white">
{callLink} {callLink}
</div> </div>
{showWaUrl && ( {showWaUrl && (
<div className="bg-[#1A1A1A] dark:bg-[#000000] rounded-xl p-2 mb-3 break-all text-[10px] text-[#00AA00] font-mono"> <div className="bg-card dark:bg-[#000000] rounded-xl p-2 mb-3 break-all text-[10px] text-[#4A9078] font-mono">
{showWaUrl} {showWaUrl}
</div> </div>
)} )}
@@ -237,7 +237,7 @@ export default function VoiceCallModal({ open, onClose }: VoiceCallModalProps) {
</button> </button>
<button <button
onClick={copyLink} onClick={copyLink}
className="flex items-center justify-center gap-2 bg-[#444444] hover:bg-[#555555] dark:bg-[#333333] dark:hover:bg-[#444444] text-white font-semibold text-sm rounded-xl py-2.5 transition-all duration-200" className="flex items-center justify-center gap-2 bg-[#8A9078] hover:bg-[#8A9078]/80 dark:bg-[#333333] dark:hover:bg-[#444444] text-white font-semibold text-sm rounded-xl py-2.5 transition-all duration-200"
> >
<Copy className="h-4 w-4" /> <Copy className="h-4 w-4" />
Copy URL Copy URL
@@ -245,16 +245,16 @@ export default function VoiceCallModal({ open, onClose }: VoiceCallModalProps) {
</div> </div>
{shareError && ( {shareError && (
<p className="text-[#CC0000] text-xs text-center">{shareError}</p> <p className="text-[#C84B4B] text-xs text-center">{shareError}</p>
)} )}
</> </>
) )
) : ( ) : (
<> <>
<h2 className="font-bold text-lg text-[#111111] dark:text-white">Start a Call</h2> <h2 className="font-bold text-lg text-[#2D3020] dark:text-white">Start a Call</h2>
<p className="text-[#888888] text-sm mt-1 mb-5">Search contacts or dial a number</p> <p className="text-[#8A9078] text-sm mt-1 mb-5">Search contacts or dial a number</p>
<p className="text-[10px] font-bold uppercase tracking-widest text-[#888888] dark:text-[#666666] mb-2"> <p className="text-[10px] font-bold uppercase tracking-widest text-[#8A9078] dark:text-[#666666] mb-2">
CONTACTS CONTACTS
</p> </p>
@@ -262,25 +262,25 @@ export default function VoiceCallModal({ open, onClose }: VoiceCallModalProps) {
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search contacts..." placeholder="Search contacts..."
className="bg-[#F5F5F5] dark:bg-[#1A1A1A] border border-[#E0E0E0] dark:border-[#333333] text-[#111111] dark:text-white placeholder-[#AAAAAA] dark:placeholder-[#555555] rounded-xl px-4 py-2.5 text-sm w-full mb-3 outline-none transition-colors focus:border-[#CC0000] dark:focus:border-[#FF4444]" className="bg-[#FAFAF6] dark:bg-[#1A1A1A] border border-[#D4D8CC] dark:border-[#333333] text-[#2D3020] dark:text-white placeholder-[#8A9078] dark:placeholder-[#555555] rounded-xl px-4 py-2.5 text-sm w-full mb-3 outline-none transition-colors focus:border-[#C84B4B] dark:focus:border-[#FF4444]"
/> />
<div className="max-h-[200px] overflow-y-auto space-y-1 mb-4"> <div className="max-h-[200px] overflow-y-auto space-y-1 mb-4">
{contactsLoading && ( {contactsLoading && (
<p className="text-[#AAAAAA] text-sm text-center py-4 animate-pulse">Loading contacts...</p> <p className="text-[#8A9078] text-sm text-center py-4 animate-pulse">Loading contacts...</p>
)} )}
{contactsError && ( {contactsError && (
<p className="text-[#CC0000] text-sm text-center py-4">Could not load contacts</p> <p className="text-[#C84B4B] text-sm text-center py-4">Could not load contacts</p>
)} )}
{!contactsLoading && !contactsError && filteredContacts.length === 0 && ( {!contactsLoading && !contactsError && filteredContacts.length === 0 && (
<p className="text-[#AAAAAA] text-sm text-center py-4">No contacts found</p> <p className="text-[#8A9078] text-sm text-center py-4">No contacts found</p>
)} )}
{!contactsLoading && !contactsError && filteredContacts.map((contact) => ( {!contactsLoading && !contactsError && filteredContacts.map((contact) => (
<div <div
key={contact.id} key={contact.id}
className="flex items-center gap-3 px-3 py-2.5 rounded-xl cursor-pointer transition-all duration-150 hover:bg-[#F5F5F5] dark:hover:bg-[#1A1A1A]" className="flex items-center gap-3 px-3 py-2.5 rounded-xl cursor-pointer transition-all duration-150 hover:bg-[#FAFAF6] dark:hover:bg-[#1A1A1A]"
> >
<div className="w-9 h-9 rounded-full flex items-center justify-center text-sm font-bold shrink-0 bg-[#FFF0F0] dark:bg-[#CC0000]/15 text-[#CC0000] dark:text-[#FF4444]"> <div className="w-9 h-9 rounded-full flex items-center justify-center text-sm font-bold shrink-0 bg-[#FAEAEA] dark:bg-[#CC0000]/15 text-[#C84B4B] dark:text-[#FF4444]">
{contact.avatar_url ? ( {contact.avatar_url ? (
<img <img
src={contact.avatar_url} src={contact.avatar_url}
@@ -292,13 +292,13 @@ export default function VoiceCallModal({ open, onClose }: VoiceCallModalProps) {
)} )}
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<p className="text-sm font-medium text-[#111111] dark:text-white truncate">{contact.name}</p> <p className="text-sm font-medium text-[#2D3020] dark:text-white truncate">{contact.name}</p>
<p className="text-xs text-[#888888] dark:[#666666] truncate">{contact.phone}</p> <p className="text-xs text-[#8A9078] dark:[#666666] truncate">{contact.phone}</p>
</div> </div>
<button <button
type="button" type="button"
onClick={() => handleCall(contact.phone)} onClick={() => handleCall(contact.phone)}
className="shrink-0 text-[#CC0000] dark:text-[#FF4444] hover:opacity-80 transition-opacity" className="shrink-0 text-[#C84B4B] dark:text-[#FF4444] hover:opacity-80 transition-opacity"
> >
<Phone className="h-4 w-4" /> <Phone className="h-4 w-4" />
</button> </button>
@@ -307,12 +307,12 @@ export default function VoiceCallModal({ open, onClose }: VoiceCallModalProps) {
</div> </div>
<div className="flex items-center gap-3 my-4"> <div className="flex items-center gap-3 my-4">
<hr className="flex-1 border-t border-[#E0E0E0] dark:border-[#222222]" /> <hr className="flex-1 border-t border-[#D4D8CC] dark:border-[#222222]" />
<span className="text-xs text-[#AAAAAA] dark:text-[#555555]">OR</span> <span className="text-xs text-[#8A9078] dark:text-[#555555]">OR</span>
<hr className="flex-1 border-t border-[#E0E0E0] dark:border-[#222222]" /> <hr className="flex-1 border-t border-[#D4D8CC] dark:border-[#222222]" />
</div> </div>
<p className="text-[10px] font-bold uppercase tracking-widest text-[#888888] dark:text-[#666666] mb-2"> <p className="text-[10px] font-bold uppercase tracking-widest text-[#8A9078] dark:text-[#666666] mb-2">
DIAL A NUMBER DIAL A NUMBER
</p> </p>
@@ -321,10 +321,10 @@ export default function VoiceCallModal({ open, onClose }: VoiceCallModalProps) {
value={phoneNumber} value={phoneNumber}
onChange={(e) => { setPhoneNumber(e.target.value); setPhoneError(false) }} onChange={(e) => { setPhoneNumber(e.target.value); setPhoneError(false) }}
placeholder="+27 000 000 0000" placeholder="+27 000 000 0000"
className="bg-[#F5F5F5] dark:bg-[#1A1A1A] border border-[#E0E0E0] dark:border-[#333333] text-[#111111] dark:text-white placeholder-[#AAAAAA] dark:placeholder-[#555555] rounded-xl px-4 py-2.5 text-sm w-full outline-none transition-colors focus:border-[#CC0000] dark:focus:border-[#FF4444]" className="bg-[#FAFAF6] dark:bg-[#1A1A1A] border border-[#D4D8CC] dark:border-[#333333] text-[#2D3020] dark:text-white placeholder-[#8A9078] dark:placeholder-[#555555] rounded-xl px-4 py-2.5 text-sm w-full outline-none transition-colors focus:border-[#C84B4B] dark:focus:border-[#FF4444]"
/> />
{phoneError && ( {phoneError && (
<p className="text-[#CC0000] text-xs mt-1">Please enter a phone number</p> <p className="text-[#C84B4B] text-xs mt-1">Please enter a phone number</p>
)} )}
<button <button
@@ -337,7 +337,7 @@ export default function VoiceCallModal({ open, onClose }: VoiceCallModalProps) {
setPhoneError(false) setPhoneError(false)
handleCall(trimmed) handleCall(trimmed)
}} }}
className="w-full mt-3 bg-[#CC0000] hover:bg-[#990000] dark:bg-[#FF1111] dark:hover:bg-[#CC0000] text-white font-semibold text-sm rounded-xl py-2.5 flex items-center justify-center gap-2 transition-all duration-200" className="w-full mt-3 bg-[#C84B4B] hover:bg-[#C84B4B]/80 dark:bg-[#FF1111] dark:hover:bg-[#CC0000] text-white font-semibold text-sm rounded-xl py-2.5 flex items-center justify-center gap-2 transition-all duration-200"
> >
<Phone className="h-4 w-4" /> <Phone className="h-4 w-4" />
Call Now Call Now
@@ -21,14 +21,14 @@ export function CrossedLightsabers() {
<circle cx="100" cy="73" r="24" fill="url(#purpleGlow)" className="animate-pulse-intersection" /> <circle cx="100" cy="73" r="24" fill="url(#purpleGlow)" className="animate-pulse-intersection" />
<g className="animate-pulse-red"> <g className="animate-pulse-red">
<line x1="35" y1="145" x2="125" y2="45" strokeLinecap="round" opacity="0.12" className="stroke-[#dc2626] dark:stroke-[#ef4444] saber-outer" /> <line x1="35" y1="145" x2="125" y2="45" strokeLinecap="round" opacity="0.12" className="stroke-[#C84B4B] dark:stroke-[#ef4444] saber-outer" />
<line x1="35" y1="145" x2="125" y2="45" strokeLinecap="round" opacity="0.25" className="stroke-[#dc2626] dark:stroke-[#ef4444] saber-mid" /> <line x1="35" y1="145" x2="125" y2="45" strokeLinecap="round" opacity="0.25" className="stroke-[#C84B4B] dark:stroke-[#ef4444] saber-mid" />
<line x1="35" y1="145" x2="125" y2="45" strokeLinecap="round" className="stroke-[#f87171] dark:stroke-[#fca5a5] saber-core" /> <line x1="35" y1="145" x2="125" y2="45" strokeLinecap="round" className="stroke-[#f87171] dark:stroke-[#fca5a5] saber-core" />
</g> </g>
<g className="animate-pulse-blue"> <g className="animate-pulse-blue">
<line x1="165" y1="145" x2="75" y2="45" strokeLinecap="round" opacity="0.12" className="stroke-[#1d4ed8] dark:stroke-[#3b82f6] saber-blue-outer" /> <line x1="165" y1="145" x2="75" y2="45" strokeLinecap="round" opacity="0.12" className="stroke-[#5A8FC4] dark:stroke-[#3b82f6] saber-blue-outer" />
<line x1="165" y1="145" x2="75" y2="45" strokeLinecap="round" opacity="0.25" className="stroke-[#1d4ed8] dark:stroke-[#3b82f6] saber-blue-mid" /> <line x1="165" y1="145" x2="75" y2="45" strokeLinecap="round" opacity="0.25" className="stroke-[#5A8FC4] dark:stroke-[#3b82f6] saber-blue-mid" />
<line x1="165" y1="145" x2="75" y2="45" strokeLinecap="round" className="stroke-[#60a5fa] dark:stroke-[#93c5fd] saber-blue-core" /> <line x1="165" y1="145" x2="75" y2="45" strokeLinecap="round" className="stroke-[#60a5fa] dark:stroke-[#93c5fd] saber-blue-core" />
</g> </g>
@@ -16,8 +16,8 @@ interface LeadsPerMonthChartProps {
data: IntervalData[] data: IntervalData[]
} }
const NEW_LEADS = "#CC0000" const NEW_LEADS = "#C84B4B"
const CLOSED = "#0033CC" const CLOSED = "#5A8FC4"
export function LeadsPerMonthChart({ data: initialData }: LeadsPerMonthChartProps) { export function LeadsPerMonthChart({ data: initialData }: LeadsPerMonthChartProps) {
const [year, setYear] = useState(new Date().getFullYear()) const [year, setYear] = useState(new Date().getFullYear())
@@ -153,11 +153,11 @@ export function LeadsPerMonthChart({ data: initialData }: LeadsPerMonthChartProp
> >
<defs> <defs>
<linearGradient id="newLeadsGrad" x1="0" y1="0" x2="0" y2="1"> <linearGradient id="newLeadsGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#FF1111" /> <stop offset="0%" stopColor="#C84B4B" />
<stop offset="100%" stopColor={NEW_LEADS} /> <stop offset="100%" stopColor={NEW_LEADS} />
</linearGradient> </linearGradient>
<linearGradient id="closedGrad" x1="0" y1="0" x2="0" y2="1"> <linearGradient id="closedGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#1144FF" /> <stop offset="0%" stopColor="#5A8FC4" />
<stop offset="100%" stopColor={CLOSED} /> <stop offset="100%" stopColor={CLOSED} />
</linearGradient> </linearGradient>
<filter id="shadowNew"> <filter id="shadowNew">
@@ -23,10 +23,10 @@ export function StatCardSkeleton() {
return ( return (
<div className="col-span-full flex flex-col items-center justify-center py-20"> <div className="col-span-full flex flex-col items-center justify-center py-20">
<p className="text-[#444444] dark:text-[#AAAAAA] text-sm"> <p className="text-[#8A9078] dark:text-[#AAAAAA] text-sm">
Loading your data... Loading your data...
</p> </p>
<div className="w-48 h-1 rounded-full overflow-hidden bg-[#E0E0E0] dark:bg-[#222222] mt-4"> <div className="w-48 h-1 rounded-full overflow-hidden bg-[#D4D8CC] dark:bg-[#222222] mt-4">
<div className="w-full h-full rounded-full bg-sidebar animate-[loading_1.5s_ease-in-out_infinite]" /> <div className="w-full h-full rounded-full bg-sidebar animate-[loading_1.5s_ease-in-out_infinite]" />
</div> </div>
</div> </div>
+18 -14
View File
@@ -3,6 +3,7 @@
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
import { motion } from "framer-motion" import { motion } from "framer-motion"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { useWebsiteTheme } from "@/providers/website-theme-provider"
import { Card, CardContent } from "@/components/ui/card" import { Card, CardContent } from "@/components/ui/card"
import { LucideIcon } from "lucide-react" import { LucideIcon } from "lucide-react"
@@ -19,12 +20,12 @@ interface StatCardProps {
} }
const cardColors: Record<string, { bg: string; text: string; glow: string; accent: string }> = { const cardColors: Record<string, { bg: string; text: string; glow: string; accent: string }> = {
"Total Leads": { bg: "bg-[#CC0000]/10", text: "text-[#CC0000] dark:text-[#FF1111]", glow: "via-[rgba(204,0,0,0.25)]", accent: "#CC0000" }, "Total Leads": { bg: "bg-[#C84B4B]/10", text: "text-[#C84B4B] dark:text-[#FF1111]", glow: "via-[rgba(200,75,75,0.25)]", accent: "#C84B4B" },
"Open Leads": { bg: "bg-[#0033CC]/10", text: "text-[#0033CC] dark:text-[#1144FF]", glow: "via-[rgba(0,51,204,0.25)]", accent: "#0033CC" }, "Open Leads": { bg: "bg-[#5A8FC4]/10", text: "text-[#5A8FC4] dark:text-[#1144FF]", glow: "via-[rgba(90,143,196,0.25)]", accent: "#5A8FC4" },
"Contacted": { bg: "bg-[#CC0000]/10", text: "text-[#CC0000] dark:text-[#FF1111]", glow: "via-[rgba(204,0,0,0.25)]", accent: "#CC0000" }, "Contacted": { bg: "bg-[#C84B4B]/10", text: "text-[#C84B4B] dark:text-[#FF1111]", glow: "via-[rgba(200,75,75,0.25)]", accent: "#C84B4B" },
"Pending": { bg: "bg-[#0033CC]/10", text: "text-[#0033CC] dark:text-[#1144FF]", glow: "via-[rgba(0,51,204,0.25)]", accent: "#0033CC" }, "Pending": { bg: "bg-[#5A8FC4]/10", text: "text-[#5A8FC4] dark:text-[#1144FF]", glow: "via-[rgba(90,143,196,0.25)]", accent: "#5A8FC4" },
"Closed": { bg: "bg-[#CC0000]/10", text: "text-[#CC0000] dark:text-[#FF1111]", glow: "via-[rgba(204,0,0,0.25)]", accent: "#CC0000" }, "Closed": { bg: "bg-[#C84B4B]/10", text: "text-[#C84B4B] dark:text-[#FF1111]", glow: "via-[rgba(200,75,75,0.25)]", accent: "#C84B4B" },
"Conversion Rate": { bg: "bg-[#0033CC]/10", text: "text-[#0033CC] dark:text-[#1144FF]", glow: "via-[rgba(0,51,204,0.25)]", accent: "#0033CC" }, "Conversion Rate": { bg: "bg-[#5A8FC4]/10", text: "text-[#5A8FC4] dark:text-[#1144FF]", glow: "via-[rgba(90,143,196,0.25)]", accent: "#5A8FC4" },
} }
function computeGoal(max: number): number { function computeGoal(max: number): number {
@@ -50,6 +51,7 @@ function smoothPath(points: { x: number; y: number }[]): string {
} }
export function StatCard({ title, value, icon: Icon, description, index = 0, trend, sparklineField, monthlyBreakdown, conversionRate }: StatCardProps) { export function StatCard({ title, value, icon: Icon, description, index = 0, trend, sparklineField, monthlyBreakdown, conversionRate }: StatCardProps) {
const { websiteTheme } = useWebsiteTheme()
const color = cardColors[title] ?? cardColors["Total Leads"] const color = cardColors[title] ?? cardColors["Total Leads"]
const isNumeric = typeof value === "number" const isNumeric = typeof value === "number"
@@ -108,8 +110,8 @@ export function StatCard({ title, value, icon: Icon, description, index = 0, tre
const sparkColor = color.accent const sparkColor = color.accent
const isRed = index % 2 === 0 const isRed = index % 2 === 0
const stripColor = isRed ? "from-[#CC0000] via-[#FF1111] to-[#CC0000]" : "from-[#0033CC] via-[#1144FF] to-[#0033CC]" const stripColor = isRed ? "from-[#C84B4B] via-[#C84B4B] to-[#C84B4B]" : "from-[#5A8FC4] via-[#5A8FC4] to-[#5A8FC4]"
const stripGlow = isRed ? "shadow-[#CC0000]/20" : "shadow-[#0033CC]/20" const stripGlow = isRed ? "shadow-[#C84B4B]/20" : "shadow-[#5A8FC4]/20"
return ( return (
<motion.div <motion.div
@@ -119,10 +121,11 @@ export function StatCard({ title, value, icon: Icon, description, index = 0, tre
className="relative" className="relative"
> >
<Card className="group h-full hover:shadow-xl transition-all duration-200 relative z-10 overflow-hidden"> <Card className="group h-full hover:shadow-xl transition-all duration-200 relative z-10 overflow-hidden">
{/* Red/Blue gradient top strip */} {websiteTheme === "spidey" && (
<div className={`h-1 w-full bg-gradient-to-r ${stripColor} ${stripGlow} shadow-sm`} /> <div className={`h-1 w-full bg-gradient-to-r ${stripColor} ${stripGlow} shadow-sm`} />
)}
{/* Action lines on hover */} {websiteTheme === "spidey" && (
<div <div
className="absolute inset-0 pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-0" className="absolute inset-0 pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-0"
style={{ style={{
@@ -131,16 +134,17 @@ export function StatCard({ title, value, icon: Icon, description, index = 0, tre
backgroundPosition: "center", backgroundPosition: "center",
}} }}
/> />
)}
<CardContent className="p-6 flex flex-col relative z-[1]"> <CardContent className="p-6 flex flex-col relative z-[1]">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="space-y-1"> <div className="space-y-1">
<p className="text-sm font-medium text-muted-foreground">{title}</p> <p className="text-sm font-medium text-muted-foreground">{title}</p>
<p className="text-3xl font-bold tracking-tight text-[#111111] dark:text-white"> <p className="text-3xl font-bold tracking-tight text-[#2D3020] dark:text-white">
{isNumeric ? display : value} {isNumeric ? display : value}
</p> </p>
{description && ( {description && (
<p className="text-xs text-[#888888] dark:text-[#666666]">{description}</p> <p className="text-xs text-[#8A9078] dark:text-[#666666]">{description}</p>
)} )}
{trend && ( {trend && (
<span className={cn( <span className={cn(
@@ -196,9 +200,9 @@ export function StatCard({ title, value, icon: Icon, description, index = 0, tre
{title === "Conversion Rate" && conversionRate !== undefined && ( {title === "Conversion Rate" && conversionRate !== undefined && (
<div className="mt-3"> <div className="mt-3">
<div className="w-full h-1.5 bg-muted rounded-full overflow-hidden"> <div className="w-full h-1.5 bg-muted rounded-full overflow-hidden">
<div className="h-full rounded-full bg-gradient-to-r from-[#CC0000] to-[#0033CC]" style={{ width: `${Math.min(conversionRate, 100)}%` }} /> <div className="h-full rounded-full bg-gradient-to-r from-[#C84B4B] to-[#5A8FC4]" style={{ width: `${Math.min(conversionRate, 100)}%` }} />
</div> </div>
<p className="text-xs text-[#888888] dark:text-[#666666] mt-1">{conversionRate}% conversion rate</p> <p className="text-xs text-[#8A9078] dark:text-[#666666] mt-1">{conversionRate}% conversion rate</p>
</div> </div>
)} )}
</CardContent> </CardContent>
+10 -4
View File
@@ -2,6 +2,7 @@
import { useState, useEffect } from "react" import { useState, useEffect } from "react"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { useWebsiteTheme } from "@/providers/website-theme-provider"
import { motion, AnimatePresence } from "framer-motion" import { motion, AnimatePresence } from "framer-motion"
import { Sidebar } from "./sidebar" import { Sidebar } from "./sidebar"
import { Topbar } from "./topbar" import { Topbar } from "./topbar"
@@ -11,6 +12,7 @@ interface AppShellProps {
} }
export function AppShell({ children }: AppShellProps) { export function AppShell({ children }: AppShellProps) {
const { websiteTheme } = useWebsiteTheme()
const [sidebarCollapsed, setSidebarCollapsed] = useState(false) const [sidebarCollapsed, setSidebarCollapsed] = useState(false)
const [mobileOpen, setMobileOpen] = useState(false) const [mobileOpen, setMobileOpen] = useState(false)
const pathname = usePathname() const pathname = usePathname()
@@ -34,16 +36,18 @@ export function AppShell({ children }: AppShellProps) {
return ( return (
<div className="min-h-screen bg-background dark:bg-[#0A0A0A] relative overflow-hidden" <div className="min-h-screen bg-background dark:bg-[#0A0A0A] relative overflow-hidden"
style={{ style={websiteTheme === "spidey" ? {
backgroundImage: "radial-gradient(circle, #CC000010 1px, transparent 1px), radial-gradient(circle, #0033CC06 1px, transparent 1px)", backgroundImage: "radial-gradient(circle, #CC000010 1px, transparent 1px), radial-gradient(circle, #0033CC06 1px, transparent 1px)",
backgroundSize: "28px 28px, 14px 14px", backgroundSize: "28px 28px, 14px 14px",
backgroundPosition: "0 0, 7px 7px", backgroundPosition: "0 0, 7px 7px",
}} } : undefined}
> >
{/* Spider-Man top gradient bar */} {websiteTheme === "spidey" && (
<div className="fixed top-0 left-0 right-0 h-[3px] w-full bg-gradient-to-r from-[#CC0000] via-[#FFFFFF] to-[#0033CC] dark:from-[#FF1111] dark:via-[#FFFFFF] dark:to-[#1144FF] z-50" /> <div className="fixed top-0 left-0 right-0 h-[3px] w-full bg-gradient-to-r from-[#CC0000] via-[#FFFFFF] to-[#0033CC] dark:from-[#FF1111] dark:via-[#FFFFFF] dark:to-[#1144FF] z-50" />
)}
{/* Corner spider webs */} {websiteTheme === "spidey" && (
<>
<div className="hidden lg:block fixed top-0 left-0 pointer-events-none z-0 opacity-[0.15] dark:opacity-[0.25]"> <div className="hidden lg:block fixed top-0 left-0 pointer-events-none z-0 opacity-[0.15] dark:opacity-[0.25]">
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="0" y1="0" x2="180" y2="0" stroke="#CC0000" strokeWidth="0.8"/> <line x1="0" y1="0" x2="180" y2="0" stroke="#CC0000" strokeWidth="0.8"/>
@@ -78,6 +82,8 @@ export function AppShell({ children }: AppShellProps) {
<path d="M0,180 Q180,180 180,0" stroke="#CC0000" strokeWidth="0.8" fill="none"/> <path d="M0,180 Q180,180 180,0" stroke="#CC0000" strokeWidth="0.8" fill="none"/>
</svg> </svg>
</div> </div>
</>
)}
<Sidebar <Sidebar
collapsed={sidebarCollapsed} collapsed={sidebarCollapsed}
+3 -3
View File
@@ -8,17 +8,17 @@ export function CrmIcon() {
viewBox="0 0 64 64" viewBox="0 0 64 64"
aria-hidden="true" aria-hidden="true"
> >
<g className="fill-[#333333] dark:fill-white/[0.15] dark:stroke-[#e5e7eb]" style={{strokeWidth: "1.5px"}}> <g className="fill-[#2D3020] dark:fill-white/[0.15] dark:stroke-[#e5e7eb]" style={{strokeWidth: "1.5px"}}>
<path className="dark:[stroke-linejoin:round]" d="m10.35 35.62s-.75.14-1-1.48-.58-8.61-.3-8.87a10 10 0 0 1 2.07-.63s0-7.54 2.67-11.86 5.4-6.09 10.65-7.56a29.38 29.38 0 0 1 15.18.3c4.41 1.18 8.7 4.62 9.89 8.8a49.27 49.27 0 0 1 1.49 9.55 9.53 9.53 0 0 1 2.52.9c.17.33.43 9.12 0 9.38a1.6 1.6 0 0 1 -.87.23s7.86 18 7.59 19-12.93 6.36-30.1 6.23-26.05-5.41-26.36-6.11 6.57-17.88 6.57-17.88z" /> <path className="dark:[stroke-linejoin:round]" d="m10.35 35.62s-.75.14-1-1.48-.58-8.61-.3-8.87a10 10 0 0 1 2.07-.63s0-7.54 2.67-11.86 5.4-6.09 10.65-7.56a29.38 29.38 0 0 1 15.18.3c4.41 1.18 8.7 4.62 9.89 8.8a49.27 49.27 0 0 1 1.49 9.55 9.53 9.53 0 0 1 2.52.9c.17.33.43 9.12 0 9.38a1.6 1.6 0 0 1 -.87.23s7.86 18 7.59 19-12.93 6.36-30.1 6.23-26.05-5.41-26.36-6.11 6.57-17.88 6.57-17.88z" />
</g> </g>
<g className="fill-[#777777] dark:fill-white/[0.1] dark:stroke-[#d4d4d4]" style={{strokeWidth: "1px"}}> <g className="fill-[#8A9078] dark:fill-white/[0.1] dark:stroke-[#d4d4d4]" style={{strokeWidth: "1px"}}>
<path d="m13.24 27.92s-.6-7.65 1.3-11.91a19.6 19.6 0 0 1 7.75-8.23 20.47 20.47 0 0 1 6.51-1.45c.05.16.67 10.23.66 11.61a27 27 0 0 1 -.57 4s-4-2.44-8.16-1.38a10.47 10.47 0 0 0 -7.49 7.36z" /> <path d="m13.24 27.92s-.6-7.65 1.3-11.91a19.6 19.6 0 0 1 7.75-8.23 20.47 20.47 0 0 1 6.51-1.45c.05.16.67 10.23.66 11.61a27 27 0 0 1 -.57 4s-4-2.44-8.16-1.38a10.47 10.47 0 0 0 -7.49 7.36z" />
<path d="m30.11 22.18s.53-3.69.54-4.89-.8-10.82-.6-10.95a4.11 4.11 0 0 1 1.88 0c.07.16 1.07 11.66 1.28 13.22a9.33 9.33 0 0 1 .27 2.44 2.85 2.85 0 0 1 -3.37.18z" /> <path d="m30.11 22.18s.53-3.69.54-4.89-.8-10.82-.6-10.95a4.11 4.11 0 0 1 1.88 0c.07.16 1.07 11.66 1.28 13.22a9.33 9.33 0 0 1 .27 2.44 2.85 2.85 0 0 1 -3.37.18z" />
<path d="m34.8 21.44a76.48 76.48 0 0 1 -1.4-9.35c-.17-3.62-.51-5.65-.06-5.83s6.66.17 10.42 3.45 4.06 5.61 3.81 5.66-2.52-1-2.64-.77-.15.8.06.92 2.69 1.1 2.82 1.26a4.18 4.18 0 0 1 .28.95 16.94 16.94 0 0 0 -3-1.13c-.12.17-.23.92 0 1s3 1.3 3 1.3l.11.79s-2.83-1.18-3-1.14-.36.8-.15.92 3.16 1.26 3.25 1.43.41 1.36.41 1.36l-1.71-.54s.31.87.48 1 1.3.42 1.31.76a31.35 31.35 0 0 1 .2 3.33c-.12 0-2.2-6.45-5.21-7.09s-8.98 1.72-8.98 1.72z" /> <path d="m34.8 21.44a76.48 76.48 0 0 1 -1.4-9.35c-.17-3.62-.51-5.65-.06-5.83s6.66.17 10.42 3.45 4.06 5.61 3.81 5.66-2.52-1-2.64-.77-.15.8.06.92 2.69 1.1 2.82 1.26a4.18 4.18 0 0 1 .28.95 16.94 16.94 0 0 0 -3-1.13c-.12.17-.23.92 0 1s3 1.3 3 1.3l.11.79s-2.83-1.18-3-1.14-.36.8-.15.92 3.16 1.26 3.25 1.43.41 1.36.41 1.36l-1.71-.54s.31.87.48 1 1.3.42 1.31.76a31.35 31.35 0 0 1 .2 3.33c-.12 0-2.2-6.45-5.21-7.09s-8.98 1.72-8.98 1.72z" />
<path d="m22.44 22.28c2.21 0 7.19 2.45 9.76 1.84s6.89-2.79 9.51-2.81 4.07 2.48 6.36 7.84 7.14 17.34 7 17.34-8.1-17.89-9.66-20.23-2.66-3-5.86-2.77-6.81 1-6.85 1.41 0 7.17.49 7.75 3.06.71 6.18.51 5-.33 5.77-.64a10.14 10.14 0 0 0 1.69-1l7.79 16.48a30.15 30.15 0 0 0 -2.94-2.56c-.21.05-.31.8-.19.88s3.67 3.33 4 3.7a12.43 12.43 0 0 1 1.61 2.63c-.12.17-.45.55-.66.47s-5-5.71-5.39-5.83-.45.6-.45.6 5.18 5.5 5.06 5.63a8.62 8.62 0 0 1 -1.15.57s-4.34-5-4.5-4.94-.69.6-.4.81a45.71 45.71 0 0 1 4.07 4.36 7 7 0 0 1 -1.2.4 46.49 46.49 0 0 0 -3.58-3.72c-.25 0-.61.39-.49.55s3.34 3.29 3.22 3.46a4 4 0 0 1 -1.16.37s-2.47-2.59-2.65-2.59-.74.31-.53.6 2.39 2.19 2.27 2.28a4.86 4.86 0 0 1 -1 .19c-.13 0-2-2-2.22-2s-.78.19-.65.48 1.83 1.79 1.63 1.92-5.17 1.66-15.76 1.51-14.84-1.85-14.84-1.85a46 46 0 0 0 -4.22-3.69c-.37 0-.78.6-.61.72a29.31 29.31 0 0 1 2.86 2.6c-.16 0-3.27-.92-4.32-1.1a7.6 7.6 0 0 1 -2.26-.61c0-.13 6-14.45 6.27-14.41s4.29 10.07 4.29 10.07a2.77 2.77 0 0 0 1.2 2.89 3.9 3.9 0 0 0 3.49-.25l15.51.08c.12 0 2.65 1.27 3.69-.68s.44-2.67.44-2.67 4-11.27 3.76-11.52a5.63 5.63 0 0 0 -1.76-.35s-2.75 10.32-3 10.33-1.26-.3-1.68-.34a3.18 3.18 0 0 0 -.7 0l-5.48-7.62s1.1-6-3.28-6-3.14 6-3.14 6-1-.06-2 1.51-3.39 6.29-3.39 6.29a3.77 3.77 0 0 0 -1.45.2c-.66.27-1.07.49-1.07.49s-3.54-8.29-3.46-8.46.82-2.15 1.2-2.16 2.93.39 3.93.41 1.16-.11 1.2-.28.27-.8.1-.8-5.11-.57-5.48-.57-1.07 2.14-1.36 2.23-1.32.25-1.2 0 2.16-5.13 2.29-5.1 7.53 1.07 9.73.64 2.76-1.11 3.07-2.16a31.45 31.45 0 0 0 .25-6.59 19.1 19.1 0 0 0 -6.2-1.31c-2.79-.09-4.67-.13-5.87 1.82s-10.79 27.71-10.96 27.76a3.64 3.64 0 0 1 -.92-.29s4.91-14 6.78-19.26 3.47-11.56 9.47-11.46z" /> <path d="m22.44 22.28c2.21 0 7.19 2.45 9.76 1.84s6.89-2.79 9.51-2.81 4.07 2.48 6.36 7.84 7.14 17.34 7 17.34-8.1-17.89-9.66-20.23-2.66-3-5.86-2.77-6.81 1-6.85 1.41 0 7.17.49 7.75 3.06.71 6.18.51 5-.33 5.77-.64a10.14 10.14 0 0 0 1.69-1l7.79 16.48a30.15 30.15 0 0 0 -2.94-2.56c-.21.05-.31.8-.19.88s3.67 3.33 4 3.7a12.43 12.43 0 0 1 1.61 2.63c-.12.17-.45.55-.66.47s-5-5.71-5.39-5.83-.45.6-.45.6 5.18 5.5 5.06 5.63a8.62 8.62 0 0 1 -1.15.57s-4.34-5-4.5-4.94-.69.6-.4.81a45.71 45.71 0 0 1 4.07 4.36 7 7 0 0 1 -1.2.4 46.49 46.49 0 0 0 -3.58-3.72c-.25 0-.61.39-.49.55s3.34 3.29 3.22 3.46a4 4 0 0 1 -1.16.37s-2.47-2.59-2.65-2.59-.74.31-.53.6 2.39 2.19 2.27 2.28a4.86 4.86 0 0 1 -1 .19c-.13 0-2-2-2.22-2s-.78.19-.65.48 1.83 1.79 1.63 1.92-5.17 1.66-15.76 1.51-14.84-1.85-14.84-1.85a46 46 0 0 0 -4.22-3.69c-.37 0-.78.6-.61.72a29.31 29.31 0 0 1 2.86 2.6c-.16 0-3.27-.92-4.32-1.1a7.6 7.6 0 0 1 -2.26-.61c0-.13 6-14.45 6.27-14.41s4.29 10.07 4.29 10.07a2.77 2.77 0 0 0 1.2 2.89 3.9 3.9 0 0 0 3.49-.25l15.51.08c.12 0 2.65 1.27 3.69-.68s.44-2.67.44-2.67 4-11.27 3.76-11.52a5.63 5.63 0 0 0 -1.76-.35s-2.75 10.32-3 10.33-1.26-.3-1.68-.34a3.18 3.18 0 0 0 -.7 0l-5.48-7.62s1.1-6-3.28-6-3.14 6-3.14 6-1-.06-2 1.51-3.39 6.29-3.39 6.29a3.77 3.77 0 0 0 -1.45.2c-.66.27-1.07.49-1.07.49s-3.54-8.29-3.46-8.46.82-2.15 1.2-2.16 2.93.39 3.93.41 1.16-.11 1.2-.28.27-.8.1-.8-5.11-.57-5.48-.57-1.07 2.14-1.36 2.23-1.32.25-1.2 0 2.16-5.13 2.29-5.1 7.53 1.07 9.73.64 2.76-1.11 3.07-2.16a31.45 31.45 0 0 0 .25-6.59 19.1 19.1 0 0 0 -6.2-1.31c-2.79-.09-4.67-.13-5.87 1.82s-10.79 27.71-10.96 27.76a3.64 3.64 0 0 1 -.92-.29s4.91-14 6.78-19.26 3.47-11.56 9.47-11.46z" />
<path d="m28.76 40.3a14.69 14.69 0 0 0 2.25 0l2.29-.09s-.39 9.67 0 9.75.67.27.66-.06-.19-7.75.06-7.79.75 1.52.75 1.52-.14 6.42 0 6.46.88.27.84.06 0-4.75 0-4.75l1.43 2.26-.79.06s-.08 2 .17 2a4.75 4.75 0 0 0 .71 0c.12 0 .11 1.16.11 1.16s-7.26-.19-9.8-.13-2.74.11-2.74.11a6.75 6.75 0 0 0 -.55-2l-.62-1.28s4.86-7.15 5.23-7.28z" /> <path d="m28.76 40.3a14.69 14.69 0 0 0 2.25 0l2.29-.09s-.39 9.67 0 9.75.67.27.66-.06-.19-7.75.06-7.79.75 1.52.75 1.52-.14 6.42 0 6.46.88.27.84.06 0-4.75 0-4.75l1.43 2.26-.79.06s-.08 2 .17 2a4.75 4.75 0 0 0 .71 0c.12 0 .11 1.16.11 1.16s-7.26-.19-9.8-.13-2.74.11-2.74.11a6.75 6.75 0 0 0 -.55-2l-.62-1.28s4.86-7.15 5.23-7.28z" />
</g> </g>
<g className="fill-[#333333] dark:fill-white/[0.15] dark:stroke-[#d4d4d4]" style={{strokeWidth: "0.7px"}}> <g className="fill-[#2D3020] dark:fill-white/[0.15] dark:stroke-[#d4d4d4]" style={{strokeWidth: "0.7px"}}>
<path d="m30.57 41.13c.28-.1.79-.06.84.1s.41 8.41.17 8.63-.88.14-.88 0-.25-8.69-.13-8.73z" /> <path d="m30.57 41.13c.28-.1.79-.06.84.1s.41 8.41.17 8.63-.88.14-.88 0-.25-8.69-.13-8.73z" />
<path d="m28.74 41.26c.21-.09.7-.19.79 0s.55 8.61.38 8.66-.87.18-.87 0-.3-8.66-.3-8.66z" /> <path d="m28.74 41.26c.21-.09.7-.19.79 0s.55 8.61.38 8.66-.87.18-.87 0-.3-8.66-.3-8.66z" />
<path d="m27 44.8c.06-.14.87-.4.87-.23a52.12 52.12 0 0 1 .21 5.25 3 3 0 0 1 -.79.06s-.38-4.88-.29-5.08z" /> <path d="m27 44.8c.06-.14.87-.4.87-.23a52.12 52.12 0 0 1 .21 5.25 3 3 0 0 1 -.79.06s-.38-4.88-.29-5.08z" />
+1 -1
View File
@@ -4,7 +4,7 @@ export function SidebarNameLogo() {
return ( return (
<svg <svg
viewBox="0 0 192.756 192.756" viewBox="0 0 192.756 192.756"
className="fill-[#333333] dark:fill-[#e5e7eb] w-full h-auto" className="fill-[#2D3020] dark:fill-[#e5e7eb] w-full h-auto"
aria-hidden="true" aria-hidden="true"
> >
<g fill-rule="evenodd" clip-rule="evenodd"> <g fill-rule="evenodd" clip-rule="evenodd">
+5 -5
View File
@@ -73,7 +73,7 @@ export function Topbar({ onMenuClick }: TopbarProps) {
.toUpperCase(); .toUpperCase();
return ( return (
<header className="sticky top-0 z-20 flex h-16 items-center gap-4 border-b bg-card dark:bg-sidebar 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-sidebar dark:bg-sidebar 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">
{websiteTheme === "spidey" && <CaptainAmericaShield />} {websiteTheme === "spidey" && <CaptainAmericaShield />}
@@ -117,7 +117,7 @@ export function Topbar({ onMenuClick }: TopbarProps) {
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={() => setTheme(theme === "dark" ? "light" : "dark")} onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="text-muted-foreground" className="text-muted-foreground hover:bg-[#8b949e]/10 hover:text-[#8b949e]"
> >
{mounted ? ( {mounted ? (
theme === "dark" ? ( theme === "dark" ? (
@@ -135,7 +135,7 @@ export function Topbar({ onMenuClick }: TopbarProps) {
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={() => setBugModalOpen(true)} onClick={() => setBugModalOpen(true)}
className="text-muted-foreground" className="text-muted-foreground hover:bg-[#8b949e]/10 hover:text-[#8b949e]"
title="Report a Bug" title="Report a Bug"
> >
<Bug className="h-5 w-5" /> <Bug className="h-5 w-5" />
@@ -147,7 +147,7 @@ export function Topbar({ onMenuClick }: TopbarProps) {
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
className="relative text-muted-foreground" className="relative text-muted-foreground hover:bg-[#8b949e]/10 hover:text-[#8b949e]"
> >
<Bell className="h-5 w-5" /> <Bell className="h-5 w-5" />
<span className="absolute -right-0.5 -top-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground"> <span className="absolute -right-0.5 -top-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground">
@@ -217,7 +217,7 @@ export function Topbar({ onMenuClick }: TopbarProps) {
{/* User profile */} {/* User profile */}
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-9 gap-2 pl-2 pr-3"> <Button variant="ghost" className="relative h-9 gap-2 pl-2 pr-3 hover:bg-[#8b949e]/10 hover:text-[#8b949e]">
<Avatar className="h-7 w-7"> <Avatar className="h-7 w-7">
<AvatarImage src={user.avatar} /> <AvatarImage src={user.avatar} />
<AvatarFallback>{initials}</AvatarFallback> <AvatarFallback>{initials}</AvatarFallback>
+2 -1
View File
@@ -6,7 +6,7 @@ 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, Shield } from "lucide-react" import { Sun, Moon, Monitor, Palette, Shield } from "lucide-react"
import { useWebsiteTheme } from "@/providers/website-theme-provider" import { useWebsiteTheme } from "@/providers/website-theme-provider"
const COLOR_THEME_KEY = "crm-color-theme" const COLOR_THEME_KEY = "crm-color-theme"
@@ -31,6 +31,7 @@ const themeOptions = [
] ]
const backgroundOptions = [ const backgroundOptions = [
{ value: "", label: "Default", icon: Palette, color: "bg-transparent", ring: "ring-border", desc: "Clean light/dark mode" },
{ value: "spidey", label: "Spidey", icon: Shield, color: "bg-red-600", ring: "ring-red-600", desc: "Dark theme with red accents" }, { value: "spidey", label: "Spidey", icon: Shield, color: "bg-red-600", ring: "ring-red-600", desc: "Dark theme with red accents" },
] ]
+19 -19
View File
@@ -75,7 +75,7 @@ export function BugReportModal({ open, onClose }: BugReportModalProps) {
> >
<button <button
onClick={handleClose} onClick={handleClose}
className="absolute right-4 top-4 text-[#888888] hover:text-[#111111] dark:hover:text-white transition-colors" className="absolute right-4 top-4 text-[#8A9078] hover:text-[#2D3020] dark:hover:text-white transition-colors"
> >
<X className="h-5 w-5" /> <X className="h-5 w-5" />
</button> </button>
@@ -83,15 +83,15 @@ export function BugReportModal({ open, onClose }: BugReportModalProps) {
{submitted ? ( {submitted ? (
<div className="flex flex-col items-center py-8 text-center"> <div className="flex flex-col items-center py-8 text-center">
<CheckCircle className="h-12 w-12 text-emerald-500 mb-4" /> <CheckCircle className="h-12 w-12 text-emerald-500 mb-4" />
<h3 className="text-lg font-semibold text-[#111111] dark:text-white mb-2"> <h3 className="text-lg font-semibold text-[#2D3020] dark:text-white mb-2">
Bug Report Submitted Bug Report Submitted
</h3> </h3>
<p className="text-sm text-[#666666] dark:text-[#AAAAAA]"> <p className="text-sm text-[#8A9078] dark:text-[#AAAAAA]">
Thank you for your report. The development team will investigate. Thank you for your report. The development team will investigate.
</p> </p>
<button <button
onClick={handleClose} onClick={handleClose}
className="mt-6 rounded-lg bg-[#CC0000] px-6 py-2 text-sm font-medium text-white hover:bg-[#AA0000] transition-colors" className="mt-6 rounded-lg bg-[#C84B4B] px-6 py-2 text-sm font-medium text-white hover:bg-[#C84B4B]/80 transition-colors"
> >
Done Done
</button> </button>
@@ -99,14 +99,14 @@ export function BugReportModal({ open, onClose }: BugReportModalProps) {
) : ( ) : (
<> <>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-red-100 dark:bg-red-900/30"> <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-[#FAEAEA] dark:bg-red-900/30">
<Bug className="h-5 w-5 text-[#CC0000]" /> <Bug className="h-5 w-5 text-[#C84B4B]" />
</div> </div>
<div> <div>
<h3 className="text-lg font-semibold text-[#111111] dark:text-white"> <h3 className="text-lg font-semibold text-[#2D3020] dark:text-white">
Report a Bug Report a Bug
</h3> </h3>
<p className="text-xs text-[#666666] dark:text-[#AAAAAA]"> <p className="text-xs text-[#8A9078] dark:text-[#AAAAAA]">
Help us improve the system Help us improve the system
</p> </p>
</div> </div>
@@ -120,8 +120,8 @@ export function BugReportModal({ open, onClose }: BugReportModalProps) {
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<div> <div>
<label className="mb-1.5 block text-sm font-medium text-[#444444] dark:text-[#CCCCCC]"> <label className="mb-1.5 block text-sm font-medium text-[#8A9078] dark:text-[#CCCCCC]">
Title <span className="text-[#CC0000]">*</span> Title <span className="text-[#C84B4B]">*</span>
</label> </label>
<input <input
type="text" type="text"
@@ -129,13 +129,13 @@ export function BugReportModal({ open, onClose }: BugReportModalProps) {
onChange={(e) => setTitle(e.target.value)} onChange={(e) => setTitle(e.target.value)}
placeholder="Brief description of the issue" placeholder="Brief description of the issue"
required required
className="w-full rounded-lg border border-[#E0E0E0] dark:border-[#333333] bg-white dark:bg-[#1E1E1E] px-3 py-2 text-sm text-[#111111] dark:text-white placeholder-[#999999] focus:border-[#CC0000] focus:outline-none focus:ring-1 focus:ring-[#CC0000]" className="w-full rounded-lg border border-[#D4D8CC] dark:border-[#333333] bg-white dark:bg-[#1E1E1E] px-3 py-2 text-sm text-[#2D3020] dark:text-white placeholder-[#8A9078] focus:border-[#C84B4B] focus:outline-none focus:ring-1 focus:ring-[#C84B4B]"
/> />
</div> </div>
<div> <div>
<label className="mb-1.5 block text-sm font-medium text-[#444444] dark:text-[#CCCCCC]"> <label className="mb-1.5 block text-sm font-medium text-[#8A9078] dark:text-[#CCCCCC]">
Description <span className="text-[#CC0000]">*</span> Description <span className="text-[#C84B4B]">*</span>
</label> </label>
<textarea <textarea
value={description} value={description}
@@ -143,18 +143,18 @@ export function BugReportModal({ open, onClose }: BugReportModalProps) {
placeholder="What happened? What did you expect?" placeholder="What happened? What did you expect?"
rows={4} rows={4}
required required
className="w-full resize-none rounded-lg border border-[#E0E0E0] dark:border-[#333333] bg-white dark:bg-[#1E1E1E] px-3 py-2 text-sm text-[#111111] dark:text-white placeholder-[#999999] focus:border-[#CC0000] focus:outline-none focus:ring-1 focus:ring-[#CC0000]" className="w-full resize-none rounded-lg border border-[#D4D8CC] dark:border-[#333333] bg-white dark:bg-[#1E1E1E] px-3 py-2 text-sm text-[#2D3020] dark:text-white placeholder-[#8A9078] focus:border-[#C84B4B] focus:outline-none focus:ring-1 focus:ring-[#C84B4B]"
/> />
</div> </div>
<div> <div>
<label className="mb-1.5 block text-sm font-medium text-[#444444] dark:text-[#CCCCCC]"> <label className="mb-1.5 block text-sm font-medium text-[#8A9078] dark:text-[#CCCCCC]">
Severity Severity
</label> </label>
<select <select
value={severity} value={severity}
onChange={(e) => setSeverity(e.target.value)} onChange={(e) => setSeverity(e.target.value)}
className="w-full rounded-lg border border-[#E0E0E0] dark:border-[#333333] bg-white dark:bg-[#1E1E1E] px-3 py-2 text-sm text-[#111111] dark:text-white focus:border-[#CC0000] focus:outline-none focus:ring-1 focus:ring-[#CC0000]" className="w-full rounded-lg border border-[#D4D8CC] dark:border-[#333333] bg-white dark:bg-[#1E1E1E] px-3 py-2 text-sm text-[#2D3020] dark:text-white focus:border-[#C84B4B] focus:outline-none focus:ring-1 focus:ring-[#C84B4B]"
> >
<option value="low">Low Minor cosmetic issue</option> <option value="low">Low Minor cosmetic issue</option>
<option value="medium">Medium Affects functionality</option> <option value="medium">Medium Affects functionality</option>
@@ -163,8 +163,8 @@ export function BugReportModal({ open, onClose }: BugReportModalProps) {
</select> </select>
</div> </div>
<div className="rounded-lg bg-[#F5F5F5] dark:bg-[#1A1A1A] px-3 py-2"> <div className="rounded-lg bg-[#FAFAF6] dark:bg-[#1A1A1A] px-3 py-2">
<p className="text-xs text-[#888888]"> <p className="text-xs text-[#8A9078]">
<span className="font-medium">Page:</span> {typeof window !== "undefined" ? window.location.href : ""} <span className="font-medium">Page:</span> {typeof window !== "undefined" ? window.location.href : ""}
</p> </p>
</div> </div>
@@ -172,7 +172,7 @@ export function BugReportModal({ open, onClose }: BugReportModalProps) {
<button <button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="flex w-full items-center justify-center gap-2 rounded-lg bg-[#CC0000] px-4 py-2.5 text-sm font-medium text-white hover:bg-[#AA0000] disabled:opacity-50 transition-colors" className="flex w-full items-center justify-center gap-2 rounded-lg bg-[#C84B4B] px-4 py-2.5 text-sm font-medium text-white hover:bg-[#C84B4B]/80 disabled:opacity-50 transition-colors"
> >
{loading && <Loader2 className="h-4 w-4 animate-spin" />} {loading && <Loader2 className="h-4 w-4 animate-spin" />}
{loading ? "Submitting..." : "Submit Bug Report"} {loading ? "Submitting..." : "Submit Bug Report"}
+2 -2
View File
@@ -19,8 +19,8 @@ export function PageHeader({ title, description, children, className }: PageHead
className={cn("flex items-center justify-between pb-6", className)} className={cn("flex items-center justify-between pb-6", className)}
> >
<div> <div>
<h1 className="text-2xl font-bold tracking-tight border-l-4 border-[#CC0000] dark:border-[#FF1111] pl-4 text-[#111111] dark:text-white">{title}</h1> <h1 className="text-2xl font-bold tracking-tight border-l-4 border-[#C84B4B] dark:border-[#FF1111] pl-4 text-[#2D3020] dark:text-white">{title}</h1>
{description && <p className="text-sm text-[#444444] dark:text-[#AAAAAA] mt-1">{description}</p>} {description && <p className="text-sm text-[#8A9078] dark:text-[#AAAAAA] mt-1">{description}</p>}
</div> </div>
{children && <div className="flex items-center gap-3">{children}</div>} {children && <div className="flex items-center gap-3">{children}</div>}
</motion.div> </motion.div>
+4 -4
View File
@@ -30,11 +30,11 @@ export function getDashboardStats(period: string): DashboardStats {
}) })
return [ return [
{ name: "Open", value: statusCounts.open, color: "#CC0000" }, { name: "Open", value: statusCounts.open, color: "#C84B4B" },
{ name: "Contacted", value: statusCounts.contacted, color: "#0033CC" }, { name: "Contacted", value: statusCounts.contacted, color: "#5A8FC4" },
{ name: "Pending", value: statusCounts.pending, color: "#FFFFFF" }, { name: "Pending", value: statusCounts.pending, color: "#FFFFFF" },
{ name: "Closed", value: statusCounts.closed, color: "#0033CC" }, { name: "Closed", value: statusCounts.closed, color: "#5A8FC4" },
{ name: "Ignored", value: statusCounts.ignored, color: "#CC0000" }, { name: "Ignored", value: statusCounts.ignored, color: "#C84B4B" },
] ]
} }
+1 -1
View File
@@ -74,7 +74,7 @@ function buildEmail(event: EventConfirmationInput, variant: "scheduled" | "resch
<html><head><meta charset="utf-8"></head> <html><head><meta charset="utf-8"></head>
<body style="font-family:Arial,Helvetica,sans-serif;background:#f4f4f5;padding:40px 20px"> <body style="font-family:Arial,Helvetica,sans-serif;background:#f4f4f5;padding:40px 20px">
<div style="max-width:520px;margin:0 auto;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,0.08)"> <div style="max-width:520px;margin:0 auto;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,0.08)">
<div style="background:linear-gradient(135deg,#CC0000,#0033CC);padding:24px 32px"> <div style="background:linear-gradient(135deg,#C84B4B,#5A8FC4);padding:24px 32px">
<h1 style="color:#fff;margin:0;font-size:20px">${heading}</h1> <h1 style="color:#fff;margin:0;font-size:20px">${heading}</h1>
</div> </div>
<div style="padding:32px"> <div style="padding:32px">
+3 -3
View File
@@ -38,7 +38,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>("")
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
useEffect(() => { useEffect(() => {
@@ -52,13 +52,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 || ""
setWebsiteThemeState(theme) setWebsiteThemeState(theme)
storeTheme(theme) storeTheme(theme)
applyWebsiteTheme(theme) applyWebsiteTheme(theme)
}) })
.catch(() => { .catch(() => {
applyWebsiteTheme("spidey") applyWebsiteTheme("")
}) })
.finally(() => setLoading(false)) .finally(() => setLoading(false))
}, []) }, [])
+6
View File
@@ -53,6 +53,12 @@ const config: Config = {
border: "hsl(var(--sidebar-border))", border: "hsl(var(--sidebar-border))",
ring: "hsl(var(--sidebar-ring))", ring: "hsl(var(--sidebar-ring))",
}, },
blue: "hsl(var(--color-blue))",
teal: "hsl(var(--color-teal))",
"red-tint": "hsl(var(--color-red-tint))",
"blue-tint": "hsl(var(--color-blue-tint))",
"teal-tint": "hsl(var(--color-teal-tint))",
avatar: "hsl(var(--color-avatar))",
}, },
borderRadius: { borderRadius: {
lg: "var(--radius)", lg: "var(--radius)",