sépare la vitrine de l'application : vraie coquille, barre par rôle, chat
Une fois connecté, plus aucun lien vitrine : nouvelle layouts/app (barre
du haut avec menu avatar, barre latérale par rôle, bulle de chat
Messenger, barre d'onglets mobile). Les 10 entrées de navigation
demandées sont construites en vrai plutôt qu'en renvoi "bientôt
disponible" : Ma classe, Emploi du temps, Mes devoirs, Mes cours, Mes
notes côté élève ; Mes classes, Emploi du temps, Séances en direct,
Bibliothèque, Résultats (avec Observations, la dernière fonction
fictive du tableau de bord démo) côté prof.
Chat élève <-> prof de sa classe, jamais entre élèves. Mon profil,
Paramètres (thème, taille du texte), Signaler un problème, Mes
raccourcis. /eleve vidée des faux contenus ("À faire", "Ma progression",
"Mes QCM") restés avant le chantier "vrai cockpit".
Bug trouvé en testant en réel sur le serveur (pas en local) : le
compteur de messages non lus du chat ne redescendait jamais à zéro
(FilConversation sans lu_par_prof_a/lu_par_eleve_a dans son fillable) —
corrigé avec un test de non-régression.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-13 18:21:28 +02:00
@ extends ( 'layouts.app' )
2026-10-08 02:01:58 +02:00
@ section ( 'titre' , $classe -> nom )
2026-10-12 22:25:56 +02:00
{{ -- Tableau de bord prof ( chantier « vrai cockpit » , étape 1 — 2026 - 10 - 12 ) :
même mise en page que la démonstration / cockpit ( sélecteur de classe ,
cartes d ' indicateurs , tableau des élèves ), mais chaque chiffre vient
d 'une vraie table ou d' un vrai cache . Rien n ' est inventé ( CLAUDE . md ) :
les cartes des fonctions pas encore construites ( quiz , DM ,
observations , examen ) n 'existent pas ici tant qu' elles n ' existent pas
vraiment — elles arriveront une par une . -- }}
2026-10-08 02:01:58 +02:00
@ section ( 'contenu' )
2026-10-12 22:25:56 +02:00
@ php
$etats = \App\Models\LabSession :: class ;
@ endphp
< div class = " mx-auto grid max-w-[1600px] lg:grid-cols-[240px_minmax(0,1fr)] " >
< aside class = " border-b border-[#e3e5e8] bg-[#f8f9fa] p-5 lg:border-b-0 lg:border-r lg:border-[#e3e5e8] " >
< p class = " font-mono text-[11px] font-semibold uppercase tracking-wider text-[#5f6368] " > Mes classes </ p >
< label class = " mt-2 block " >
< span class = " sr-only " > Changer de classe </ span >
< select class = " h-11 w-full rounded-lg border border-[#dadce0] bg-white px-2 font-bold " onchange = " location = this.value " >
@ foreach ( $classes as $autre )
< option value = " { { route('mes-classes.show', $autre ) }} " @ selected ( $autre -> id === $classe -> id ) > {{ $autre -> nom }} ({{ $autre -> eleves_count }}) </ option >
@ endforeach
</ select >
</ label >
< p class = " mt-6 font-mono text-[11px] font-semibold uppercase tracking-wider text-[#5f6368] " > Cette classe </ p >
< ul class = " mt-2 grid gap-1 text-sm font-bold " >
< li >< a href = " #apercu " class = " flex items-center rounded-lg bg-[#0b57d0] px-3 py-2 text-white " > Vue d ' ensemble </ a ></ li >
< li >< a href = " #rejoindre " class = " flex items-center rounded-lg border-2 border-transparent px-3 py-2 hover:border-black " > Faire rejoindre </ a ></ li >
< li >< a href = " #eleves " class = " flex items-center rounded-lg border-2 border-transparent px-3 py-2 hover:border-black " > Élèves </ a ></ li >
< li >< a href = " #seance " class = " flex items-center rounded-lg border-2 border-transparent px-3 py-2 hover:border-black " > {{ $classe -> estArchivee () ? 'Classe archivée' : 'Séance' }} </ a ></ li >
2026-10-13 12:14:30 +02:00
< li >< a href = " { { route('mes-classes.quiz.index', $classe ) }} " class = " flex items-center rounded-lg border-2 border-transparent px-3 py-2 hover:border-black " > Quiz flash </ a ></ li >
2026-10-13 12:44:00 +02:00
< li >< a href = " { { route('mes-classes.devoirs.index', $classe ) }} " class = " flex items-center rounded-lg border-2 border-transparent px-3 py-2 hover:border-black " > Devoirs maison </ a ></ li >
sépare la vitrine de l'application : vraie coquille, barre par rôle, chat
Une fois connecté, plus aucun lien vitrine : nouvelle layouts/app (barre
du haut avec menu avatar, barre latérale par rôle, bulle de chat
Messenger, barre d'onglets mobile). Les 10 entrées de navigation
demandées sont construites en vrai plutôt qu'en renvoi "bientôt
disponible" : Ma classe, Emploi du temps, Mes devoirs, Mes cours, Mes
notes côté élève ; Mes classes, Emploi du temps, Séances en direct,
Bibliothèque, Résultats (avec Observations, la dernière fonction
fictive du tableau de bord démo) côté prof.
Chat élève <-> prof de sa classe, jamais entre élèves. Mon profil,
Paramètres (thème, taille du texte), Signaler un problème, Mes
raccourcis. /eleve vidée des faux contenus ("À faire", "Ma progression",
"Mes QCM") restés avant le chantier "vrai cockpit".
Bug trouvé en testant en réel sur le serveur (pas en local) : le
compteur de messages non lus du chat ne redescendait jamais à zéro
(FilConversation sans lu_par_prof_a/lu_par_eleve_a dans son fillable) —
corrigé avec un test de non-régression.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-13 18:21:28 +02:00
< li >< a href = " #annonces " class = " flex items-center rounded-lg border-2 border-transparent px-3 py-2 hover:border-black " > Annonces </ a ></ li >
< li >< a href = " #observations " class = " flex items-center rounded-lg border-2 border-transparent px-3 py-2 hover:border-black " > Observations </ a ></ li >
< li >< a href = " #chat " class = " flex items-center rounded-lg border-2 border-transparent px-3 py-2 hover:border-black " > Chat </ a ></ li >
2026-10-12 22:25:56 +02:00
</ ul >
@ if ( $seance )
< div class = " mt-6 rounded-lg border border-[#dadce0] bg-white p-3 text-sm font-semibold " >
< p class = " font-extrabold " > Séance en cours </ p >
< p class = " mt-1 " > {{ $seance -> lab -> titre }} </ p >
< p class = " mt-2 font-mono text-xs " > {{ $seance -> statut === \App\Models\Seance :: EN_COURS ? 'En cours' : 'Salle d\'attente' }} </ p >
2026-10-12 23:20:28 +02:00
@ if ( $progressionSeance !== null )
< div class = " mt-2 h-2 rounded-lg border border-[#dadce0] bg-white " >< div class = " h-full rounded-lg bg-black " style = " width: { { $progressionSeance }}% " ></ div ></ div >
< p class = " mt-1 font-mono text-[11px] text-[#5f6368] " > {{ $progressionSeance }} % du temps estimé ({{ $seance -> lab -> duree_min }} min ) </ p >
@ endif
2026-10-12 22:25:56 +02:00
</ div >
@ endif
</ aside >
< div class = " min-w-0 p-5 lg:p-8 " >
< div id = " apercu " class = " flex flex-wrap items-end justify-between gap-4 " >
2026-10-08 02:01:58 +02:00
< div >
2026-10-12 22:25:56 +02:00
< p class = " font-mono text-xs font-semibold text-[#5f6368] " > Tableau de bord </ p >
< h1 class = " mt-1 text-4xl font-extrabold tracking-tight " > {{ $classe -> nom }} </ h1 >
2026-10-08 02:01:58 +02:00
@ if ( $classe -> formation )
< p class = " mt-1 text-lg font-semibold " > {{ $classe -> formation }} </ p >
@ endif
</ div >
< p class = " font-mono text-sm font-semibold " > Code de classe : < span class = " rounded-lg border border-[#dadce0] bg-white px-2 py-1 " > {{ $classe -> code_classe }} </ span ></ p >
</ div >
@ if ( session ( 'classe_creee' ))
2026-10-12 22:25:56 +02:00
< p class = " mt-4 rounded-lg border border-[#e3e5e8] bg-[#e6f4ea] p-4 font-semibold " > Classe créée . Ajoute maintenant tes élèves ci - dessous .</ p >
@ endif
{{ -- Indicateurs réels : rien ici qui ne vienne d 'une vraie table ou d' un vrai cache . -- }}
< ul class = " mt-6 grid grid-cols-2 gap-4 md:grid-cols-3 xl:grid-cols-4 " aria - label = " Indicateurs " >
< li class = " flex flex-col rounded-lg border border-[#dadce0] bg-white p-4 " >
< p class = " text-sm font-bold " > Élèves </ p >
< p class = " mt-2 font-mono text-3xl font-semibold leading-none " > {{ $classe -> eleves -> count () }} </ p >
</ li >
< li class = " flex flex-col rounded-lg border border-[#dadce0] bg-white p-4 " >
< p class = " text-sm font-bold " > Demandes en attente </ p >
< p class = " mt-2 font-mono text-3xl font-semibold leading-none " > {{ $demandes -> count () }} </ p >
</ li >
@ if ( $seance )
< li class = " flex flex-col rounded-lg border border-[#dadce0] bg-white p-4 " >
< p class = " text-sm font-bold " > Dans la séance </ p >
< p class = " mt-2 font-mono text-3xl font-semibold leading-none " > {{ $seance -> labSessions -> whereIn ( 'etat' , [ $etats :: ACTIF , $etats :: ADMIS ]) -> count () }} / {{ $classe -> eleves -> count () }} </ p >
< p class = " mt-1 text-xs font-bold text-[#5f6368] " > admis ou en préparation </ p >
</ li >
@ if ( $reussiteMoyenne !== null )
< li class = " flex flex-col rounded-lg border border-[#dadce0] bg-white p-4 " >
< p class = " text-sm font-bold " > Réussite ( dernière vérif . ) </ p >
< p class = " mt-2 font-mono text-3xl font-semibold leading-none " > {{ $reussiteMoyenne }} %</ p >
</ li >
@ endif
2026-10-12 23:20:28 +02:00
@ if ( $seance -> visioOuverte ())
2026-10-12 22:25:56 +02:00
< li class = " flex flex-col rounded-lg border border-[#dadce0] bg-[#fce8e6] p-4 " >
< p class = " text-sm font-bold " > Mains levées ( visio ) </ p >
2026-10-12 23:20:28 +02:00
< p class = " mt-2 font-mono text-3xl font-semibold leading-none " > {{ count ( $mains ) }} </ p >
2026-10-12 22:25:56 +02:00
</ li >
@ endif
@ endif
</ ul >
2026-10-12 23:20:28 +02:00
@ if ( $seance && $avancement -> isNotEmpty ())
{{ -- Équivalent réel de la « répartition par étape » de la démo :
une barre par vérification du lab , comptée sur les vraies
dernières corrections ( pas de fausse étape numérotée ) . -- }}
< section class = " mt-6 rounded-lg border border-[#dadce0] bg-white p-5 " aria - labelledby = " t-avancement " >
< div class = " flex flex-wrap items-baseline justify-between gap-2 " >
< h2 id = " t-avancement " class = " text-lg font-extrabold " > Avancement du lab « {{ $seance -> lab -> titre }} » </ h2 >
< p class = " font-mono text-xs font-semibold text-[#5f6368] " > {{ $avancement -> count () }} vérifications · {{ $avancement -> first ()[ 'total' ] }} élève ( s ) corrigé ( s ) </ p >
</ div >
< ul class = " mt-4 grid gap-2 " >
@ foreach ( $avancement as $v )
< li class = " flex items-center gap-3 " >
< span class = " w-40 shrink-0 truncate text-sm font-bold " title = " { { $v['titre'] }} " > {{ $v [ 'titre' ] }} </ span >
< span class = " h-3 flex-1 rounded-lg border border-[#dadce0] bg-[#f8f9fa] " >< span class = " block h-full rounded-lg bg-[#3C8A5A] " style = " width: { { $v['total'] ? round( $v['reussi'] / $v['total'] * 100) : 0 }}% " ></ span ></ span >
< span class = " w-14 shrink-0 text-right font-mono text-sm font-semibold " > {{ $v [ 'reussi' ] }} / {{ $v [ 'total' ] }} </ span >
</ li >
@ endforeach
</ ul >
</ section >
@ endif
@ if ( $seance && $seance -> visioOuverte ())
{{ -- Équivalent réel du panneau « Mains levées » de la démo : la
même file que la page de visio , avec la même action ( donner
la parole ), juste accessible sans quitter le tableau de bord . -- }}
< section class = " mt-6 rounded-lg border border-[#dadce0] bg-white " aria - labelledby = " t-mains " >
< div class = " flex items-center justify-between border-b border-[#e3e5e8] bg-[#fce8e6] p-4 " >
< h2 id = " t-mains " class = " text-lg font-extrabold " > Mains levées </ h2 >
< span class = " font-mono text-xs font-semibold " > ordre d ' arrivée </ span >
</ div >
@ if ( empty ( $mains ))
< p class = " p-4 font-semibold " > Aucune main levée pour l ' instant .</ p >
@ else
< ol class = " divide-y divide-[#e3e5e8] " >
@ foreach ( $mains as $i => $main )
< li class = " flex items-center justify-between gap-3 p-4 " >
< span class = " flex items-center gap-3 " >
< span class = " font-mono text-sm font-semibold " > {{ $i + 1 }} </ span >
< span class = " font-extrabold " > {{ $main [ 'nom' ] }} </ span >
@ php ( $attente = max ( 0 , time () - $main [ 'depuis' ]))
< span class = " font-mono text-xs text-[#5f6368] " > · depuis {{ intdiv ( $attente , 60 ) }} min {{ str_pad (( string ) ( $attente % 60 ), 2 , '0' , STR_PAD_LEFT ) }} s </ span >
</ span >
< form action = " { { route('seances.visio.parole', [ $seance , $main['eleve_id'] ]) }} " method = " post " >
@ csrf
< button type = " submit " class = " inline-flex h-9 items-center rounded-lg bg-black px-3 text-xs font-extrabold text-white hover:bg-[#ec3013] " > Donner la parole </ button >
</ form >
</ li >
@ endforeach
</ ol >
@ endif
</ section >
@ endif
2026-10-12 22:25:56 +02:00
@ if ( $seance )
{{ -- Vraies données de la séance en cours : un élève , son état , sa dernière vérification . -- }}
< section class = " mt-6 rounded-lg border border-[#dadce0] bg-white " aria - labelledby = " t-seance-encours " >
< div class = " flex flex-wrap items-center justify-between gap-3 border-b border-[#e3e5e8] p-4 " >
2026-10-14 00:35:52 +02:00
< h2 id = " t-seance-encours " class = " text-lg font-extrabold " > {{ $seance -> lab -> titre }} — {{ $seance -> statut === \App\Models\Seance :: EN_COURS ? 'en cours' : 'salle d\'attente' }}
@ if ( $seance -> mode_examen )
< span class = " rounded-lg border border-[#A63D2A] bg-[#fce8e6] px-2 py-0.5 font-mono text-xs font-bold align-middle " > Mode examen · {{ $seance -> duree_examen_min }} min </ span >
@ endif
</ h2 >
2026-10-12 22:25:56 +02:00
< span class = " flex flex-wrap gap-2 " >
< a href = " { { route('seances.attente', $seance ) }} " class = " inline-flex h-10 items-center rounded-lg border border-[#dadce0] px-4 text-sm font-extrabold hover:bg-[#f8f9fa] " > Salle d ' attente </ a >
< a href = " { { route('seances.visio.prof', $seance ) }} " class = " inline-flex h-10 items-center rounded-lg bg-[#0b57d0] px-4 text-sm font-extrabold text-white " > {{ $seance -> visioOuverte () ? 'Rejoindre la visio' : 'Lancer la visio' }} </ a >
</ span >
</ div >
@ if ( $seance -> labSessions -> isEmpty ())
< p class = " p-4 font-semibold " > Personne n ' a encore rejoint .</ p >
@ else
< div class = " overflow-x-auto " >< table class = " w-full min-w-[640px] border-collapse text-left text-sm " >
< thead class = " border-b border-[#e3e5e8] bg-[#f8f9fa] font-mono text-[11px] uppercase tracking-wider " >
2026-10-14 00:35:52 +02:00
< tr >< th class = " px-4 py-3 font-semibold " > Élève </ th >< th class = " px-4 py-3 font-semibold " > État </ th >< th class = " px-4 py-3 font-semibold " > Dernière vérification </ th >
@ if ( $seance -> mode_examen ) < th class = " px-4 py-3 font-semibold " > Sorties </ th >@ endif
< th class = " px-4 py-3 font-semibold " ></ th ></ tr >
2026-10-12 22:25:56 +02:00
</ thead >
< tbody class = " divide-y divide-[#e3e5e8] " >
@ foreach ( $seance -> labSessions as $labSession )
< tr >
< td class = " px-4 py-3 font-extrabold " > {{ $labSession -> eleve -> nomAffiche () }} </ td >
< td class = " px-4 py-3 " >
@ switch ( $labSession -> etat )
@ case ( $etats :: EN_ATTENTE )
< form action = " { { route('seances.admettre', [ $seance , $labSession ]) }} " method = " post " >@ csrf < button type = " submit " class = " inline-flex h-9 items-center rounded-lg bg-[#0b57d0] px-3 text-xs font-extrabold text-white " > Admettre </ button ></ form >
@ break
@ case ( $etats :: ADMIS )
< span class = " rounded-lg border border-[#dadce0] bg-[#fef7e0] px-2 py-0.5 font-mono text-xs font-bold " > Préparation des VM… </ span >
@ break
@ case ( $etats :: ACTIF )
< span class = " rounded-lg border border-[#dadce0] bg-[#e6f4ea] px-2 py-0.5 font-mono text-xs font-bold " > Admis </ span >
@ break
@ case ( $etats :: ECHEC )
< span class = " rounded-lg border border-[#A63D2A] bg-[#fce8e6] px-2 py-0.5 font-mono text-xs font-bold " title = " { { $labSession->raison_echec }} " > Échec ( 3 essais ) </ span >
@ break
@ default
< span class = " rounded-lg border border-[#dadce0] px-2 py-0.5 font-mono text-xs font-bold " > Terminé </ span >
@ endswitch
</ td >
< td class = " px-4 py-3 font-mono font-semibold " >
@ if ( $labSession -> derniereCorrection )
< span class = " { { $labSession->derniereCorrection ->reussi ? 'text-[#3C8A5A]' : '' }} " > {{ $labSession -> derniereCorrection -> reussi ? 'Réussi' : 'En cours' }} · {{ $labSession -> derniereCorrection -> note }} / {{ $labSession -> derniereCorrection -> note_max }} </ span >
@ else
—
@ endif
</ td >
2026-10-14 00:35:52 +02:00
@ if ( $seance -> mode_examen )
< td class = " px-4 py-3 font-mono font-semibold " >
@ if ( $labSession -> evenementsExamen -> isNotEmpty ())
< span class = " text-[#A63D2A] " > {{ $labSession -> evenementsExamen -> count () }} </ span >
@ else
0
@ endif
</ td >
@ endif
2026-10-12 22:25:56 +02:00
< td class = " px-4 py-3 text-right " >
@ if ( $labSession -> etat === $etats :: ACTIF )
< a href = " { { route('seances.console.prof.page', [ $seance , $labSession ]) }} " class = " font-bold underline underline-offset-4 " > Voir sa console </ a >
@ endif
</ td >
</ tr >
@ endforeach
</ tbody >
</ table ></ div >
@ endif
</ section >
2026-10-08 02:01:58 +02:00
@ endif
2026-10-09 22:18:53 +02:00
@ unless ( $classe -> estArchivee ())
2026-10-12 22:25:56 +02:00
< section id = " rejoindre " class = " mt-6 grid gap-6 rounded-lg border border-[#dadce0] bg-white p-6 md:grid-cols-[1fr_auto] " aria - labelledby = " t-rejoindre " >
2026-10-09 22:18:53 +02:00
< div >
< h2 id = " t-rejoindre " class = " text-2xl font-extrabold " > Faire rejoindre tes élèves </ h2 >
< p class = " mt-2 font-semibold " > Projette ce code ou ce QR . Chaque élève va sur < span class = " font-mono " > {{ preg_replace ( '#^https?://#' , '' , route ( 'rejoindre' )) }} </ span > , tape le code et son prénom ; tu acceptes sa demande ici , et il reçoit à l ' écran son identifiant et son code secret . Aucun e - mail .</ p >
< p class = " mt-4 font-mono text-5xl font-bold tracking-[0.3em] " > {{ $classe -> code_classe }} </ p >
< h3 class = " mt-6 text-lg font-extrabold " > Demandes en attente ({{ $demandes -> count () }}) </ h3 >
@ if ( $demandes -> isEmpty ())
< p class = " mt-2 font-semibold text-[#5f6368] " > Aucune pour l ' instant . Cette liste se met à jour toute seule .</ p >
@ else
< ul class = " mt-2 grid gap-2 " >
@ foreach ( $demandes as $demande )
2026-10-12 22:25:56 +02:00
< li class = " flex flex-wrap items-center justify-between gap-3 rounded-lg border border-[#e3e5e8] p-3 " >
2026-10-09 22:18:53 +02:00
< span class = " font-extrabold " > {{ $demande -> prenom }} @ if ( $demande -> nom ) {{ $demande -> nom }} @ endif < span class = " font-mono text-sm font-semibold text-[#5f6368] " > · {{ $demande -> created_at -> diffForHumans () }} </ span ></ span >
< span class = " flex gap-2 " >
< form action = " { { route('mes-classes.demandes.accepter', [ $classe , $demande ]) }} " method = " post " >@ csrf < button type = " submit " class = " inline-flex h-10 items-center rounded-full bg-[#0b57d0] px-4 font-bold text-white " > Accepter </ button ></ form >
< form action = " { { route('mes-classes.demandes.refuser', [ $classe , $demande ]) }} " method = " post " >@ csrf < button type = " submit " class = " inline-flex h-10 items-center rounded-full border border-[#dadce0] px-4 font-bold " > Refuser </ button ></ form >
</ span >
</ li >
@ endforeach
</ ul >
@ endif
</ div >
< div class = " justify-self-center " > { !! \App\Support\QrCode :: svg ( route ( 'rejoindre' , [ 'code' => $classe -> code_classe ])) !! } </ div >
</ section >
@ endunless
2026-10-08 02:01:58 +02:00
@ if ( session ( 'fiches' ))
2026-10-12 22:25:56 +02:00
< section class = " mt-6 rounded-lg border border-[#e3e5e8] bg-[#fef7e0] p-6 print:border-0 print:bg-white " aria - labelledby = " t-fiches " >
2026-10-08 02:01:58 +02:00
< div class = " flex flex-wrap items-center justify-between gap-3 print:hidden " >
< h2 id = " t-fiches " class = " text-2xl font-extrabold " >
Fiches à imprimer
@ if ( session ( 'nombre_importes' ))
< span class = " font-mono text-sm font-semibold " > ({{ session ( 'nombre_importes' ) }} élève ( s ) importé ( s )) </ span >
@ endif
</ h2 >
2026-10-12 22:25:56 +02:00
< button type = " button " data - imprimer class = " rounded-lg border border-[#dadce0] bg-black px-4 py-2 font-extrabold text-white " > Imprimer </ button >
2026-10-08 02:01:58 +02:00
</ div >
< p class = " mt-2 font-semibold print:hidden " > Le code secret ne sera plus jamais affiché après cette page : imprime ou note - le maintenant .</ p >
< div class = " mt-6 grid gap-4 sm:grid-cols-2 print:grid-cols-1 " >
@ foreach ( session ( 'fiches' ) as $fiche )
2026-10-12 22:25:56 +02:00
< div class = " flex items-center gap-4 rounded-lg border border-[#dadce0] bg-white p-5 print:break-inside-avoid " >
2026-10-08 02:01:58 +02:00
< div class = " shrink-0 " > { !! \App\Support\QrCode :: svg ( route ( 'connexion.qr' , $fiche [ 'jeton_qr' ])) !! } </ div >
< div class = " min-w-0 " >
< p class = " text-lg font-extrabold " > {{ $fiche [ 'prenom' ] }} @ if ( $fiche [ 'nom' ]) {{ $fiche [ 'nom' ] }} @ endif </ p >
< p class = " mt-2 font-mono text-sm " > Identifiant < br >< span class = " text-lg font-bold " > {{ $fiche [ 'identifiant' ] }} </ span ></ p >
< p class = " mt-2 font-mono text-sm " > Code secret < br >< span class = " text-lg font-bold " > {{ $fiche [ 'code_secret' ] }} </ span ></ p >
</ div >
</ div >
@ endforeach
</ div >
</ section >
@ endif
2026-10-12 22:25:56 +02:00
< div class = " mt-6 grid gap-6 sm:grid-cols-2 " >
< section class = " rounded-lg border border-[#dadce0] bg-white p-6 " aria - labelledby = " t-ajout " >
2026-10-08 02:01:58 +02:00
< h2 id = " t-ajout " class = " text-xl font-extrabold " > Ajouter un élève </ h2 >
@ error ( 'prenom' ) < p class = " mt-2 font-bold text-[#A63D2A] " > {{ $message }} </ p >@ enderror
< form action = " { { route('mes-classes.eleves.store', $classe ) }} " method = " post " class = " mt-4 grid gap-3 " >
@ csrf
< label class = " grid gap-1 font-bold " > Prénom < input name = " prenom " required class = " w-full rounded-xl border border-[#c4c7c5] bg-white px-3 py-2 font-semibold " ></ label >
< label class = " grid gap-1 font-bold " > Nom < span class = " font-medium " > ( facultatif , visible de toi seul ) </ span >< input name = " nom " class = " w-full rounded-xl border border-[#c4c7c5] bg-white px-3 py-2 font-semibold " ></ label >
< button type = " submit " class = " mt-2 inline-flex h-11 items-center justify-center rounded-full bg-[#0b57d0] px-5 font-bold text-white " > Ajouter </ button >
</ form >
</ section >
2026-10-12 22:25:56 +02:00
< section class = " rounded-lg border border-[#dadce0] bg-white p-6 " aria - labelledby = " t-import " >
2026-10-08 02:01:58 +02:00
< h2 id = " t-import " class = " text-xl font-extrabold " > Importer une liste ( CSV ) </ h2 >
< p class = " mt-2 font-semibold " > Une ligne par élève : < code class = " font-mono " > prenom , nom </ code > ( le nom peut être vide ) .</ p >
@ error ( 'fichier' ) < p class = " mt-2 font-bold text-[#A63D2A] " > {{ $message }} </ p >@ enderror
< form action = " { { route('mes-classes.eleves.importer', $classe ) }} " method = " post " enctype = " multipart/form-data " class = " mt-4 grid gap-3 " >
@ csrf
< input type = " file " name = " fichier " accept = " .csv,text/csv " required class = " w-full rounded-xl border border-[#c4c7c5] bg-white px-3 py-2 font-semibold " >
< button type = " submit " class = " mt-2 inline-flex h-11 items-center justify-center rounded-full bg-[#0b57d0] px-5 font-bold text-white " > Importer </ button >
</ form >
</ section >
</ div >
2026-10-12 22:25:56 +02:00
< section id = " eleves " class = " mt-6 rounded-lg border border-[#dadce0] bg-white " aria - labelledby = " t-eleves " >
2026-10-09 22:18:53 +02:00
< div class = " flex flex-wrap items-center justify-between gap-3 p-6 pb-0 " >
< h2 id = " t-eleves " class = " text-2xl font-extrabold " > Élèves ({{ $classe -> eleves -> count () }}) </ h2 >
@ if ( $classe -> eleves -> isNotEmpty ())
< form id = " form-fiches " action = " { { route('mes-classes.fiches', $classe ) }} " method = " post " data - confirmer = " Générer de nouvelles fiches pour les élèves cochés ? Leurs anciens codes ne fonctionneront plus. " >
@ csrf
< button type = " submit " class = " inline-flex h-10 items-center rounded-full border border-[#0b57d0] px-4 font-bold text-[#0b57d0] " > Nouvelles fiches pour la sélection </ button >
</ form >
@ endif
</ div >
@ error ( 'eleves' ) < p class = " px-6 pt-2 font-semibold text-[#A63D2A] " > Coche au moins un élève .</ p >@ enderror
2026-10-08 02:01:58 +02:00
@ if ( $classe -> eleves -> isEmpty ())
< p class = " p-6 font-semibold " > Aucun élève pour l ' instant .</ p >
@ else
< div class = " overflow-x-auto " >
< table class = " mt-4 w-full min-w-[480px] text-left " >
2026-10-09 22:18:53 +02:00
< thead class = " bg-[#0b57d0] text-white " >< tr >< th class = " px-4 py-2 " >< input type = " checkbox " data - tout - cocher aria - label = " Tout sélectionner " ></ th >< th class = " px-4 py-2 " > Élève </ th >< th class = " px-4 py-2 font-mono text-xs " > Identifiant </ th >< th class = " px-4 py-2 " > Dernière connexion </ th >< th class = " px-4 py-2 " ></ th ></ tr ></ thead >
2026-10-08 02:01:58 +02:00
< tbody class = " divide-y divide-[#e3e5e8] " >
@ foreach ( $classe -> eleves as $eleve )
< tr >
2026-10-09 22:18:53 +02:00
< td class = " px-4 py-3 " >< input type = " checkbox " name = " eleves[] " value = " { { $eleve->id }} " form = " form-fiches " aria - label = " Sélectionner { { $eleve->prenom }} " ></ td >
2026-10-08 02:01:58 +02:00
< td class = " px-4 py-3 font-extrabold " > {{ $eleve -> nomAffiche () }} </ td >
< td class = " px-4 py-3 font-mono text-sm " > {{ $eleve -> identifiant }} </ td >
< td class = " px-4 py-3 font-semibold " > {{ $eleve -> derniere_connexion_a ? -> diffForHumans () ? ? 'jamais' }} </ td >
< td class = " px-4 py-3 " >
2026-10-09 10:39:50 +02:00
< form action = " { { route('mes-classes.eleves.regenererCode', [ $classe , $eleve ]) }} " method = " post " data - confirmer = " Régénérer le code de { { $eleve->prenom }} ? L’ ancien code ne fonctionnera plus. " >
2026-10-08 02:01:58 +02:00
@ csrf
< button type = " submit " class = " font-bold underline underline-offset-4 " > Régénérer le code </ button >
</ form >
</ td >
</ tr >
@ endforeach
</ tbody >
</ table >
</ div >
@ endif
</ section >
sépare la vitrine de l'application : vraie coquille, barre par rôle, chat
Une fois connecté, plus aucun lien vitrine : nouvelle layouts/app (barre
du haut avec menu avatar, barre latérale par rôle, bulle de chat
Messenger, barre d'onglets mobile). Les 10 entrées de navigation
demandées sont construites en vrai plutôt qu'en renvoi "bientôt
disponible" : Ma classe, Emploi du temps, Mes devoirs, Mes cours, Mes
notes côté élève ; Mes classes, Emploi du temps, Séances en direct,
Bibliothèque, Résultats (avec Observations, la dernière fonction
fictive du tableau de bord démo) côté prof.
Chat élève <-> prof de sa classe, jamais entre élèves. Mon profil,
Paramètres (thème, taille du texte), Signaler un problème, Mes
raccourcis. /eleve vidée des faux contenus ("À faire", "Ma progression",
"Mes QCM") restés avant le chantier "vrai cockpit".
Bug trouvé en testant en réel sur le serveur (pas en local) : le
compteur de messages non lus du chat ne redescendait jamais à zéro
(FilConversation sans lu_par_prof_a/lu_par_eleve_a dans son fillable) —
corrigé avec un test de non-régression.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-13 18:21:28 +02:00
< section id = " annonces " class = " mt-6 rounded-lg border border-[#dadce0] bg-white p-6 " aria - labelledby = " t-annonces " >
< h2 id = " t-annonces " class = " text-2xl font-extrabold " > Annonces </ h2 >
< p class = " mt-2 font-semibold " > Visibles par tes élèves dans « Ma classe » .</ p >
< form action = " { { route('mes-classes.annonces.store', $classe ) }} " method = " post " class = " mt-4 grid gap-3 " >
@ csrf
< label class = " grid gap-1 font-bold " > Titre < input name = " titre " required maxlength = " 150 " class = " w-full rounded-xl border border-[#c4c7c5] bg-white px-3 py-2 font-semibold " ></ label >
< label class = " grid gap-1 font-bold " > Message < textarea name = " contenu " required maxlength = " 3000 " rows = " 3 " class = " w-full rounded-xl border border-[#c4c7c5] bg-white px-3 py-2 font-semibold " ></ textarea ></ label >
< button type = " submit " class = " mt-1 inline-flex h-11 w-fit items-center rounded-full bg-[#0b57d0] px-5 font-bold text-white " > Publier </ button >
</ form >
@ if ( $classe -> annonces -> isNotEmpty ())
< ul class = " mt-6 grid gap-3 " >
@ foreach ( $classe -> annonces as $annonce )
< li class = " flex flex-wrap items-start justify-between gap-3 rounded-xl border border-[#e3e5e8] p-4 " >
< div >
< p class = " font-extrabold " > {{ $annonce -> titre }} </ p >
< p class = " mt-1 font-semibold " > {{ $annonce -> contenu }} </ p >
< p class = " mt-2 font-mono text-xs text-[#5f6368] " > {{ $annonce -> created_at -> translatedFormat ( 'd/m à H:i' ) }} </ p >
</ div >
< form action = " { { route('mes-classes.annonces.destroy', [ $classe , $annonce ]) }} " method = " post " >
@ csrf
< button type = " submit " class = " font-bold underline underline-offset-4 " > Retirer </ button >
</ form >
</ li >
@ endforeach
</ ul >
@ endif
</ section >
@ if ( $classe -> eleves -> isNotEmpty ())
< section id = " observations " class = " mt-6 rounded-lg border border-[#dadce0] bg-white p-6 " aria - labelledby = " t-observations " >
< h2 id = " t-observations " class = " text-2xl font-extrabold " > Observations </ h2 >
< p class = " mt-2 font-semibold " > Notes rapides pour le conseil de classe — jamais montrées à l ' élève .</ p >
< form action = " { { route('mes-classes.observations.store', $classe ) }} " method = " post " class = " mt-4 grid gap-3 " >
@ csrf
< label class = " grid gap-1 font-bold " > Élève < select name = " eleve_id " required class = " rounded-xl border border-[#c4c7c5] bg-white px-3 py-2 font-semibold " >
@ foreach ( $classe -> eleves as $eleveOption )
< option value = " { { $eleveOption->id }} " > {{ $eleveOption -> nomAffiche () }} </ option >
@ endforeach
</ select ></ label >
< label class = " grid gap-1 font-bold " > Observation < textarea name = " contenu " required maxlength = " 2000 " rows = " 3 " class = " w-full rounded-xl border border-[#c4c7c5] bg-white px-3 py-2 font-semibold " ></ textarea ></ label >
< button type = " submit " class = " mt-1 inline-flex h-11 w-fit items-center rounded-full bg-[#0b57d0] px-5 font-bold text-white " > Ajouter </ button >
</ form >
@ if ( $classe -> observations -> isNotEmpty ())
< ul class = " mt-6 grid gap-2 " >
@ foreach ( $classe -> observations as $observation )
< li class = " flex flex-wrap items-start justify-between gap-3 rounded-xl border border-[#e3e5e8] p-3 " >
< p class = " font-semibold " >< span class = " font-extrabold " > {{ $observation -> eleve -> nomAffiche () }} </ span > — {{ $observation -> contenu }}
< span class = " font-mono text-xs text-[#5f6368] " > ({{ $observation -> created_at -> translatedFormat ( 'd/m' ) }}) </ span ></ p >
< form action = " { { route('mes-classes.observations.destroy', [ $classe , $observation ]) }} " method = " post " >
@ csrf
< button type = " submit " class = " font-bold underline underline-offset-4 " > Retirer </ button >
</ form >
</ li >
@ endforeach
</ ul >
@ endif
</ section >
@ endif
< section id = " chat " class = " mt-6 rounded-lg border border-[#dadce0] bg-white p-6 " aria - labelledby = " t-chat " >
< h2 id = " t-chat " class = " text-2xl font-extrabold " > Chat </ h2 >
< p class = " mt-2 font-semibold " >
{{ $classe -> chat_actif ? 'Activé : tes élèves peuvent t\'écrire depuis la bulle en bas à droite.' : 'Désactivé : tes élèves ne peuvent plus t\'écrire.' }}
</ p >
< form action = " { { route('mes-classes.chat.basculer', $classe ) }} " method = " post " class = " mt-4 " >
@ csrf
< button type = " submit " class = " inline-flex h-11 items-center rounded-full border border-[#dadce0] px-5 font-bold " > {{ $classe -> chat_actif ? 'Désactiver le chat' : 'Activer le chat' }} </ button >
</ form >
</ section >
2026-10-12 22:25:56 +02:00
< div id = " seance " >
2026-10-09 21:07:25 +02:00
@ if ( $classe -> estArchivee ())
2026-10-12 22:25:56 +02:00
< section class = " mt-6 rounded-lg border border-[#A63D2A] bg-[#fce8e6] p-6 " aria - labelledby = " t-archivee " >
2026-10-09 21:07:25 +02:00
< h2 id = " t-archivee " class = " text-2xl font-extrabold " > Classe archivée </ h2 >
< p class = " mt-2 font-semibold " >
Archivée le {{ $classe -> archivee_a -> timezone ( config ( 'app.fuseau_utilisateurs' )) -> format ( 'd/m/Y' ) }} .
La classe et toutes les données de ses élèves ( comptes , séances , résultats ) seront
< strong > supprimées définitivement le {{ $classe -> supprimeeLe () -> timezone ( config ( 'app.fuseau_utilisateurs' )) -> format ( 'd/m/Y' ) }} </ strong >.
</ p >
< form action = " { { route('mes-classes.desarchiver', $classe ) }} " method = " post " class = " mt-4 " >
@ csrf
< button type = " submit " class = " inline-flex h-11 items-center rounded-full bg-black px-5 font-bold text-white " > Désarchiver la classe </ button >
</ form >
</ section >
@ else
2026-10-12 22:25:56 +02:00
< section class = " mt-6 rounded-lg border border-[#dadce0] bg-white p-6 " aria - labelledby = " t-seance " >
ajoute le stock chaud, la correction automatique et les nouveaux essais de création de stack
Stock de stacks prêtes par lab dans Redis (prise atomique en Lua, ménage
par comparaison avec Heat, disjoncteur), files gérées par Horizon, séances
planifiables. Correcteur Go sur l'hyperviseur : socket Unix, commandes lues
dans le lab.yaml uniquement, exécution par l'agent invité QEMU. Une création
de stack ratée est supprimée et réessayée deux fois, puis la session passe
en échec, visible du prof.
Corrige aussi trois défauts des étapes précédentes : la console se coupait
à la première frappe d'un navigateur (trames texte), l'élève ne pouvait pas
rejoindre une séance, la déconnexion n'avait pas de jeton CSRF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:52:16 +02:00
< h2 id = " t-seance " class = " text-2xl font-extrabold " > Lancer ou planifier une séance </ h2 >
< p class = " mt-2 font-semibold " > Les élèves qui rejoignent patientent dans la salle d 'attente jusqu' à ce que tu les admettes . Si tu planifies la séance , les machines de toute la classe sont préparées 10 minutes avant : elles s ' ouvrent alors en quelques secondes .</ p >
@ if ( session ( 'seance_planifiee' ))
2026-10-12 22:25:56 +02:00
< p class = " mt-4 rounded-lg border border-[#e3e5e8] bg-[#e6f4ea] p-4 font-semibold " > Séance planifiée .</ p >
ajoute le stock chaud, la correction automatique et les nouveaux essais de création de stack
Stock de stacks prêtes par lab dans Redis (prise atomique en Lua, ménage
par comparaison avec Heat, disjoncteur), files gérées par Horizon, séances
planifiables. Correcteur Go sur l'hyperviseur : socket Unix, commandes lues
dans le lab.yaml uniquement, exécution par l'agent invité QEMU. Une création
de stack ratée est supprimée et réessayée deux fois, puis la session passe
en échec, visible du prof.
Corrige aussi trois défauts des étapes précédentes : la console se coupait
à la première frappe d'un navigateur (trames texte), l'élève ne pouvait pas
rejoindre une séance, la déconnexion n'avait pas de jeton CSRF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:52:16 +02:00
@ endif
2026-10-14 00:35:52 +02:00
< form action = " { { route('mes-classes.seances.store', $classe ) }} " method = " post " class = " mt-4 flex flex-wrap items-end gap-3 " id = " form-seance " >
2026-10-08 02:01:58 +02:00
@ csrf
< label class = " grid gap-1 font-bold " > Lab < select name = " lab_id " required class = " rounded-xl border border-[#c4c7c5] bg-white px-3 py-2 font-semibold " >
2026-10-14 07:34:55 +02:00
@ foreach ( $labsCatalogue as $lab )
2026-10-08 02:01:58 +02:00
< option value = " { { $lab->id }} " > {{ $lab -> titre }} </ option >
@ endforeach
</ select ></ label >
ajoute le stock chaud, la correction automatique et les nouveaux essais de création de stack
Stock de stacks prêtes par lab dans Redis (prise atomique en Lua, ménage
par comparaison avec Heat, disjoncteur), files gérées par Horizon, séances
planifiables. Correcteur Go sur l'hyperviseur : socket Unix, commandes lues
dans le lab.yaml uniquement, exécution par l'agent invité QEMU. Une création
de stack ratée est supprimée et réessayée deux fois, puis la session passe
en échec, visible du prof.
Corrige aussi trois défauts des étapes précédentes : la console se coupait
à la première frappe d'un navigateur (trames texte), l'élève ne pouvait pas
rejoindre une séance, la déconnexion n'avait pas de jeton CSRF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:52:16 +02:00
< label class = " grid gap-1 font-bold " > Date et heure ( facultatif ) < input type = " datetime-local " name = " prevue_a " value = " { { old('prevue_a') }} " class = " rounded-xl border border-[#c4c7c5] bg-white px-3 py-2 font-semibold " ></ label >
2026-10-14 00:35:52 +02:00
< label class = " flex items-center gap-2 font-bold " >< input type = " checkbox " name = " mode_examen " value = " 1 " id = " case-mode-examen " > Mode examen </ label >
< label class = " grid gap-1 font-bold " id = " champ-duree-examen " hidden > Durée ( minutes ) < input type = " number " name = " duree_examen_min " min = " 5 " max = " 300 " value = " { { old('duree_examen_min', 60) }} " class = " w-28 rounded-xl border border-[#c4c7c5] bg-white px-3 py-2 font-semibold " ></ label >
ajoute le stock chaud, la correction automatique et les nouveaux essais de création de stack
Stock de stacks prêtes par lab dans Redis (prise atomique en Lua, ménage
par comparaison avec Heat, disjoncteur), files gérées par Horizon, séances
planifiables. Correcteur Go sur l'hyperviseur : socket Unix, commandes lues
dans le lab.yaml uniquement, exécution par l'agent invité QEMU. Une création
de stack ratée est supprimée et réessayée deux fois, puis la session passe
en échec, visible du prof.
Corrige aussi trois défauts des étapes précédentes : la console se coupait
à la première frappe d'un navigateur (trames texte), l'élève ne pouvait pas
rejoindre une séance, la déconnexion n'avait pas de jeton CSRF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:52:16 +02:00
< button type = " submit " class = " inline-flex h-11 items-center justify-center rounded-full bg-black px-5 font-bold text-white " > Ouvrir ou planifier </ button >
2026-10-08 02:01:58 +02:00
</ form >
ajoute le stock chaud, la correction automatique et les nouveaux essais de création de stack
Stock de stacks prêtes par lab dans Redis (prise atomique en Lua, ménage
par comparaison avec Heat, disjoncteur), files gérées par Horizon, séances
planifiables. Correcteur Go sur l'hyperviseur : socket Unix, commandes lues
dans le lab.yaml uniquement, exécution par l'agent invité QEMU. Une création
de stack ratée est supprimée et réessayée deux fois, puis la session passe
en échec, visible du prof.
Corrige aussi trois défauts des étapes précédentes : la console se coupait
à la première frappe d'un navigateur (trames texte), l'élève ne pouvait pas
rejoindre une séance, la déconnexion n'avait pas de jeton CSRF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:52:16 +02:00
@ error ( 'prevue_a' )
< p class = " mt-2 font-semibold text-[#A63D2A] " > {{ $message }} </ p >
@ enderror
2026-10-14 00:35:52 +02:00
@ error ( 'duree_examen_min' )
< p class = " mt-2 font-semibold text-[#A63D2A] " > {{ $message }} </ p >
@ enderror
< p class = " mt-2 text-sm font-semibold text-[#5f6368] " > Mode examen : chronométré , détecte les changements d 'onglet et propose le plein écran. Ça ne bloque pas vraiment l' élève — un navigateur ne le permet pas — mais tu vois les sorties .</ p >
ajoute le stock chaud, la correction automatique et les nouveaux essais de création de stack
Stock de stacks prêtes par lab dans Redis (prise atomique en Lua, ménage
par comparaison avec Heat, disjoncteur), files gérées par Horizon, séances
planifiables. Correcteur Go sur l'hyperviseur : socket Unix, commandes lues
dans le lab.yaml uniquement, exécution par l'agent invité QEMU. Une création
de stack ratée est supprimée et réessayée deux fois, puis la session passe
en échec, visible du prof.
Corrige aussi trois défauts des étapes précédentes : la console se coupait
à la première frappe d'un navigateur (trames texte), l'élève ne pouvait pas
rejoindre une séance, la déconnexion n'avait pas de jeton CSRF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:52:16 +02:00
@ php ( $planifiees = $classe -> seances () -> where ( 'statut' , \App\Models\Seance :: PLANIFIEE ) -> with ( 'lab' ) -> orderBy ( 'prevue_a' ) -> get ())
@ if ( $planifiees -> isNotEmpty ())
< h3 class = " mt-6 text-lg font-extrabold " > Séances planifiées </ h3 >
< ul class = " mt-2 grid gap-2 " >
@ foreach ( $planifiees as $planifiee )
< li class = " flex flex-wrap items-center justify-between gap-3 rounded-xl border border-[#e3e5e8] p-3 " >
< span >< span class = " font-mono font-bold " > {{ $planifiee -> prevue_a -> timezone ( config ( 'app.fuseau_utilisateurs' )) -> translatedFormat ( 'd/m/Y H:i' ) }} </ span > · < span class = " font-bold " > {{ $planifiee -> lab -> titre }} </ span ></ span >
< form action = " { { route('seances.ouvrir', $planifiee ) }} " method = " post " >
@ csrf
< button type = " submit " class = " font-bold underline underline-offset-4 " > Ouvrir la salle d ' attente </ button >
</ form >
</ li >
@ endforeach
</ ul >
@ endif
2026-10-08 02:01:58 +02:00
</ section >
2026-10-09 21:07:25 +02:00
2026-10-12 22:25:56 +02:00
< section class = " mt-6 rounded-lg border border-[#e3e5e8] bg-white p-6 " aria - labelledby = " t-archiver " >
2026-10-09 21:07:25 +02:00
< h2 id = " t-archiver " class = " text-2xl font-extrabold " > Fin d ' année </ h2 >
< p class = " mt-2 font-semibold " >
Archiver la classe ferme ses séances . {{ \App\Models\Classe :: DELAI_PURGE_JOURS }} jours plus tard , la classe
et toutes les données de ses élèves sont supprimées automatiquement ( RGPD ) . Tu peux la désarchiver d ' ici là .
</ p >
@ error ( 'archivage' )
< p class = " mt-2 font-semibold text-[#A63D2A] " > {{ $message }} </ p >
@ enderror
< form action = " { { route('mes-classes.archiver', $classe ) }} " method = " post " class = " mt-4 "
data - confirmer = " Archiver { { $classe->nom }} ? Ses données seront supprimées dans { { \ App \ Models \ Classe::DELAI_PURGE_JOURS }} jours. " >
@ csrf
< button type = " submit " class = " inline-flex h-11 items-center rounded-full border border-[#A63D2A] px-5 font-bold text-[#A63D2A] " > Archiver la classe </ button >
</ form >
</ section >
@ endif
2026-10-12 22:25:56 +02:00
</ div >
2026-10-08 02:01:58 +02:00
</ div >
2026-10-12 22:25:56 +02:00
</ div >
< script nonce = " { { Vite::cspNonce() }} " >
document . querySelectorAll ( '[data-tout-cocher]' ) . forEach ( function ( c ) {
c . addEventListener ( 'change' , function () { document . querySelectorAll ( 'input[name="eleves[]"]' ) . forEach ( function ( x ) { x . checked = c . checked ; }); });
});
@ unless ( $classe -> estArchivee ())
( function () {
var vues = {{ $demandes -> count () }};
setInterval ( function () {
fetch ( '{{ route(' mes - classes . demandes . nombre ', $classe) }}' , { headers : { 'Accept' : 'application/json' } })
. then ( function ( r ) { return r . json (); })
. then ( function ( d ) {
var occupe = document . activeElement && / INPUT | TEXTAREA | SELECT /. test ( document . activeElement . tagName );
if ( d . en_attente !== vues && ! occupe ) location . reload ();
}) . catch ( function () {});
}, 4000 );
})();
@ endunless
@ if ( $seance )
// La séance en cours bouge vite (admissions, corrections, mains levées) :
// un rafraîchissement discret suffit, sans rien casser si le prof tape.
setInterval ( function () {
var occupe = document . activeElement && / INPUT | TEXTAREA | SELECT /. test ( document . activeElement . tagName );
if ( ! occupe ) location . reload ();
}, 15000 );
@ endif
2026-10-14 00:35:52 +02:00
var caseModeExamen = document . getElementById ( 'case-mode-examen' );
if ( caseModeExamen ) caseModeExamen . addEventListener ( 'change' , function () {
document . getElementById ( 'champ-duree-examen' ) . hidden = ! caseModeExamen . checked ;
});
2026-10-12 22:25:56 +02:00
document . querySelectorAll ( '[data-imprimer]' ) . forEach ( function ( b ) { b . addEventListener ( 'click' , function () { window . print (); }); });
document . querySelectorAll ( 'form[data-confirmer]' ) . forEach ( function ( f ) {
f . addEventListener ( 'submit' , function ( e ) { if ( ! window . confirm ( f . dataset . confirmer )) e . preventDefault (); });
});
</ script >
2026-10-08 02:01:58 +02:00
@ endsection