import React, { useState, useEffect } from 'react'; import { NavLink, Outlet, useLocation, useNavigate, Navigate, Link } from 'react-router-dom'; import { LayoutDashboard, CheckSquare, ShieldAlert, Tent, Utensils, Map as MapIcon, LogOut, Sun, Moon, Loader2, Leaf, ShoppingBag, Menu, X, BarChart2, Gamepad2, Settings, Palette, Library, RefreshCw, Laptop, MessageCircle, Lock, Zap, Calendar as CalendarIcon, Bot, ChevronRight, Award } from 'lucide-react'; import { StorageService } from '../services/storageService'; import { UserProfile } from '../types'; import { useLanguage } from '../contexts/LanguageContext'; import { THEME_REGISTRY } from '../config/themes'; import { Logo } from './ui/Logo'; import RudraAI from './RudraAI'; // Import from components folder import confetti from 'canvas-confetti'; const getFrameStyle = (id?: string) => { if (!id || id === 'none') return 'ring-2 ring-white/50 dark:ring-white/20'; if (id === 'unicorn') return 'ring-4 ring-pink-400 shadow-[0_0_15px_#f472b6]'; if (id === 'royal') return 'ring-4 ring-yellow-500 shadow-[0_0_20px_#eab308]'; if (id === 'nature') return 'ring-4 ring-green-500 border-green-300'; if (id === 'dark') return 'ring-4 ring-gray-800 shadow-[0_0_20px_#000]'; if (id === 'frame_gold') return 'ring-4 ring-yellow-400 border-4 border-yellow-600 shadow-[0_0_25px_#eab308]'; return 'ring-2 ring-indigo-400'; }; const BadgeOverlay = () => { const [badge, setBadge] = useState<{title: string, icon: any} | null>(null); useEffect(() => { const handleUnlock = (e: any) => { const { title, icon } = e.detail; setBadge({ title, icon }); // Trigger confetti const duration = 3000; const animationEnd = Date.now() + duration; const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 9999 }; const randomInRange = (min: number, max: number) => Math.random() * (max - min) + min; const interval: any = setInterval(function() { const timeLeft = animationEnd - Date.now(); if (timeLeft <= 0) { return clearInterval(interval); } const particleCount = 50 * (timeLeft / duration); confetti({ ...defaults, particleCount, origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 } }); confetti({ ...defaults, particleCount, origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 } }); }, 250); // Auto dismiss setTimeout(() => setBadge(null), 4000); }; window.addEventListener('rudraksha-badge-unlock', handleUnlock); return () => window.removeEventListener('rudraksha-badge-unlock', handleUnlock); }, []); if (!badge) return null; return (

Achievement Unlocked

