"use client"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { useEffect, useState, type ReactNode } from "react"; import { BackendIcon, type BackendIconName } from "@/components/BackendIcons"; import { can, ROLE_LABELS } from "@/lib/rbac"; import type { AppRole } from "@/types/next-auth"; import { doSignOut } from "./actions"; const TITLES: Record = { "/backend": ["Tableau de bord", "Vue d'ensemble de l'association"], "/backend/adherents": ["Adhérents", "Gérer les commerçants et entreprises"], "/backend/demandes": ["Demandes & messages", "Demandes d'adhésion et messages de contact"], "/backend/promotions": [ "Modération des promotions", "Validez les offres soumises par les adhérents", ], "/backend/rencontres": ["Rencontres", "Gérer les prochaines dates"], "/backend/inscriptions": ["Inscriptions", "Gérer les participants aux rencontres"], "/backend/rencontres-passees": ["Rencontres passées", "Publier les archives et leurs photos"], "/backend/informations": ["Informations", "Ce que l'association publie dans l'espace adhérent"], "/backend/emails": ["Création d'e-mails", "Composer des messages aux couleurs de Plein R"], "/backend/reseaux": ["Réseaux sociaux", "Connecter les pages Facebook et LinkedIn"], "/backend/administrateurs": ["Administrateurs", "Gérer les accès à l'administration"], "/backend/categories": ["Catégories", "Gérer les métiers de l'annuaire"], "/backend/parametres": ["Paramètres du site", "Configurer l'association et les mentions légales"], "/backend/espace": ["Mon profil", "Votre fiche publique, vos inscriptions et votre droit à l'image"], "/backend/espace/promotions": ["Mes promotions", "Proposez une offre et suivez celles en ligne"], "/backend/espace/informations": ["Informations", "Les annonces de l'association"], "/backend/changer-mot-de-passe": ["Mot de passe", "Sécurisez votre compte"], }; type Capability = Parameters[1]; type BadgeKey = "inbox" | "promos" | "infos"; type NavItem = { href: string; label: string; icon: BackendIconName; capability?: Capability; badge?: BadgeKey; /** Les fiches adhérent sont des sous-routes : l'entrée doit rester active. */ prefix?: boolean; }; type NavSection = { label: string; items: NavItem[] }; /** * Menu groupé par usage plutôt qu'en une seule pile : on distingue le quotidien * (réseau, rencontres) de ce qu'on ne touche qu'occasionnellement (configuration). */ const SECTIONS: NavSection[] = [ { label: "Pilotage", items: [{ href: "/backend", label: "Tableau de bord", icon: "dashboard", capability: "viewDashboard" }], }, { label: "Vie du réseau", items: [ { href: "/backend/adherents", label: "Adhérents", icon: "members", capability: "manageMembers", prefix: true }, { href: "/backend/demandes", label: "Demandes & messages", icon: "inbox", capability: "manageMembers", badge: "inbox" }, { href: "/backend/promotions", label: "Promotions", icon: "promos", capability: "moderatePromos", badge: "promos" }, ], }, { label: "Rencontres", items: [ { href: "/backend/rencontres", label: "Prochaines dates", icon: "meetings", capability: "manageMeetings" }, { href: "/backend/inscriptions", label: "Inscriptions", icon: "registrations", capability: "manageMeetings" }, { href: "/backend/rencontres-passees", label: "Archives & photos", icon: "gallery", capability: "manageMeetings" }, ], }, { label: "Communication", items: [ { href: "/backend/informations", label: "Informations", icon: "news", capability: "manageInformations" }, { href: "/backend/emails", label: "E-mails", icon: "emails", capability: "manageEmails" }, { href: "/backend/reseaux", label: "Réseaux sociaux", icon: "social", capability: "manageSettings" }, ], }, { label: "Configuration", items: [ { href: "/backend/categories", label: "Catégories", icon: "categories", capability: "manageCategories" }, { href: "/backend/parametres", label: "Paramètres du site", icon: "settings", capability: "manageSettings" }, { href: "/backend/administrateurs", label: "Administrateurs", icon: "admins", capability: "manageAdmins" }, ], }, { label: "Côté adhérent", items: [ { href: "/backend/espace/informations", label: "Informations", icon: "news", badge: "infos" }, { href: "/backend/espace", label: "Mon profil", icon: "space" }, { href: "/backend/espace/promotions", label: "Mes promotions", icon: "promos" }, ], }, ]; function initialsOf(name: string) { return name .split(/\s+/) .map((w) => w[0]) .join("") .slice(0, 2) .toUpperCase(); } function Badge({ value }: { value: number }) { return ( {value} ); } export function BackendShell({ user, pendingCount, inboxCount = 0, infoCount = 0, children, }: { user: { name: string; role: AppRole }; pendingCount: number; inboxCount?: number; infoCount?: number; children: ReactNode; }) { const pathname = usePathname(); const [menuOpen, setMenuOpen] = useState(false); const [title, sub] = TITLES[pathname] ?? (pathname.startsWith("/backend/adherents") ? TITLES["/backend/adherents"] : ["Administration", "Plein R"]); // Le tiroir se referme dès qu'on navigue. useEffect(() => { setMenuOpen(false); }, [pathname]); // Pas de défilement de la page derrière le tiroir ouvert. useEffect(() => { if (!menuOpen) return; const previous = document.body.style.overflow; document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = previous; }; }, [menuOpen]); useEffect(() => { if (!menuOpen) return; const onKey = (event: KeyboardEvent) => { if (event.key === "Escape") setMenuOpen(false); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [menuOpen]); const badges: Record = { inbox: inboxCount, promos: pendingCount, infos: infoCount }; const sections = SECTIONS.map((section) => ({ ...section, items: section.items.filter((item) => !item.capability || can(user.role, item.capability)), })).filter((section) => section.items.length > 0); const siteLink = ( ← Voir le site ); const signOut = (
); return (
{menuOpen && (
{initialsOf(user.name)}
{user.name}
{ROLE_LABELS[user.role]}
{/* Sur téléphone, l'entête n'a pas la place : les actions vivent ici. */}
{siteLink} {signOut}

{title}

{sub}
{siteLink} {signOut}
{children}
); }