506 lines
24 KiB
TypeScript
506 lines
24 KiB
TypeScript
"use client"
|
|
|
|
import { ReactNode, useState, useEffect, useRef } from "react"
|
|
import { BookOpen, LayoutDashboard, Settings, FileText, Activity, LineChart, Trophy, User, ChevronUp, Lock, ShieldAlert, Flame, Target, Gauge, Zap, Calendar, Clock, BrainCircuit, BarChart3, School, CheckSquare, MessageSquare } from "lucide-react"
|
|
import Link from "next/link"
|
|
import { usePathname, useRouter } from "next/navigation"
|
|
import { LanguageToggle } from "@/components/LanguageToggle"
|
|
import { Logo } from "@/components/Logo"
|
|
import { ThemeToggle } from "@/components/ThemeToggle"
|
|
import { TierBadge } from "@/components/TierBadge"
|
|
import { StreakCounter } from "@/components/StreakCounter"
|
|
import { AITutorChat } from "@/components/AI/AITutorChat"
|
|
import { useActiveUser, DEMO_USERS, isDemoUser } from "@/hooks/useActiveUser"
|
|
import { StreakOnboardingModal } from "@/components/Dashboard/StreakOnboardingModal"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import MaintenanceScreen from "@/components/MaintenanceScreen"
|
|
|
|
export const dynamic = 'force-dynamic';
|
|
|
|
export default function DashboardLayout({ children }: { children: ReactNode }) {
|
|
const pathname = usePathname()
|
|
const [dropdownOpen, setDropdownOpen] = useState(false)
|
|
const dropdownRef = useRef<HTMLDivElement>(null)
|
|
|
|
const { userId, user, globalSettings, switchUser, logout, hasAccessTo, loading, switching, trialInfo } = useActiveUser();
|
|
const [maintenance, setMaintenance] = useState(false);
|
|
const [checkingMaintenance, setCheckingMaintenance] = useState(true);
|
|
|
|
useEffect(() => {
|
|
if (globalSettings !== undefined && globalSettings !== null) {
|
|
if (globalSettings.maintenance_mode) {
|
|
setMaintenance(true);
|
|
} else {
|
|
setMaintenance(false);
|
|
}
|
|
setCheckingMaintenance(false);
|
|
} else if (globalSettings === null && !loading) {
|
|
// If we finished loading but settings failed to load
|
|
setCheckingMaintenance(false);
|
|
}
|
|
}, [globalSettings, loading]);
|
|
|
|
// Close dropdown when clicking outside
|
|
useEffect(() => {
|
|
function handleClickOutside(event: MouseEvent) {
|
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
|
setDropdownOpen(false)
|
|
}
|
|
}
|
|
document.addEventListener("mousedown", handleClickOutside)
|
|
return () => document.removeEventListener("mousedown", handleClickOutside)
|
|
}, [])
|
|
|
|
// BUG-05 fix: strip any parenthetical role annotations from display names
|
|
// e.g. "Rahul (AI Coach)" → "Rahul", "Priya (NEET Aspirant)" → "Priya"
|
|
const cleanDisplayName = (name: string) => name.replace(/\s*\([^)]*\)\s*$/, '').trim();
|
|
|
|
const userInitials = user ? cleanDisplayName(user.name).split(" ").filter(n => /^[a-zA-Z]/.test(n)).slice(0, 2).map(n => n[0]).join("").toUpperCase() : "DS"
|
|
const targetExamLabel = user?.target_exam === "JEE_Main" ? "JEE" : user?.target_exam === "JEE_Advanced" ? "JEE Advanced" : user?.target_exam === "NEET" ? "NEET" : "JEE + NEET"
|
|
|
|
const router = useRouter();
|
|
|
|
if (maintenance) {
|
|
return <MaintenanceScreen />;
|
|
}
|
|
|
|
// Redirect to login (or admin) if no user is found after loading
|
|
if (!loading && !switching && !user) {
|
|
if (typeof window !== 'undefined') {
|
|
const activeId = localStorage.getItem("activeUserId");
|
|
if (activeId === "admin") {
|
|
router.replace("/admin");
|
|
} else {
|
|
router.replace("/login");
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
if (loading || !user) {
|
|
return (
|
|
<div className="h-screen w-screen flex flex-col items-center justify-center bg-background">
|
|
<div className="animate-pulse flex flex-col items-center gap-4">
|
|
<Logo height={56} className="mb-4" />
|
|
<div className="h-6 w-6 border-4 border-[var(--cyan-ai)] border-t-transparent rounded-full animate-spin"></div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex h-screen bg-background overflow-hidden text-foreground selection:bg-primary/20">
|
|
{/* Demo Account Switch — slim top progress bar, no full-page reload */}
|
|
{switching && (
|
|
<div className="fixed top-0 left-0 right-0 z-[9999] h-0.5 overflow-hidden">
|
|
<div
|
|
className="h-full bg-[var(--cyan-ai)] animate-[slideProgress_0.8s_ease-in-out_infinite]"
|
|
style={{ width: '60%', animation: 'slideProgress 0.9s ease-in-out infinite' }}
|
|
/>
|
|
<style>{`
|
|
@keyframes slideProgress {
|
|
0% { transform: translateX(-100%); width: 60%; }
|
|
50% { transform: translateX(70%); width: 80%; }
|
|
100% { transform: translateX(200%); width: 60%; }
|
|
}
|
|
`}</style>
|
|
</div>
|
|
)}
|
|
{/* Sidebar */}
|
|
<aside className="w-64 border-r border-border/60 bg-background/95 backdrop-blur-xl flex flex-col hidden md:flex relative">
|
|
{/* Subtle gradient overlay */}
|
|
<div className="absolute inset-0 bg-gradient-to-b from-[var(--cyan-ai)]/[0.02] via-transparent to-purple-500/[0.02] pointer-events-none" />
|
|
|
|
{/* Logo */}
|
|
<div className="px-5 py-4 border-b border-border/40">
|
|
<Logo href="/dashboard" className="w-full" imageClassName="object-contain object-left scale-105 origin-left" height={52} />
|
|
</div>
|
|
|
|
<nav className="flex-1 min-h-0 px-3 py-3 overflow-y-auto custom-scrollbar space-y-0.5">
|
|
|
|
{/* Section divider — only shows 'Pro Tier' label for users who have Pro or above */}
|
|
{hasAccessTo("chapter-wise-progress-tracking") ? (
|
|
<div className="px-2 pt-2 pb-2">
|
|
<div className="flex items-center gap-2">
|
|
<div className="h-px flex-1 bg-gradient-to-r from-transparent via-yellow-500/40 to-transparent" />
|
|
<span className="text-[10px] font-black uppercase tracking-[0.15em] text-yellow-600 dark:text-yellow-400 flex items-center gap-1.5 shrink-0">
|
|
<span className="w-1 h-1 rounded-full bg-yellow-500 shadow-[0_0_4px_#EAB308]"></span>
|
|
Pro Tier
|
|
</span>
|
|
<div className="h-px flex-1 bg-gradient-to-l from-transparent via-yellow-500/40 to-transparent" />
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="px-2 pt-2 pb-2">
|
|
<div className="flex items-center gap-2">
|
|
<div className="h-px flex-1 bg-gradient-to-r from-transparent via-border to-transparent" />
|
|
<span className="text-[10px] font-black uppercase tracking-[0.15em] text-muted-foreground flex items-center gap-1.5 shrink-0">
|
|
Free Plan
|
|
</span>
|
|
<div className="h-px flex-1 bg-gradient-to-l from-transparent via-border to-transparent" />
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<NavItem section="pro" href="/dashboard" icon={<LayoutDashboard size={17} />} label="Overview" active={pathname === "/dashboard"} />
|
|
<NavItem section="pro" href="/dashboard/leaderboard" icon={<Trophy size={17} />} label="Leaderboard" active={pathname.startsWith("/dashboard/leaderboard")} isLocked={!hasAccessTo("chapter-wise-progress-tracking")} />
|
|
<NavItem
|
|
section="pro"
|
|
href="/dashboard/progress"
|
|
icon={<BarChart3 size={17} />}
|
|
label="Syllabus Tracker"
|
|
active={pathname.startsWith("/dashboard/progress")}
|
|
isLocked={!hasAccessTo("chapter-wise-progress-tracking")}
|
|
/>
|
|
<NavItem
|
|
section="pro"
|
|
href="/dashboard/cbt"
|
|
icon={<CheckSquare size={17} />}
|
|
label="Mock Tests (CBT)"
|
|
active={pathname.startsWith("/dashboard/cbt")}
|
|
isLocked={!hasAccessTo("weekly-test-series")}
|
|
/>
|
|
<NavItem
|
|
section="pro"
|
|
href="/dashboard/dpp"
|
|
icon={<FileText size={17} />}
|
|
label="Daily Practice (DPP)"
|
|
active={pathname.startsWith("/dashboard/dpp")}
|
|
isLocked={!hasAccessTo("chapter-wise-progress-tracking")}
|
|
/>
|
|
|
|
<NavItem
|
|
section="pro"
|
|
href="/dashboard/community"
|
|
icon={<MessageSquare size={17} />}
|
|
label="Doubt Forum"
|
|
active={pathname.startsWith("/dashboard/community")}
|
|
isLocked={!hasAccessTo("doubt-resolution-community")}
|
|
/>
|
|
|
|
{/* AI COACH Section always visible */}
|
|
<div className="px-2 pt-4 pb-2">
|
|
<div className="flex items-center gap-2">
|
|
<div className="h-px flex-1 bg-gradient-to-r from-transparent via-cyan-500/50 to-transparent" />
|
|
<span className="text-[10px] font-black uppercase tracking-[0.15em] text-cyan-600 dark:text-cyan-400 flex items-center gap-1.5 shrink-0">
|
|
<span className="w-1 h-1 rounded-full bg-cyan-500 shadow-[0_0_6px_var(--cyan-ai)] dark:shadow-[0_0_8px_var(--cyan-ai)]"></span>
|
|
AI Coach
|
|
</span>
|
|
<div className="h-px flex-1 bg-gradient-to-l from-transparent via-cyan-500/50 to-transparent" />
|
|
</div>
|
|
</div>
|
|
|
|
<NavItem
|
|
section="ai"
|
|
href="/dashboard/analytics"
|
|
icon={<LineChart size={17} />}
|
|
label="Performance Analytics"
|
|
active={pathname.startsWith("/dashboard/analytics")}
|
|
isLocked={!hasAccessTo("detailed-performance-analytics")}
|
|
/>
|
|
<NavItem
|
|
section="ai"
|
|
href="/dashboard/ai-coach/tutor"
|
|
icon={<BrainCircuitIcon />}
|
|
label="AI Tutor"
|
|
active={pathname.startsWith("/dashboard/ai-coach/tutor") || pathname === "/dashboard/ai-coach"}
|
|
isLocked={!hasAccessTo("ai-tutor-chat")}
|
|
/>
|
|
<NavItem
|
|
section="ai"
|
|
href="/dashboard/ai-coach/cbt-dpp"
|
|
icon={<Zap size={17} />}
|
|
label="PrepPilot AI"
|
|
active={pathname.startsWith("/dashboard/ai-coach/cbt-dpp")}
|
|
isLocked={!hasAccessTo("ai-tutor-chat")}
|
|
/>
|
|
<NavItem
|
|
section="ai"
|
|
href="/dashboard/ai-coach/readiness"
|
|
icon={<Gauge size={17} />}
|
|
label="Exam Readiness"
|
|
active={pathname.startsWith("/dashboard/ai-coach/readiness")}
|
|
isLocked={!hasAccessTo("detailed-performance-analytics")}
|
|
/>
|
|
<NavItem
|
|
section="ai"
|
|
href="/dashboard/ai-coach/college-predictor"
|
|
icon={<School size={17} />}
|
|
label="College Predictor"
|
|
active={pathname.startsWith("/dashboard/ai-coach/college-predictor")}
|
|
isLocked={!hasAccessTo("college-predictor")}
|
|
/>
|
|
<NavItem
|
|
section="ai"
|
|
href="/dashboard/ai-coach/strategy"
|
|
icon={<Target size={17} />}
|
|
label="Exam Strategy"
|
|
active={pathname.startsWith("/dashboard/ai-coach/strategy")}
|
|
isLocked={!hasAccessTo("exam-day-strategy")}
|
|
/>
|
|
<NavItem
|
|
section="ai"
|
|
href="/dashboard/ai-coach/study-plan"
|
|
icon={<Calendar size={17} />}
|
|
label="AI Study Plan"
|
|
active={pathname.startsWith("/dashboard/ai-coach/study-plan")}
|
|
isLocked={!hasAccessTo("personalized-study-plan")}
|
|
/>
|
|
<NavItem
|
|
section="ai"
|
|
href="/dashboard/ai-coach/time-optimization"
|
|
icon={<Clock size={17} />}
|
|
label="Time Optimization"
|
|
active={pathname.startsWith("/dashboard/ai-coach/time-optimization")}
|
|
isLocked={!hasAccessTo("personalized-study-plan")}
|
|
/>
|
|
<NavItem
|
|
section="ai"
|
|
href="/dashboard/ai-coach/heatmap"
|
|
icon={<Flame size={17} />}
|
|
label="Mastery Map"
|
|
active={pathname.startsWith("/dashboard/ai-coach/heatmap")}
|
|
isLocked={!hasAccessTo("ai-mistake-tracking")}
|
|
/>
|
|
<NavItem
|
|
section="ai"
|
|
href="/dashboard/ai-coach/weekly-progress"
|
|
icon={<Activity size={17} />}
|
|
label="Weekly Progress"
|
|
active={pathname.startsWith("/dashboard/ai-coach/weekly-progress")}
|
|
isLocked={!hasAccessTo("detailed-performance-analytics")}
|
|
/>
|
|
|
|
{(!hasAccessTo("ai-tutor-chat") || !hasAccessTo("chapter-wise-progress-tracking")) && (
|
|
<div className="px-3 pt-6 pb-2">
|
|
<div className="rounded-xl bg-gradient-to-br from-cyan-500/10 to-yellow-500/10 border border-border/50 p-4 text-center">
|
|
<div className="w-10 h-10 mx-auto bg-background rounded-full shadow-sm flex items-center justify-center mb-3">
|
|
<Lock size={18} className="text-muted-foreground" />
|
|
</div>
|
|
<h4 className="text-sm font-bold text-foreground mb-1">More Features Locked</h4>
|
|
<p className="text-xs text-muted-foreground mb-3">
|
|
Upgrade your plan to unlock the AI Coach and advanced Pro tools.
|
|
</p>
|
|
<Link
|
|
href="/dashboard/settings"
|
|
className="block w-full py-2 px-3 bg-primary text-primary-foreground text-xs font-bold rounded-lg shadow-sm hover:bg-primary/90 transition-colors"
|
|
>
|
|
Upgrade Now
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
</nav>
|
|
|
|
{/* Bottom Settings + Profile */}
|
|
<div className="p-3 border-t border-border/40 bg-secondary/10 space-y-1.5">
|
|
<Link
|
|
href="/dashboard/settings"
|
|
className={`flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all duration-200 border ${
|
|
pathname.startsWith("/dashboard/settings")
|
|
? "bg-[var(--cyan-ai-10)] text-foreground font-semibold border-[var(--cyan-ai-20)]"
|
|
: "text-muted-foreground hover:bg-secondary/60 hover:text-foreground border-transparent"
|
|
}`}
|
|
>
|
|
<Settings size={17} className="shrink-0" />
|
|
<span className="text-[13px] font-medium">Settings</span>
|
|
</Link>
|
|
|
|
{/* Functional User Dropdown */}
|
|
<div className="relative" ref={dropdownRef}>
|
|
{dropdownOpen && user && (
|
|
<div className="absolute bottom-full left-0 w-full mb-2 bg-background border border-border rounded-xl shadow-xl shadow-black/20 overflow-hidden animate-in fade-in slide-in-from-bottom-2 duration-200 z-50">
|
|
<div className="px-4 py-3 border-b border-border bg-secondary/30">
|
|
<p className="text-sm font-bold text-foreground truncate">{cleanDisplayName(user.name)}</p>
|
|
<p className="text-xs text-muted-foreground truncate">{user.email || user.phone}</p>
|
|
{/* Trial info in dropdown removed */}
|
|
</div>
|
|
<div className="p-1.5">
|
|
<Link href="/dashboard/settings" className="flex items-center gap-2 px-3 py-2 text-sm text-foreground hover:bg-secondary rounded-lg transition-colors">
|
|
<User size={16} /> Edit Profile
|
|
</Link>
|
|
{isDemoUser(userId) && (
|
|
<button
|
|
onClick={() => switchUser(userId, true)}
|
|
className="w-full text-left flex items-center gap-2 px-3 py-2 text-sm text-foreground hover:bg-secondary rounded-lg transition-colors"
|
|
>
|
|
Reset Current Demo
|
|
</button>
|
|
)}
|
|
<div className="h-px bg-border my-1" />
|
|
<button
|
|
onClick={logout}
|
|
className="w-full text-left flex items-center gap-2 px-3 py-2 text-sm text-red-500 hover:bg-red-500/10 rounded-lg transition-colors font-semibold"
|
|
>
|
|
Sign Out
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<button
|
|
onClick={() => setDropdownOpen(!dropdownOpen)}
|
|
className="w-full flex items-center justify-between p-2.5 rounded-xl bg-secondary/40 hover:bg-secondary/70 transition-all duration-200 border border-border/50 hover:border-border group"
|
|
>
|
|
<div className="flex items-center gap-3 text-left">
|
|
<div className="h-8 w-8 rounded-full bg-cyan-500/20 text-cyan-700 dark:bg-cyan-500/20 dark:text-cyan-400 flex items-center justify-center font-bold text-sm shrink-0 border border-cyan-500/20 dark:shadow-[0_0_10px_-2px_var(--cyan-ai)]">
|
|
{userInitials}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-sm font-bold leading-none truncate">{user ? cleanDisplayName(user.name).split(" ")[0] : "Loading..."}</p>
|
|
<div className="mt-1">
|
|
<TierBadge tier={user ? (user.tier === "AI Coach" ? "AI Coach" : user.tier === "Pro" ? "Pro" : "Free") : "Free"} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<ChevronUp size={15} className={`text-muted-foreground transition-transform shrink-0 ${dropdownOpen ? "rotate-180" : ""}`} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
{/* Main Content Area */}
|
|
<div className="flex-1 flex flex-col h-screen overflow-hidden">
|
|
{/* Top Header Static Bar */}
|
|
<header className="h-16 border-b border-border bg-background/95 backdrop-blur-md flex items-center justify-between px-8 shrink-0 z-40">
|
|
<div className="flex items-center gap-4">
|
|
<h2 className="font-bold text-lg hidden sm:block">
|
|
{user ? `Welcome back, ${cleanDisplayName(user.name).split(" ")[0]}!` : "Welcome back!"}
|
|
</h2>
|
|
{user && (
|
|
<Badge variant="outline" className="bg-cyan-500/10 text-cyan-700 dark:text-cyan-400 border-cyan-500/20 font-bold tracking-wide uppercase px-2.5 py-0.5 animate-pulse shadow-none dark:shadow-[0_0_10px_-2px_var(--cyan-ai)]">
|
|
{targetExamLabel} Target
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4">
|
|
{/* Streak count moved to top header */}
|
|
{user && (
|
|
<button
|
|
type="button"
|
|
onClick={() => window.dispatchEvent(new CustomEvent("pragativeer-show-streak-animation"))}
|
|
className="flex items-center justify-center hover:opacity-85 transition-opacity cursor-pointer border-none bg-transparent p-0"
|
|
title="View Streak Celebration"
|
|
>
|
|
<StreakCounter streak={user.streak_count} />
|
|
</button>
|
|
)}
|
|
|
|
{/* Demo Switcher — only visible when logged in as a known demo account */}
|
|
{isDemoUser(userId) && (
|
|
<div className="flex items-center gap-2">
|
|
{switching && (
|
|
<span className="text-xs text-[var(--cyan-ai)] font-bold animate-pulse hidden lg:inline">Switching…</span>
|
|
)}
|
|
{!switching && (
|
|
<span className="text-xs text-muted-foreground font-bold hidden lg:inline">Demo:</span>
|
|
)}
|
|
<select
|
|
value={userId}
|
|
disabled={switching}
|
|
onChange={(e) => switchUser(e.target.value)}
|
|
className="bg-secondary border border-border text-foreground px-2.5 py-1.5 rounded-xl text-xs font-bold focus:ring-2 focus:ring-[var(--cyan-ai)] outline-none cursor-pointer hover:bg-secondary/80 transition-colors disabled:opacity-50 disabled:cursor-wait"
|
|
>
|
|
<option value={DEMO_USERS.AICOACH_JEE.id}>{DEMO_USERS.AICOACH_JEE.label}</option>
|
|
<option value={DEMO_USERS.AICOACH_NEET.id}>{DEMO_USERS.AICOACH_NEET.label}</option>
|
|
<option value={DEMO_USERS.AICOACH_JEE_NEET.id}>{DEMO_USERS.AICOACH_JEE_NEET.label}</option>
|
|
<option value={DEMO_USERS.FREE_JEE.id}>{DEMO_USERS.FREE_JEE.label}</option>
|
|
</select>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex items-center gap-2 border-l border-border pl-4">
|
|
{/* <LanguageToggle /> */}
|
|
<ThemeToggle />
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Free Trial Banner */}
|
|
{trialInfo.isTrialActive && (
|
|
<div className="bg-primary text-primary-foreground px-4 py-2.5 text-center text-sm font-bold flex justify-center items-center gap-2 shadow-sm shrink-0 z-30">
|
|
<span className="size-2 rounded-full bg-primary-foreground animate-pulse"></span>
|
|
{trialInfo.trialDaysRemaining} Days Remaining out of {trialInfo.trialTotalDays} Days in Free Trial
|
|
</div>
|
|
)}
|
|
|
|
{/* Scrollable page body */}
|
|
<main className="flex-1 overflow-y-auto relative bg-background/50">
|
|
<div key={pathname} className="w-full h-full animate-in fade-in zoom-in-95 slide-in-from-bottom-4 duration-500">
|
|
{children}
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
{/* Global AI Tutor Chat */}
|
|
{hasAccessTo("ai-tutor-chat") && <AITutorChat />}
|
|
|
|
{/* Onboarding Motivation Popup */}
|
|
<StreakOnboardingModal />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function NavItem({ href, icon, label, active, isLocked, section }: { href: string, icon: React.ReactNode, label: string, active?: boolean, isLocked?: boolean, section?: "pro" | "ai" }) {
|
|
// We no longer return null for isLocked. We render it with a disabled appearance.
|
|
|
|
// Section-aware active color tokens (light mode only, dark mode falls back to cyan)
|
|
const activeClasses = active
|
|
? section === "pro"
|
|
? "bg-yellow-50 light:bg-yellow-50 text-yellow-700 dark:bg-[var(--cyan-ai-10)] dark:text-[var(--cyan-ai)] font-semibold border border-yellow-300/60 dark:border-[var(--cyan-ai-20)] shadow-[0_0_12px_-4px_rgba(234,179,8,0.2)] dark:shadow-[0_0_12px_-4px_rgba(12,96,96,0.3)]"
|
|
: section === "ai"
|
|
? "bg-cyan-50 dark:bg-[var(--cyan-ai-10)] text-cyan-800 dark:text-[var(--cyan-ai)] font-semibold border border-cyan-300/60 dark:border-[var(--cyan-ai-20)] shadow-[0_0_12px_-4px_rgba(6,182,212,0.2)] dark:shadow-[0_0_12px_-4px_rgba(12,96,96,0.3)]"
|
|
: "bg-[var(--cyan-ai-10)] text-[var(--cyan-ai)] font-semibold border border-[var(--cyan-ai-20)] shadow-[0_0_12px_-4px_rgba(12,96,96,0.3)]"
|
|
: "text-muted-foreground/80 hover:bg-secondary/60 hover:text-foreground border border-transparent hover:border-border/40"
|
|
|
|
const iconActiveClass = active
|
|
? section === "pro"
|
|
? "text-yellow-600 dark:text-[var(--cyan-ai)]"
|
|
: section === "ai"
|
|
? "text-cyan-700 dark:text-[var(--cyan-ai)]"
|
|
: "text-[var(--cyan-ai)]"
|
|
: "text-muted-foreground/60 group-hover:text-foreground/80"
|
|
|
|
const activeBarClass = active
|
|
? section === "pro"
|
|
? "w-1 h-4 rounded-full bg-yellow-500 dark:bg-[var(--cyan-ai)] shrink-0 shadow-[0_0_6px_rgba(234,179,8,0.6)] dark:shadow-[0_0_6px_rgba(12,96,96,0.8)]"
|
|
: section === "ai"
|
|
? "w-1 h-4 rounded-full bg-cyan-600 dark:bg-[var(--cyan-ai)] shrink-0 shadow-[0_0_6px_rgba(6,182,212,0.6)] dark:shadow-[0_0_6px_rgba(12,96,96,0.8)]"
|
|
: "w-1 h-4 rounded-full bg-[var(--cyan-ai)] shrink-0 shadow-[0_0_6px_rgba(12,96,96,0.8)]"
|
|
: ""
|
|
|
|
return (
|
|
<Link
|
|
href={isLocked ? "/dashboard/settings" : href}
|
|
className={`group flex items-center justify-between px-3 py-2 rounded-xl transition-all duration-200 ${isLocked ? 'opacity-60 grayscale-[0.5]' : ''} ${activeClasses}`}
|
|
>
|
|
<div className="flex items-center gap-2.5">
|
|
<span className={`transition-all duration-200 ${iconActiveClass}`}>
|
|
{icon}
|
|
</span>
|
|
<span className="text-[13px] font-medium tracking-[-0.01em]">{label}</span>
|
|
</div>
|
|
<div className="flex items-center gap-1.5">
|
|
{isLocked && <Lock size={12} className="text-muted-foreground/70" />}
|
|
{!isLocked && active && <span className={activeBarClass}></span>}
|
|
</div>
|
|
</Link>
|
|
)
|
|
}
|
|
|
|
function BrainCircuitIcon() {
|
|
return (
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="lucide lucide-brain-circuit">
|
|
<path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/>
|
|
<path d="M18 8a3 3 0 0 0-3-3h-1"/>
|
|
<path d="M9 5H8a3 3 0 0 0-3 3v1"/>
|
|
<path d="M5 14v1a3 3 0 0 0 3 3h1"/>
|
|
<path d="M14 18h1a3 3 0 0 0 3-3v-1"/>
|
|
<path d="M12 18v2"/>
|
|
<path d="M12 4V2"/>
|
|
</svg>
|
|
)
|
|
}
|