diff --git a/web/package-lock.json b/web/package-lock.json index 4767cf7..0861deb 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8,6 +8,7 @@ "name": "sbc-web", "version": "1.0.0", "dependencies": { + "lucide-react": "^1.24.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.0" @@ -1407,6 +1408,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.24.0.tgz", + "integrity": "sha512-YT6mBD8lGKkg4nM39enlm94/sfJIiW0YKUT60fBy4YK8tai31ylg1VhGNWxkpSKHo9UagfnZqwIff3HTDQwXeA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", diff --git a/web/package.json b/web/package.json index 5e07a76..b4d58f3 100644 --- a/web/package.json +++ b/web/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "lucide-react": "^1.24.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.26.0" diff --git a/web/src/pages/Admin.jsx b/web/src/pages/Admin.jsx index 5db72b2..f2c2255 100644 --- a/web/src/pages/Admin.jsx +++ b/web/src/pages/Admin.jsx @@ -1,5 +1,22 @@ import { useEffect, useState } from 'react'; import { Link, Navigate } from 'react-router-dom'; +import { + ArrowLeft, + CalendarCheck2, + CalendarDays, + ClipboardList, + LayoutDashboard, + LogOut, + Mail, + PanelsTopLeft, + ReceiptText, + Settings2, + ShieldCheck, + Tags, + UserCog, + UserRoundPlus, + Users, +} from 'lucide-react'; import { useAuth } from '../lib/AuthContext.jsx'; import { api, dateParts, statutLabel } from '../lib/api.js'; import { MembersTab, RencontresTab, InscriptionsTab } from '../components/admin/AdminTabs.jsx'; @@ -11,24 +28,31 @@ import { RequestsTab } from '../components/admin/AdminRequests.jsx'; import { BillingTab } from '../components/admin/AdminBilling.jsx'; import { RgpdTab } from '../components/admin/AdminRgpd.jsx'; -// Moderators get members/membership requests/rencontres/inscriptions/rencontres passées -// (create + edit, never delete) — everything else (dashboard, taxonomy, -// site content, staff account management) is admin-only, enforced both -// here (sidebar) and server-side (routes). +// Staff members share the operational sections. Administrators also manage +// content and settings, while treasurers additionally access billing. The +// same permissions are enforced here and by the server routes. const TABS = [ - { key: 'dashboard', icon: '◧', label: 'Tableau de bord', title: 'Tableau de bord', roles: ['admin'] }, - { key: 'membres', icon: '▤', label: 'Membres', title: 'Gestion des membres', roles: ['admin', 'moderator', 'treasurer'] }, - { key: 'demandes', icon: '◌', label: 'Demandes', title: "Demandes d’adhésion", roles: ['admin', 'moderator', 'treasurer'] }, - { key: 'rencontres', icon: '◈', label: 'Rencontres', title: 'Rencontres', roles: ['admin', 'moderator', 'treasurer'] }, - { key: 'passees', icon: '▦', label: 'Rencontres passées', title: 'Rencontres passées', roles: ['admin', 'moderator', 'treasurer'] }, - { key: 'inscriptions', icon: '✎', label: 'Inscriptions', title: 'Inscriptions', roles: ['admin', 'moderator', 'treasurer'] }, - { key: 'facturation', icon: '€', label: 'Facturation', title: 'Facturation des adhésions', roles: ['admin', 'treasurer'] }, - { key: 'categories', icon: '☲', label: 'Catégories', title: 'Catégories', roles: ['admin'] }, - { key: 'contenu', icon: '▧', label: 'Contenu du site', title: 'Contenu du site', roles: ['admin'] }, - { key: 'parametres', icon: '⚙', label: 'Paramètres', title: "Paramètres de l'association", roles: ['admin'] }, - { key: 'rgpd', icon: '⚖', label: 'RGPD', title: 'Conformité RGPD', roles: ['admin'] }, - { key: 'emails', icon: '✉', label: "Créateur d'e-mail", title: "Créateur d'e-mail", roles: ['admin'] }, - { key: 'utilisateurs', icon: '⚿', label: 'Utilisateurs', title: 'Administrateurs, trésoriers & modérateurs', roles: ['admin'] }, + { key: 'dashboard', group: 'overview', icon: LayoutDashboard, label: 'Tableau de bord', title: 'Tableau de bord', roles: ['admin'] }, + { key: 'demandes', group: 'members', icon: UserRoundPlus, label: 'Demandes', title: "Demandes d’adhésion", roles: ['admin', 'moderator', 'treasurer'] }, + { key: 'membres', group: 'members', icon: Users, label: 'Membres', title: 'Gestion des membres', roles: ['admin', 'moderator', 'treasurer'] }, + { key: 'facturation', group: 'members', icon: ReceiptText, label: 'Facturation', title: 'Facturation des adhésions', roles: ['admin', 'treasurer'] }, + { key: 'rencontres', group: 'events', icon: CalendarDays, label: 'Rencontres', title: 'Rencontres', roles: ['admin', 'moderator', 'treasurer'] }, + { key: 'inscriptions', group: 'events', icon: ClipboardList, label: 'Inscriptions', title: 'Inscriptions', roles: ['admin', 'moderator', 'treasurer'] }, + { key: 'passees', group: 'events', icon: CalendarCheck2, label: 'Rencontres passées', title: 'Rencontres passées', roles: ['admin', 'moderator', 'treasurer'] }, + { key: 'emails', group: 'communication', icon: Mail, label: "Créateur d'e-mail", title: "Créateur d'e-mail", roles: ['admin'] }, + { key: 'contenu', group: 'communication', icon: PanelsTopLeft, label: 'Contenu du site', title: 'Contenu du site', roles: ['admin'] }, + { key: 'categories', group: 'communication', icon: Tags, label: 'Catégories', title: 'Catégories', roles: ['admin'] }, + { key: 'parametres', group: 'settings', icon: Settings2, label: 'Association', title: "Paramètres de l'association", roles: ['admin'] }, + { key: 'utilisateurs', group: 'settings', icon: UserCog, label: 'Utilisateurs', title: 'Administrateurs, trésoriers & modérateurs', roles: ['admin'] }, + { key: 'rgpd', group: 'settings', icon: ShieldCheck, label: 'RGPD', title: 'Conformité RGPD', roles: ['admin'] }, +]; + +const NAV_GROUPS = [ + { key: 'overview', label: "Vue d'ensemble" }, + { key: 'members', label: 'Adhérents' }, + { key: 'events', label: 'Rencontres' }, + { key: 'communication', label: 'Communication' }, + { key: 'settings', label: 'Configuration' }, ]; function Dashboard() { @@ -108,6 +132,10 @@ function Dashboard() { export function AdminShell() { const { user, logout } = useAuth(); const tabs = TABS.filter((t) => t.roles.includes(user.role)); + const navGroups = NAV_GROUPS.map((group) => ({ + ...group, + tabs: tabs.filter((item) => item.group === group.key), + })).filter((group) => group.tabs.length > 0); const [tab, setTab] = useState(tabs[0].key); const current = tabs.find((t) => t.key === tab) || tabs[0]; @@ -116,27 +144,45 @@ export function AdminShell() { return (