ajoute le portail Laravel : connexion prof/élève, session unique, gestion de classe, salle d'attente

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Adam Belkacemi 2026-10-08 02:01:58 +02:00
parent aa31b6112f
commit 30e14b1c54
191 changed files with 25324 additions and 0 deletions

View file

@ -0,0 +1,35 @@
@extends('layouts.vitrine')
@section('titre', 'Mes classes')
@section('contenu')
<div class="mx-auto max-w-5xl px-5 py-14 lg:px-10">
<h1 class="text-4xl font-extrabold tracking-tight">Mes classes</h1>
@if (session('session_precedente_fermee'))
<p class="mt-4 border border-[#e3e5e8] rounded-2xl bg-[#fef7e0] p-4 font-semibold">
Une session précédente sur ce compte a été fermée : une seule session active à la fois.
</p>
@endif
@if ($classes->isEmpty())
<p class="mt-6 font-semibold">Aucune classe pour l'instant.</p>
@else
<ul class="mt-8 grid gap-4">
@foreach ($classes as $classe)
<li>
<a href="{{ route('mes-classes.show', $classe) }}" class="flex flex-wrap items-center justify-between gap-3 border border-[#e3e5e8] rounded-2xl bg-white p-5 hover:bg-[#f8f9fa]">
<span>
<span class="block text-xl font-extrabold">{{ $classe->nom }}</span>
@if ($classe->formation)
<span class="block font-semibold">{{ $classe->formation }}</span>
@endif
</span>
<span class="font-mono text-sm font-semibold">{{ $classe->eleves_count }} élève(s)</span>
</a>
</li>
@endforeach
</ul>
@endif
</div>
@endsection

View file

@ -0,0 +1,113 @@
@extends('layouts.vitrine')
@section('titre', $classe->nom)
@section('contenu')
<div class="mx-auto max-w-5xl px-5 py-14 lg:px-10">
<div class="flex flex-wrap items-end justify-between gap-4">
<div>
<h1 class="text-4xl font-extrabold tracking-tight">{{ $classe->nom }}</h1>
@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'))
<p class="mt-4 border border-[#e3e5e8] rounded-2xl bg-[#e6f4ea] p-4 font-semibold">Classe créée. Ajoute maintenant tes élèves ci-dessous.</p>
@endif
@if (session('fiches'))
<section class="mt-10 border border-[#e3e5e8] rounded-2xl bg-[#fef7e0] p-6 print:border-0 print:bg-white" aria-labelledby="t-fiches">
<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>
<button type="button" onclick="window.print()" class="border border-[#dadce0] rounded-lg bg-black px-4 py-2 font-extrabold text-white">Imprimer</button>
</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)
<div class="flex items-center gap-4 border border-[#dadce0] rounded-2xl bg-white p-5 print:break-inside-avoid">
<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
<div class="mt-10 grid gap-6 sm:grid-cols-2">
<section class="border border-[#e3e5e8] rounded-2xl bg-white p-6" aria-labelledby="t-ajout">
<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>
<section class="border border-[#e3e5e8] rounded-2xl bg-white p-6" aria-labelledby="t-import">
<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>
<section class="mt-10 border border-[#e3e5e8] rounded-2xl bg-white" aria-labelledby="t-eleves">
<h2 id="t-eleves" class="p-6 pb-0 text-2xl font-extrabold">Élèves ({{ $classe->eleves->count() }})</h2>
@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">
<thead class="bg-[#0b57d0] text-white"><tr><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>
<tbody class="divide-y divide-[#e3e5e8]">
@foreach ($classe->eleves as $eleve)
<tr>
<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">
<form action="{{ route('mes-classes.eleves.regenererCode', [$classe, $eleve]) }}" method="post" onsubmit="return confirm('Régénérer le code de {{ $eleve->prenom }} ? L’ancien code ne fonctionnera plus.');">
@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>
<section class="mt-10 border border-[#e3e5e8] rounded-2xl bg-white p-6" aria-labelledby="t-seance">
<h2 id="t-seance" class="text-2xl font-extrabold">Lancer 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.</p>
<form action="{{ route('mes-classes.seances.store', $classe) }}" method="post" class="mt-4 flex flex-wrap items-end gap-3">
@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">
@foreach (\App\Models\Lab::orderBy('titre')->get() as $lab)
<option value="{{ $lab->id }}">{{ $lab->titre }}</option>
@endforeach
</select></label>
<button type="submit" class="inline-flex h-11 items-center justify-center rounded-full bg-black px-5 font-bold text-white">Ouvrir la salle d'attente</button>
</form>
</section>
</div>
@endsection