'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 (
);
}
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' && (
<>
{adminNavItems.map((item) => (
))}
>
)}
{user?.role === 'technical' && (
<>
{technicalNavItems.map((item) => (
))}
>
)}
{user?.role === 'sales' && (
<>
{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}
);
}