reconstruit le tableau de bord du prof dans le style de /cockpit, avec les vraies données (chantier vrai cockpit, étape 1)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Adam Belkacemi 2026-10-12 22:25:56 +02:00
parent 78030de488
commit 0573894721
6 changed files with 302 additions and 41 deletions

View file

@ -5,6 +5,7 @@ namespace App\Http\Controllers;
use App\Models\Classe;
use App\Models\DemandeInscription;
use App\Models\Eleve;
use App\Models\LabSession;
use App\Models\Seance;
use App\Services\GenerateurCompteEleve;
use Illuminate\Http\JsonResponse;
@ -26,13 +27,38 @@ class ClasseController extends Controller
return view('mes-classes.index', ['classes' => $classes]);
}
/**
* Le tableau de bord (étape 1 du chantier « vrai cockpit », 2026-10-12)
* ne montre que des chiffres réels : rien n'est affiché qui ne vienne
* pas d'une vraie table ou d'un vrai cache (CLAUDE.md : pas de KPI
* inventé). Les fonctions sans backend (quiz, DM, observations, examen)
* n'ont pas de carte ici — elles arriveront une par une.
*/
public function show(Classe $classe): View
{
$this->verifierProprietaire($classe);
// La séance la plus pertinente à montrer : en cours avant salle
// d'attente, la plus récemment démarrée d'abord.
$seance = $classe->seances()
->whereIn('statut', [Seance::SALLE_ATTENTE, Seance::EN_COURS])
->with(['lab', 'labSessions' => fn ($q) => $q->with(['eleve', 'derniereCorrection', 'machines'])])
->latest('demarree_a')
->get()
->sortByDesc(fn ($s) => $s->statut === Seance::EN_COURS)
->first();
$corrections = $seance
? LabSession::query()->whereIn('id', $seance->labSessions->pluck('id'))->whereHas('derniereCorrection')->with('derniereCorrection')->get()->pluck('derniereCorrection')
: collect();
return view('mes-classes.show', [
'classes' => Auth::guard('web')->user()->classes()->withCount('eleves')->orderBy('nom')->get(),
'classe' => $classe->load('eleves'),
'demandes' => $classe->demandesInscription()->where('statut', DemandeInscription::EN_ATTENTE)->oldest()->get(),
'seance' => $seance,
'reussiteMoyenne' => $corrections->isNotEmpty() ? (int) round($corrections->avg(fn ($c) => $c->note_max > 0 ? $c->note / $c->note_max * 100 : 0)) : null,
'mainsLevees' => ($seance && $seance->visioOuverte()) ? VisioController::nombreMainsLevees($seance) : null,
]);
}

View file

@ -202,11 +202,26 @@ class VisioController extends Controller
return $eleve->prenom.($eleve->nom ? ' '.mb_strtoupper(mb_substr($eleve->nom, 0, 1)).'.' : '');
}
private function cle(Seance $seance): string
/**
* Lu aussi par le tableau de bord du prof (ClasseController), d'où la
* visibilité publique de la clé et de ce compteur — jamais du contenu
* complet de l'état (noms des élèves), seulement un chiffre.
*/
public static function cleEtat(Seance $seance): string
{
return "visio:{$seance->id}";
}
public static function nombreMainsLevees(Seance $seance): int
{
return count((Cache::get(self::cleEtat($seance)) ?? [])['mains'] ?? []);
}
private function cle(Seance $seance): string
{
return self::cleEtat($seance);
}
/**
* @return array{mains: array<int, array{nom: string, depuis: int}>, participants: array<int, string>, presences: array<int, array{nom: string, vu: int}>}
*/