{badge.title}

); }; const Layout: React.FC = () => { const [isAuthenticated, setIsAuthenticated] = useState(false); const [loading, setLoading] = useState(true); const [profile, setProfile] = useState(null); const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [isRefreshing, setIsRefreshing] = useState(false); const [refreshKey, setRefreshKey] = useState(0); const [isFocusMode, setIsFocusMode] = useState(() => localStorage.getItem('rudraksha_focus_mode') === 'true'); // Easter Egg State const [logoClicks, setLogoClicks] = useState(0); const [themeMode, setThemeMode] = useState<'system' | 'light' | 'dark'>(() => { if (typeof window !== 'undefined') { const saved = localStorage.getItem('themeMode'); return (saved === 'light' || saved === 'dark' || saved === 'system') ? saved : 'dark'; } return 'dark'; }); const navigate = useNavigate(); const location = useLocation(); const { t, language, setLanguage } = useLanguage(); useEffect(() => { checkAuth(); const handleUpdate = () => { checkAuth(); setIsFocusMode(localStorage.getItem('rudraksha_focus_mode') === 'true'); }; window.addEventListener('rudraksha-profile-update', handleUpdate); window.addEventListener('rudraksha-focus-update', handleUpdate); return () => { window.removeEventListener('rudraksha-profile-update', handleUpdate); window.removeEventListener('rudraksha-focus-update', handleUpdate); }; }, []); useEffect(() => { setIsSidebarOpen(false); }, [location.pathname]); useEffect(() => { const root = window.document.documentElement; const body = document.body; if (body.getAttribute('data-theme-override')) return; const currentThemeId = profile?.activeTheme || 'default'; const themeConfig = THEME_REGISTRY[currentThemeId] || THEME_REGISTRY['default']; let isDark = false; if (themeConfig.uiMode !== 'auto') { isDark = themeConfig.uiMode === 'dark'; } else { isDark = themeMode === 'system' ? window.matchMedia('(prefers-color-scheme: dark)').matches : themeMode === 'dark'; } if (isDark) root.classList.add('dark'); else root.classList.remove('dark'); Object.entries(themeConfig.colors).forEach(([key, value]) => root.style.setProperty(key, value)); body.setAttribute('data-theme', currentThemeId); const bgColor = (isDark && themeConfig.darkBgColor) ? themeConfig.darkBgColor : themeConfig.bgColor; const bgPattern = (isDark && themeConfig.darkBgPattern !== undefined) ? themeConfig.darkBgPattern : (themeConfig.bgPattern || 'none'); body.style.backgroundColor = bgColor; body.style.backgroundImage = bgPattern; body.style.backgroundAttachment = 'fixed'; body.style.backgroundSize = 'cover'; body.style.backgroundPosition = themeConfig.bgPosition || 'center'; if (themeConfig.isAnimated) body.classList.add('animate-neon-flow'); else body.classList.remove('animate-neon-flow'); body.style.filter = isFocusMode ? 'saturate(0.7) contrast(1.1)' : 'none'; localStorage.setItem('themeMode', themeMode); }, [themeMode, profile?.activeTheme, location.pathname, isFocusMode]); const checkAuth = async () => { const auth = await StorageService.isAuthenticated(); const user = await StorageService.getProfile(); setIsAuthenticated(auth); setProfile(user); setLoading(false); }; const cycleThemeMode = () => { const modes: ('system' | 'light' | 'dark')[] = ['system', 'light', 'dark']; const nextIndex = (modes.indexOf(themeMode) + 1) % modes.length; setThemeMode(modes[nextIndex]); }; const handleRefresh = async () => { setIsRefreshing(true); window.dispatchEvent(new Event('rudraksha-profile-update')); await checkAuth(); setRefreshKey(prev => prev + 1); setTimeout(() => setIsRefreshing(false), 800); }; const toggleFocusMode = () => { const newState = !isFocusMode; localStorage.setItem('rudraksha_focus_mode', String(newState)); setIsFocusMode(newState); window.dispatchEvent(new Event('rudraksha-focus-update')); }; const handleLogoClick = async (e: React.MouseEvent) => { e.preventDefault(); // Only count clicks if authenticated if (!isAuthenticated || !profile) return; setLogoClicks(prev => prev + 1); // Easter Egg Trigger (5 clicks) if (logoClicks + 1 === 5) { // STRICT CHECK: Ensure profile data is loaded and badge isn't already owned if (profile.unlockedItems?.includes('badge_secret')) { console.log("Easter egg already claimed."); setLogoClicks(0); return; } // If valid claim: await StorageService.addPoints(500, 0, 'easter_egg', 'Secret Hunter'); // Manually update unlock list in DB immediately const currentItems = profile.unlockedItems || []; const updatedProfile = await StorageService.updateProfile({ unlockedItems: [...currentItems, 'badge_secret'] }); // Update local state to reflect change immediately (prevents double tap) if(updatedProfile) setProfile(updatedProfile); // Trigger Visuals window.dispatchEvent(new CustomEvent('rudraksha-badge-unlock', { detail: { title: 'Secret Hunter', icon: 'spy' } })); setLogoClicks(0); } // Reset after 2 seconds of no click setTimeout(() => setLogoClicks(0), 2000); }; if (loading) return
; if (!isAuthenticated && location.pathname !== '/auth' && location.pathname !== '/welcome') return ; const isTeacher = profile?.role === 'teacher'; const isStudent = profile?.role === 'student'; const navGroups = [ { items: [{ path: '/', label: t('Dashboard', 'Dashboard'), icon: LayoutDashboard }] }, { title: t('ACADEMIC', 'ACADEMIC'), condition: isStudent || isTeacher, items: [ { path: '/study-buddy', label: t('Rudra AI', 'Rudra AI'), icon: Bot }, { path: '/planner', label: t('Assignments', 'Assignments'), icon: CheckSquare }, { path: '/library', label: t('Library', 'Library'), icon: Library }, ] }, { title: 'CULTURE', condition: !isFocusMode, items: [ { path: '/culture', label: t('Calendar', 'Calendar'), icon: CalendarIcon }, { path: '/map', label: t('Map & Provinces', 'Map & Provinces'), icon: MapIcon }, { path: '/recipes', label: t('Kitchen', 'Kitchen'), icon: Utensils }, ] }, { title: 'COMMUNITY', condition: !isFocusMode, items: [ { path: '/community-chat', label: t('Network', 'Network'), icon: MessageCircle }, { path: '/health', label: t('Wellness', 'Wellness'), icon: Leaf }, { path: '/safety', label: t('FTL Rescue', 'FTL Rescue'), icon: ShieldAlert }, { path: '/rewards', label: t('Karma Bazaar', 'Karma Bazaar'), icon: ShoppingBag }, { path: '/arcade', label: t('Arcade', 'Arcade'), icon: Gamepad2 }, ] } ]; const NavContent = ({ onItemClick }: { onItemClick?: () => void }) => (
{navGroups.map((group, idx) => { if (group.condition === false) return null; return (
{group.title && (

{group.title}

)}
{group.items.map((item) => ( ` flex items-center justify-between px-4 py-3 rounded-2xl text-sm font-black italic transition-all duration-300 group ${isActive ? 'bg-white/80 dark:bg-white/10 text-indigo-700 dark:text-indigo-400 shadow-lg backdrop-blur-md translate-x-1 border border-indigo-100/50' : 'text-gray-700 dark:text-gray-300 hover:bg-white/40 dark:hover:bg-white/5 hover:translate-x-1'} `} >
{item.label}
))}
); })}
); const UserProfileCard = () => (
Avatar

{profile?.name || 'User'}

🏆 {profile?.points || 0} pts
); return (
{/* Desktop Sidebar */}
{/* Mobile Header - Visible only on mobile */}
0 ? 'scale-110' : ''} transition-transform`}> {isFocusMode ? : }
{isFocusMode ? Deep Work : 'Rudraksha'}
{!isFocusMode && ( )}
{/* Global Rudra AI Button */} {!isFocusMode && } {/* Mobile Sidebar Overlay */} {isSidebarOpen && (
setIsSidebarOpen(false)}>
)} {/* Mobile Sidebar Drawer */}
setIsSidebarOpen(false)} className="flex items-center gap-3 font-black text-xl text-red-700 dark:text-red-500 uppercase italic">
{isFocusMode ? : }
Rudraksha
); }; export default Layout;