import { useAuth } from "@/_core/hooks/useAuth";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
SidebarTrigger,
useSidebar,
} from "@/components/ui/sidebar";
import { APP_LOGO, APP_TITLE, getLoginUrl } from "@/const";
import { useIsMobile } from "@/hooks/useMobile";
import { LayoutDashboard, LogOut, PanelLeft, Users, GraduationCap, Calendar, CalendarDays, Bell, BarChart3, UserCog, Mail, Settings, TrendingUp, Building2, FileSpreadsheet, ClipboardList, ChevronDown, User, FileText } from "lucide-react";
import { CSSProperties, useEffect, useRef, useState } from "react";
import { useLocation } from "wouter";
import { DashboardLayoutSkeleton } from './DashboardLayoutSkeleton';
import { Button } from "./ui/button";
const menuSections = [
{
title: "Tableau de bord",
items: [
{ icon: LayoutDashboard, label: "Tableau de bord", path: "/admin" },
]
},
{
title: "Gestion",
items: [
{ icon: GraduationCap, label: "Formations", path: "/admin/formations" },
{ icon: Calendar, label: "Séquences", path: "/admin/sequences" },
{ icon: CalendarDays, label: "Calendrier", path: "/admin/calendrier" },
{ icon: Users, label: "Apprenants", path: "/admin/apprenants" },
]
},
{
title: "Configuration",
items: [
{ icon: UserCog, label: "Utilisateurs", path: "/admin/users" },
{ icon: User, label: "Formateurs", path: "/admin/formateurs" },
{ icon: FileSpreadsheet, label: "Import Excel", path: "/admin/import-excel" },
{ icon: Bell, label: "Rappels", path: "/admin/rappels" },
{ icon: ClipboardList, label: "Questionnaires", path: "/admin/questionnaires" },
{ icon: Mail, label: "Templates d'emails", path: "/admin/email-templates" },
{ icon: Settings, label: "Configuration SMTP", path: "/admin/email-config" },
{ icon: FileText, label: "Attestations de formation", path: "/admin/attestations" },
]
},
{
title: "Traçabilité",
items: [
{ icon: Building2, label: "Établissements", path: "/admin/etablissements" },
{ icon: Mail, label: "Notifications", path: "/admin/notifications" },
{ icon: TrendingUp, label: "Suivi Questionnaires", path: "/admin/questionnaires/suivi" },
{ icon: Bell, label: "Rappels", path: "/admin/rappels/historique" },
]
},
{
title: "Analyses",
items: [
{ icon: TrendingUp, label: "Tableau de bord analytique", path: "/admin/analytics" },
{ icon: BarChart3, label: "Rapport Public Cible", path: "/admin/rapport-public-cible" },
{ icon: BarChart3, label: "Statistiques des rappels", path: "/admin/rappels/statistiques" },
]
},
];
const SIDEBAR_WIDTH_KEY = "sidebar-width";
const DEFAULT_WIDTH = 280;
const MIN_WIDTH = 200;
const MAX_WIDTH = 480;
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
const [sidebarWidth, setSidebarWidth] = useState(() => {
const saved = localStorage.getItem(SIDEBAR_WIDTH_KEY);
return saved ? parseInt(saved, 10) : DEFAULT_WIDTH;
});
const { loading, user } = useAuth();
useEffect(() => {
localStorage.setItem(SIDEBAR_WIDTH_KEY, sidebarWidth.toString());
}, [sidebarWidth]);
if (loading) {
return
}
if (!user) {
return (
{APP_TITLE}
Veuillez vous connecter pour continuer
);
}
return (
{children}
);
}
type DashboardLayoutContentProps = {
children: React.ReactNode;
setSidebarWidth: (width: number) => void;
};
function DashboardLayoutContent({
children,
setSidebarWidth,
}: DashboardLayoutContentProps) {
const { user, logout } = useAuth();
const [location, setLocation] = useLocation();
const { state, toggleSidebar } = useSidebar();
const isCollapsed = state === "collapsed";
const [isResizing, setIsResizing] = useState(false);
const sidebarRef = useRef(null);
const activeMenuItem = menuSections.flatMap(section => section.items).find(item => item.path === location);
const isMobile = useIsMobile();
// État pour gérer les sections ouvertes/fermées (persisté dans localStorage)
const [openSections, setOpenSections] = useState>(() => {
// Version du système de menu (incrémenter pour forcer la réinitialisation)
const MENU_VERSION = 2;
const savedVersion = localStorage.getItem('menuVersion');
// Si la version a changé, ignorer le localStorage existant
if (savedVersion && parseInt(savedVersion) === MENU_VERSION) {
const saved = localStorage.getItem('menuSectionsState');
if (saved) {
try {
return JSON.parse(saved);
} catch {
// En cas d'erreur, utiliser les valeurs par défaut
}
}
}
// Valeurs par défaut : seule la section TABLEAU DE BORD est ouverte
const initial: Record = {};
menuSections.forEach(section => {
// Ouvrir uniquement "Tableau de bord", fermer les autres
initial[section.title] = section.title === "Tableau de bord";
});
// Sauvegarder la version et l'état initial
localStorage.setItem('menuVersion', MENU_VERSION.toString());
localStorage.setItem('menuSectionsState', JSON.stringify(initial));
return initial;
});
// Fonction pour toggle une section
const toggleSection = (sectionTitle: string) => {
setOpenSections(prev => {
const newState = {
...prev,
[sectionTitle]: !prev[sectionTitle]
};
// Sauvegarder dans localStorage
localStorage.setItem('menuSectionsState', JSON.stringify(newState));
return newState;
});
};
useEffect(() => {
if (isCollapsed) {
setIsResizing(false);
}
}, [isCollapsed]);
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
if (!isResizing) return;
const sidebarLeft = sidebarRef.current?.getBoundingClientRect().left ?? 0;
const newWidth = e.clientX - sidebarLeft;
if (newWidth >= MIN_WIDTH && newWidth <= MAX_WIDTH) {
setSidebarWidth(newWidth);
}
};
const handleMouseUp = () => {
setIsResizing(false);
};
if (isResizing) {
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
document.body.style.cursor = "col-resize";
document.body.style.userSelect = "none";
}
return () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
document.body.style.cursor = "";
document.body.style.userSelect = "";
};
}, [isResizing, setSidebarWidth]);
return (
<>
{isCollapsed ? (
) : (
<>
{APP_TITLE}
>
)}
{menuSections.map((section, sectionIndex) => {
const isOpen = openSections[section.title] ?? false;
return (
{sectionIndex > 0 && (
)}
{isOpen && (
{section.items.map(item => {
const isActive = location === item.path;
const hasSubItems = 'subItems' in item && item.subItems && item.subItems.length > 0;
const isSubItemActive = hasSubItems && item.subItems.some((sub: any) => location === sub.path);
return (
setLocation(item.path)}
tooltip={item.label}
className={`h-10 transition-all font-normal`}
>
{item.label}
{hasSubItems && (
{item.subItems.map((subItem: any) => {
const isSubActive = location === subItem.path;
return (
setLocation(subItem.path)}
tooltip={subItem.label}
className="h-9 text-sm font-normal"
>
{subItem.label}
);
})}
)}
);
})}
)}
);
})}
{
await logout();
setLocation('/');
}}
className="cursor-pointer text-destructive focus:text-destructive"
>
Déconnexion
{
if (isCollapsed) return;
setIsResizing(true);
}}
style={{ zIndex: 50 }}
/>
{isMobile && (
{activeMenuItem?.label ?? APP_TITLE}
)}
{children}
>
);
}