'use client'; import { useEffect } from 'react'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { useLanguage } from '@/context/LanguageContext'; import { useAuth } from '@/context/AuthContext'; import { PrivacyProvider, usePrivacy } from '@/context/PrivacyContext'; import LanguageToggle from '@/components/LanguageToggle'; import Button from '@/components/ui/Button'; import { HomeIcon, CalendarIcon, TicketIcon, UsersIcon, CreditCardIcon, EnvelopeIcon, InboxIcon, PhotoIcon, CameraIcon, Cog6ToothIcon, ArrowLeftOnRectangleIcon, Bars3Icon, XMarkIcon, BanknotesIcon, QrCodeIcon, DocumentTextIcon, QuestionMarkCircleIcon, EyeIcon, EyeSlashIcon, } from '@heroicons/react/24/outline'; import clsx from 'clsx'; import { useState } from 'react'; export default function AdminLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } function AdminLayoutInner({ children, }: { children: React.ReactNode; }) { const router = useRouter(); const pathname = usePathname(); const { t, locale } = useLanguage(); const { user, hasAdminAccess, isLoading, logout } = useAuth(); const { privacyMode, togglePrivacyMode } = usePrivacy(); const [sidebarOpen, setSidebarOpen] = useState(false); type Role = 'admin' | 'organizer' | 'staff' | 'marketing'; const userRole = (user?.role || 'user') as Role; const navigationWithRoles: { name: string; href: string; icon: typeof HomeIcon; allowedRoles: Role[] }[] = [ { name: t('admin.nav.dashboard'), href: '/admin', icon: HomeIcon, allowedRoles: ['admin', 'organizer'] }, { name: t('admin.nav.events'), href: '/admin/events', icon: CalendarIcon, allowedRoles: ['admin', 'organizer', 'staff'] }, { name: t('admin.nav.bookings'), href: '/admin/bookings', icon: TicketIcon, allowedRoles: ['admin', 'organizer'] }, { name: locale === 'es' ? 'Escáner' : 'Scanner', href: '/admin/scanner', icon: QrCodeIcon, allowedRoles: ['admin', 'organizer', 'staff'] }, { name: t('admin.nav.users'), href: '/admin/users', icon: UsersIcon, allowedRoles: ['admin'] }, { name: t('admin.nav.payments'), href: '/admin/payments', icon: CreditCardIcon, allowedRoles: ['admin', 'organizer'] }, { name: locale === 'es' ? 'Opciones de Pago' : 'Payment Options', href: '/admin/payment-options', icon: BanknotesIcon, allowedRoles: ['admin', 'organizer'] }, { name: t('admin.nav.contacts'), href: '/admin/contacts', icon: EnvelopeIcon, allowedRoles: ['admin', 'organizer', 'marketing'] }, { name: t('admin.nav.emails'), href: '/admin/emails', icon: InboxIcon, allowedRoles: ['admin', 'organizer'] }, { name: t('admin.nav.gallery'), href: '/admin/gallery', icon: PhotoIcon, allowedRoles: ['admin', 'organizer'] }, { name: t('admin.nav.photos'), href: '/admin/photos', icon: CameraIcon, allowedRoles: ['admin', 'organizer'] }, { name: locale === 'es' ? 'Páginas Legales' : 'Legal Pages', href: '/admin/legal-pages', icon: DocumentTextIcon, allowedRoles: ['admin'] }, { name: 'FAQ', href: '/admin/faq', icon: QuestionMarkCircleIcon, allowedRoles: ['admin'] }, { name: locale === 'es' ? 'Configuración' : 'Settings', href: '/admin/settings', icon: Cog6ToothIcon, allowedRoles: ['admin'] }, ]; const allowedPathsForRole = new Set( navigationWithRoles.filter((item) => item.allowedRoles.includes(userRole)).map((item) => item.href) ); // All known admin routes regardless of role, used only to tell "not allowed // for this role" apart from "doesn't exist" - the latter should render the // 404 page instead of bouncing to the default route. const allAdminHrefs = new Set(navigationWithRoles.map((item) => item.href)); const defaultAdminRoute = userRole === 'staff' ? '/admin/scanner' : userRole === 'marketing' ? '/admin/contacts' : '/admin'; // All hooks must be called unconditionally before any early returns useEffect(() => { if (!isLoading && (!user || !hasAdminAccess)) { router.push('/login'); } }, [user, hasAdminAccess, isLoading, router]); useEffect(() => { if (!user || !hasAdminAccess) return; if (!pathname.startsWith('/admin')) return; if (pathname === '/admin' && (userRole === 'staff' || userRole === 'marketing')) { router.replace(defaultAdminRoute); return; } const matchesHrefSet = (path: string, hrefs: Set) => { if (hrefs.has(path)) return true; return Array.from(hrefs).some((href) => path.startsWith(href + '/')); }; // Unknown route entirely (e.g. a typo'd URL) - let it fall through to the // admin 404 page instead of silently redirecting away. if (!matchesHrefSet(pathname, allAdminHrefs)) return; if (!matchesHrefSet(pathname, allowedPathsForRole)) { router.replace(defaultAdminRoute); } }, [pathname, userRole, defaultAdminRoute, router, user, hasAdminAccess]); if (isLoading) { return ( ); } if (!user || !hasAdminAccess) { return null; } const visibleNav = navigationWithRoles.filter((item) => item.allowedRoles.includes(userRole)); const navigation = visibleNav; const handleLogout = () => { logout(); router.push('/'); }; // Scanner page gets fullscreen layout without sidebar const isScannerPage = pathname === '/admin/scanner'; if (isScannerPage) { return ( {children} ); } return ( {/* Mobile sidebar backdrop */} {sidebarOpen && ( setSidebarOpen(false)} /> )} {/* Sidebar */} {/* Main content */} {/* Top bar */} setSidebarOpen(true)} > {privacyMode ? ( ) : ( )} {privacyMode ? t('admin.privacy.show') : t('admin.privacy.hide')} View Site {/* Page content */} {children} ); }