Fixed Themes not showing

This commit is contained in:
JCBSComputer
2026-06-30 16:48:59 +02:00
parent feb2549373
commit 4eba6fe476
7 changed files with 112 additions and 7 deletions
+40
View File
@@ -929,3 +929,43 @@
.dark.theme-cyberpunk .dark\:text-\[\#666666\].text-xs.font-semibold.tracking-widest.uppercase { .dark.theme-cyberpunk .dark\:text-\[\#666666\].text-xs.font-semibold.tracking-widest.uppercase {
color: hsl(210, 20%, 88%) !important; color: hsl(210, 20%, 88%) !important;
} }
/* ============================================================================
Pipeline Color Bars — Cyberpunk neon pink/cyan
============================================================================ */
.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .h-1.w-full {
background: linear-gradient(to right, hsl(330, 100%, 50%), hsl(330, 100%, 65%), hsl(330, 100%, 50%)) !important;
box-shadow: 0 0 8px rgba(255, 0, 127, 0.3) !important;
}
.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .h-1.w-full {
background: linear-gradient(to right, hsl(180, 100%, 50%), hsl(180, 100%, 65%), hsl(180, 100%, 50%)) !important;
box-shadow: 0 0 8px rgba(0, 240, 255, 0.3) !important;
}
.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .rounded-xl.shrink-0 {
background: hsla(330, 100%, 50%, 0.12) !important;
}
.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .rounded-xl.shrink-0 {
background: hsla(180, 100%, 50%, 0.1) !important;
}
.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .rounded-xl.shrink-0 svg {
color: hsl(330, 100%, 50%) !important;
}
.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .rounded-xl.shrink-0 svg {
color: hsl(180, 100%, 50%) !important;
}
.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .absolute.bottom-0.h-1 {
background: linear-gradient(to right, transparent, hsla(330, 100%, 50%, 0.3), transparent) !important;
}
.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .absolute.bottom-0.h-1 {
background: linear-gradient(to right, transparent, hsla(180, 100%, 50%, 0.25), transparent) !important;
}
.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 .rounded-full.bg-gradient-to-r {
background: linear-gradient(to right, hsl(330, 100%, 50%), hsl(180, 100%, 50%)) !important;
}
.dark.theme-cyberpunk .growth-word,
.light.theme-cyberpunk .growth-word {
-webkit-text-fill-color: #1BB0CE !important;
color: #1BB0CE !important;
background: none !important;
}
+1
View File
@@ -1,4 +1,5 @@
@import url("./cosmic-theme.css"); @import url("./cosmic-theme.css");
@import url("./cyberpunk-theme.css");
@import url("./cyber2-theme.css"); @import url("./cyber2-theme.css");
@import url("./pumpkin-theme.css"); @import url("./pumpkin-theme.css");
@import url("./bw-theme.css"); @import url("./bw-theme.css");
+20 -7
View File
@@ -12,16 +12,34 @@ import {
setSessionContext, setSessionContext,
SESSION_COOKIE, SESSION_COOKIE,
} from "@/lib/auth" } from "@/lib/auth"
import { checkRateLimit } from "@/lib/rate-limit"
function jsonResponse(data: unknown, status: number) { function jsonResponse(data: unknown, status: number, headers?: Record<string, string>) {
return new Response(JSON.stringify(data), { return new Response(JSON.stringify(data), {
status, status,
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json", ...headers },
}) })
} }
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
try { try {
const ipAddress =
request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
request.headers.get("x-real-ip") ||
"127.0.0.1"
const rateCheck = checkRateLimit(`login:${ipAddress}`, 10, 60000)
if (!rateCheck.allowed) {
return jsonResponse(
{ error: "Too many login attempts. Try again in 60 seconds." },
429,
{
"Retry-After": String(Math.ceil((rateCheck.resetAt - Date.now()) / 1000)),
"X-RateLimit-Remaining": "0",
},
)
}
const { email, username, password } = await request.json() const { email, username, password } = await request.json()
const credential = email || username const credential = email || username
@@ -40,11 +58,6 @@ export async function POST(request: NextRequest) {
) )
} }
const ipAddress =
request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
request.headers.get("x-real-ip") ||
"127.0.0.1"
const userAgent = request.headers.get("user-agent") || null const userAgent = request.headers.get("user-agent") || null
// Try to find user by email first, then by username // Try to find user by email first, then by username
+18
View File
@@ -34,9 +34,19 @@ function LoginForm() {
const [remember, setRemember] = useState(false) const [remember, setRemember] = useState(false)
const [error, setError] = useState("") const [error, setError] = useState("")
const [counts, setCounts] = useState({ leads: 0, conversion: 0, users: 0 }) const [counts, setCounts] = useState({ leads: 0, conversion: 0, users: 0 })
const [checkingSession, setCheckingSession] = useState(true)
const counterStarted = useRef(false) const counterStarted = useRef(false)
const [testimonialIndex, setTestimonialIndex] = useState(0) const [testimonialIndex, setTestimonialIndex] = useState(0)
useEffect(() => {
fetch("/api/auth/me", { credentials: "include" })
.then((r) => {
if (r.ok) router.push("/dashboard")
else setCheckingSession(false)
})
.catch(() => setCheckingSession(false))
}, [router])
const testimonials = [ const testimonials = [
{ {
text: "This CRM transformed how we manage our sales pipeline. We've seen a 40% increase in lead conversion.", text: "This CRM transformed how we manage our sales pipeline. We've seen a 40% increase in lead conversion.",
@@ -240,6 +250,14 @@ function LoginForm() {
} }
} }
if (checkingSession) {
return (
<div className="flex min-h-screen bg-[#0a0a0f] items-center justify-center">
<div className="w-8 h-8 border-2 border-[#1BB0CE] border-t-transparent rounded-full animate-spin" />
</div>
)
}
return ( return (
<div className="flex min-h-screen bg-[#0a0a0f]"> <div className="flex min-h-screen bg-[#0a0a0f]">
<div className="left-panel flex flex-1 flex-col p-12"> <div className="left-panel flex flex-1 flex-col p-12">
@@ -34,6 +34,7 @@ const backgroundOptions = [
{ value: "default", label: "None", icon: Shield, color: "bg-gray-400", ring: "ring-gray-400", desc: "No background theme" }, { value: "default", label: "None", icon: Shield, color: "bg-gray-400", ring: "ring-gray-400", desc: "No background theme" },
{ 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" },
{ value: "cosmic", label: "Cosmic", icon: Shield, color: "bg-gradient-to-r from-purple-500 to-green-500", ring: "ring-purple-500", desc: "Deep space with purple nebula and cyan accents" }, { value: "cosmic", label: "Cosmic", icon: Shield, color: "bg-gradient-to-r from-purple-500 to-green-500", ring: "ring-purple-500", desc: "Deep space with purple nebula and cyan accents" },
{ value: "cyberpunk", label: "Cyberpunk", icon: Shield, color: "bg-gradient-to-r from-pink-500 to-cyan-400", ring: "ring-pink-500", desc: "Dark retro-futuristic synthwave aesthetic" },
{ value: "cyber2", label: "Cyber2.0", icon: Shield, color: "bg-gradient-to-r from-cyan-400 to-fuchsia-500", ring: "ring-cyan-400", desc: "Neon grid with magenta and cyan glow" }, { value: "cyber2", label: "Cyber2.0", icon: Shield, color: "bg-gradient-to-r from-cyan-400 to-fuchsia-500", ring: "ring-cyan-400", desc: "Neon grid with magenta and cyan glow" },
{ value: "pumpkin", label: "Pumpkin Spice", icon: Shield, color: "bg-gradient-to-r from-orange-600 to-amber-500", ring: "ring-orange-600", desc: "Warm autumn orange with spicy accents" }, { value: "pumpkin", label: "Pumpkin Spice", icon: Shield, color: "bg-gradient-to-r from-orange-600 to-amber-500", ring: "ring-orange-600", desc: "Warm autumn orange with spicy accents" },
{ value: "bw", label: "Black & White", icon: Shield, color: "bg-gradient-to-r from-gray-900 to-gray-400", ring: "ring-gray-500", desc: "Clean monochrome with subtle texture" }, { value: "bw", label: "Black & White", icon: Shield, color: "bg-gradient-to-r from-gray-900 to-gray-400", ring: "ring-gray-500", desc: "Clean monochrome with subtle texture" },
+31
View File
@@ -0,0 +1,31 @@
const attempts = new Map<string, { count: number; resetAt: number }>()
export function checkRateLimit(
key: string,
maxAttempts = 5,
windowMs = 60000,
): { allowed: boolean; remaining: number; resetAt: number } {
const now = Date.now()
const entry = attempts.get(key)
if (!entry || now > entry.resetAt) {
attempts.set(key, { count: 1, resetAt: now + windowMs })
return { allowed: true, remaining: maxAttempts - 1, resetAt: now + windowMs }
}
entry.count++
if (entry.count > maxAttempts) {
return { allowed: false, remaining: 0, resetAt: entry.resetAt }
}
return { allowed: true, remaining: maxAttempts - entry.count, resetAt: entry.resetAt }
}
// Cleanup stale entries every 5 minutes
setInterval(() => {
const now = Date.now()
for (const [key, entry] of attempts) {
if (now > entry.resetAt) attempts.delete(key)
}
}, 300000)
+1
View File
@@ -7,6 +7,7 @@ const WEBSITE_THEME_KEY = "crm-website-theme"
const themeClasses: Record<string, string> = { const themeClasses: Record<string, string> = {
spidey: "theme-spidey", spidey: "theme-spidey",
cosmic: "theme-cosmic", cosmic: "theme-cosmic",
cyberpunk: "theme-cyberpunk",
cyber2: "theme-cyber2", cyber2: "theme-cyber2",
pumpkin: "theme-pumpkin", pumpkin: "theme-pumpkin",
bw: "theme-bw", bw: "theme-bw",