'use client'; import { useEffect, useState, type ReactNode } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useAuthStore } from '@/lib/store'; import api from '@/lib/api'; import { Link } from '@/i18n/Link'; import { useLocalizedRouter, usePathname } from '@/i18n/navigation'; import { useT } from '@/i18n/I18nProvider'; import { LanguageSwitcher } from '@/i18n/LanguageSwitcher'; import type { Dictionary } from '@/i18n/dictionaries/fa'; import { DeploymentProgressManager } from '@/components/deployment-progress-manager'; import { useDeployProgressStore } from '@/lib/deploy-progress-store'; import { LayoutDashboard, Package, Rocket, Ticket, Users, Server, Scale, ClipboardList, Wrench, Briefcase, Cloud, Menu, X, LogOut, Boxes, Wallet, CreditCard, ScrollText, FileText, Database, } from 'lucide-react'; type NavKey = keyof Dictionary['nav']; type NavItem = { href: string; labelKey: NavKey; icon: ReactNode }; const userNavItems: NavItem[] = [ { href: '/dashboard', labelKey: 'dashboard', icon: }, { href: '/dashboard/apps', labelKey: 'applications', icon: }, { href: '/dashboard/services', labelKey: 'services', icon: }, { href: '/dashboard/logs', labelKey: 'logs', icon: }, { href: '/dashboard/deploy', labelKey: 'newDeploy', icon: }, { href: '/dashboard/wallet', labelKey: 'wallet', icon: }, { href: '/dashboard/invoices', labelKey: 'invoices', icon: }, { href: '/dashboard/tickets', labelKey: 'tickets', icon: }, ]; const adminNavItems: NavItem[] = [ { href: '/dashboard/admin/users', labelKey: 'users', icon: }, { href: '/dashboard/admin/apps', labelKey: 'allApplications', icon: }, { href: '/dashboard/admin/billing', labelKey: 'billingPlans', icon: }, { href: '/dashboard/admin/invoices', labelKey: 'invoices', icon: }, { href: '/dashboard/admin/clusters', labelKey: 'clusters', icon: }, { href: '/dashboard/admin/pools', labelKey: 'clusterPools', icon: }, { href: '/dashboard/admin/tickets', labelKey: 'allTickets', icon: }, ]; const technicalNavItems: NavItem[] = [ { href: '/dashboard/admin/users', labelKey: 'users', icon: }, { href: '/dashboard/admin/apps', labelKey: 'allApplications', icon: }, { href: '/dashboard/staff/tickets', labelKey: 'technicalTickets', icon: }, ]; const salesNavItems: NavItem[] = [ { href: '/dashboard/admin/users', labelKey: 'users', icon: }, { href: '/dashboard/staff/tickets', labelKey: 'salesTickets', icon: }, ]; export default function DashboardLayout({ children }: { children: React.ReactNode }) { const t = useT(); const { user, isAuthenticated, isLoading, logout } = useAuthStore(); const router = useLocalizedRouter(); const pathname = usePathname(); const [sidebarOpen, setSidebarOpen] = useState(false); const deployBarMinimized = useDeployProgressStore((s) => s.minimized); useEffect(() => { if (!isLoading && !isAuthenticated) { router.push('/login'); } }, [isLoading, isAuthenticated, router]); // Close sidebar on route change (mobile) useEffect(() => { setSidebarOpen(false); }, [pathname]); // Fetch unanswered ticket counts for staff/admin roles // Must be called before any early returns to respect React's rules of hooks const isStaffOrAdmin = user?.role === 'admin' || user?.role === 'technical' || user?.role === 'sales'; const { data: unansweredCounts } = useQuery<{ technical: number; sales: number; total: number }>({ queryKey: ['unanswered-counts'], queryFn: () => api.get('/tickets/unanswered-counts').then((r) => r.data), enabled: isStaffOrAdmin && isAuthenticated, refetchInterval: 30000, }); // Fetch wallet balance for all authenticated users (shown in header) const { data: walletData } = useQuery<{ balance: number }>({ queryKey: ['wallet-balance'], queryFn: () => api.get('/billing/wallet').then((r) => r.data), enabled: isAuthenticated, refetchInterval: 60000, }); if (isLoading) { return (
{t.common.loading}
); } if (!isAuthenticated) return null; const NavLink = ({ item, badge }: { item: NavItem; badge?: number }) => { const isActive = pathname === item.href || (item.href !== '/dashboard' && pathname.startsWith(item.href + '/')); return ( {item.icon} {t.nav[item.labelKey]} {badge !== undefined && badge > 0 && ( {badge} )} ); }; const getBadge = (href: string): number | undefined => { if (!unansweredCounts) return undefined; if (href === '/dashboard/staff/tickets') { // Staff ticket page: show count for their department if (user?.role === 'technical') return unansweredCounts.technical; if (user?.role === 'sales') return unansweredCounts.sales; } if (href === '/dashboard/admin/tickets') { return unansweredCounts.total; } return undefined; }; const SidebarContent = () => (
{userNavItems.map((item) => ( ))} {user?.role === 'admin' && ( <>

{t.nav.sectionAdmin}

{adminNavItems.map((item) => ( ))} )} {user?.role === 'technical' && ( <>

{t.nav.sectionTechnical}

{technicalNavItems.map((item) => ( ))} )} {user?.role === 'sales' && ( <>

{t.nav.sectionSales}

{salesNavItems.map((item) => ( ))} )}
{/* Sidebar footer */}

{user?.firstName} {user?.lastName}

{user?.email}

); return (
{/* Mobile overlay */} {sidebarOpen && (
setSidebarOpen(false)} /> )} {/* Mobile sidebar */} {/* Header */}
{/* Mobile hamburger */} {t.common.appName}
{/* Wallet balance */} {walletData ? Number(walletData.balance).toLocaleString('en-US') : '...'} {t.common.currencyShort}
{user?.firstName} {user?.lastName} {user?.role === 'admin' && ( {t.roles.admin} )} {user?.role === 'technical' && ( {t.roles.technical} )} {user?.role === 'sales' && ( {t.roles.sales} )}
{/* Desktop sidebar */} {/* Main content */}
{children}
); }