// Parcours 3 — Admin établissement const AdminShell = ({ active, children }) => { const nav = [ { id: 'dashboard', label: 'Vue d\'ensemble', icon: 'home', route: '#/admin' }, { id: 'campus', label: 'Campus & groupes', icon: 'building', route: '#/admin/campus' }, { id: 'teachers', label: 'Enseignants', icon: 'users', route: '#/admin/teachers' }, { id: 'evals', label: 'Évaluations', icon: 'files', route: '#/admin' }, { id: 'ai', label: 'Crédits IA', icon: 'sparkles', route: '#/admin' }, { id: 'settings', label: 'Paramètres', icon: 'settings', route: '#/admin' }, ]; const D = window.STUDYHUB_DATA; return (
{/* Admin sidebar — dark */} {/* Main content */}
{/* Topbar */}
Système nominal
{children}
); }; // 3.1 — Dashboard admin multi-campus const AdminDashboard = () => { const D = window.STUDYHUB_DATA; const kpis = [ { label: 'Étudiants inscrits', val: '8 640', delta: '+124 ce mois', color: '#6366F1', icon: 'users' }, { label: 'Enseignants actifs', val: '403', delta: '97% activation', color: '#8B5CF6', icon: 'users' }, { label: 'Évaluations du mois', val: '1 248', delta: '+18% vs N-1', color: '#F59E0B', icon: 'files' }, { label: 'Crédits IA utilisés', val: '72%', delta: '28% restants', color: '#10B981', icon: 'sparkles' }, ]; const barMax = Math.max(...D.campuses.map(c => c.students)); return (
Bonjour Jean-Philippe · Mercredi 24 avril 2026

Vue d'ensemble

{/* KPIs */}
{kpis.map((k, i) => (
{k.label}
{k.val}
{k.delta}
))}
{/* Chart + campus list */}
{/* Bar chart */}

Activité par campus

{D.campuses.map(c => (
{c.name.split('—')[0].trim()}
{c.students.toLocaleString('fr-FR')}
))}
{/* Activity feed */}

Activité récente

{[ { action: 'Nouvel enseignant activé', detail: 'Marie-Claire Dupont — Lyon', time: 'il y a 5 min', color: '#10B981', icon: 'check' }, { action: 'Évaluation lancée', detail: 'Algo Récursivité — L2 Info Sorbonne', time: 'il y a 12 min', color: '#6366F1', icon: 'files' }, { action: 'Crédits IA — seuil 75%', detail: 'Paris Sorbonne · Rechargement conseillé', time: 'il y a 1h', color: '#F59E0B', icon: 'sparkles' }, { action: '14 copies corrigées automatiquement', detail: 'Microéco Élasticité — L2 Éco', time: 'il y a 2h', color: '#8B5CF6', icon: 'checkCircle' }, { action: 'Import CSV — 28 étudiants', detail: 'Bordeaux Talence — L3 Droit', time: 'hier', color: '#A78BFA', icon: 'upload' }, ].map((item, i) => (
{item.action}
{item.detail}
{item.time}
))}
{/* Campus table */}
{['Campus', 'Étudiants', 'Enseignants', 'Évaluations', 'Statut'].map(h => ( ))} {D.campuses.map((c, i) => ( e.currentTarget.style.background = 'rgba(99,102,241,0.05)'} onMouseLeave={e => e.currentTarget.style.background = 'transparent'}> ))}
{h}
{c.name}
{c.students.toLocaleString('fr-FR')} {c.teachers} {c.evals} Actif
); }; // 3.2 — Gestion Campus & Groupes const AdminCampus = () => { const D = window.STUDYHUB_DATA; const [expandedCampus, setExpandedCampus] = React.useState('c1'); const groups = { c1: ['L1 Droit · Cohorte A (32)', 'L1 Droit · Cohorte B (30)', 'L2 Info · Groupe A (28)', 'L2 Info · Groupe B (26)', 'L2 Éco (42)', 'M1 Management (22)'], c2: ['L1 Sciences · A (38)', 'L2 Chimie (24)', 'M2 Physique (18)'], c3: ['L3 Droit (28)', 'L1 Lettres (40)'], c4: ['L2 Math (35)', 'M1 Informatique (20)'], c5: ['L1 Biologie (30)', 'Master Océano (15)'], }; return (

Campus & Groupes

Arborescence Établissement → Campus → Groupes
{/* Arborescence */}
{['Arborescence', 'Tableau'].map((v, i) => ( ))}
{D.campuses.map(c => (
setExpandedCampus(expandedCampus === c.id ? null : c.id)} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 16px', background: '#1E2640', border: `1px solid ${expandedCampus === c.id ? 'rgba(99,102,241,0.4)' : 'rgba(99,102,241,0.12)'}`, borderRadius: 12, cursor: 'pointer', transition: 'all 0.2s' }}>
{c.name}
{c.teachers} enseignants · {c.students} étudiants
{expandedCampus === c.id && (
{(groups[c.id] || []).map((g, j) => (
{g}
))}
)}
))}
{/* Stats panel */}

Résumé global

{[ { label: 'Total campus', val: '5' }, { label: 'Total groupes', val: '19' }, { label: 'Total étudiants', val: '8 640' }, { label: 'Avg étudiants/campus', val: '1 728' }, ].map((s, i) => (
{s.label}
{s.val}
))}

Import rapide

Importer des étudiants
CSV avec colonnes : nom, prénom, email, groupe
); }; // 3.3 — Onboarding enseignants const AdminTeachers = () => { const D = window.STUDYHUB_DATA; const [showInviteModal, setShowInviteModal] = React.useState(false); const statusBadge = (s) => { if (s === 'active') return Actif; if (s === 'pending') return En attente; return Invité; }; return (

Enseignants

Gestion des invitations, rôles et activations
{/* Stats row */}
{[ { label: 'Actifs', val: '398', color: '#10B981' }, { label: 'En attente d\'activation', val: '3', color: '#F59E0B' }, { label: 'Invitations envoyées', val: '2', color: '#6366F1' }, ].map((s, i) => (
{s.label}
{s.val}
))}
{/* Table */}
{['Enseignant', 'Email', 'Campus', 'Rôle', 'Statut', 'Dernière activité', 'Actions'].map(h => ( ))} {D.teachers.map((t, i) => ( e.currentTarget.style.background = 'rgba(99,102,241,0.05)'} onMouseLeave={e => e.currentTarget.style.background = 'transparent'}> ))}
{h}
{t.name}
{t.email} {t.campus} {t.role} {statusBadge(t.status)} {t.last}
{t.status === 'invited' && }
{/* Invite modal */} {showInviteModal && (
setShowInviteModal(false)}>
e.stopPropagation()}>

Inviter un enseignant

Email professionnel
Campus
Rôle
{['Enseignant', 'Référent pédagogique'].map((r, i) => (
)} ); }; Object.assign(window, { AdminDashboard, AdminCampus, AdminTeachers });