Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
ec6e9f9
feat(i18n): support multilingue FR/EN (infra + coquille)
horacioskrp Oct 21, 2026
b72dd17
Merge pull request #326 from wearedalibi/feat/i18n-base
horacioskrp Oct 21, 2026
726e737
feat(i18n): traduit le reste de la coquille (dropdown, paramètres, lo…
horacioskrp Oct 21, 2026
60f73e0
feat(i18n): traduit le module tableau de bord (FR/EN)
horacioskrp Oct 21, 2026
e86b970
feat(i18n): traduit la liste et la fiche élève (FR/EN)
horacioskrp Oct 21, 2026
8edc587
feat(i18n): traduit le formulaire élève par étapes (FR/EN)
horacioskrp Oct 21, 2026
ccee790
feat(i18n): traduit historique, import et statistiques élèves (FR/EN)
horacioskrp Oct 30, 2026
35d7e2a
feat(i18n): traduit effectifs/listes de classe et passage de classe (…
horacioskrp Oct 30, 2026
d02ffae
feat(i18n): traduit le module bourses d'élèves (FR/EN)
horacioskrp Oct 30, 2026
1890520
Merge pull request #327 from wearedalibi/feat/i18n-shell
horacioskrp Oct 30, 2026
890493a
feat(i18n): traduit le module calendrier (FR/EN)
horacioskrp Oct 30, 2026
24991f5
Merge pull request #328 from wearedalibi/feat/i18n-calendar
horacioskrp Oct 30, 2026
bfb6084
feat(i18n): traduit inscriptions & paiements — liste, fiche, édition,…
horacioskrp Oct 31, 2026
d947069
feat(i18n): traduit facture, impression facture et confirmation (FR/EN)
horacioskrp Oct 31, 2026
41f840f
Merge pull request #329 from wearedalibi/feat/i18n-enrollments
horacioskrp Oct 31, 2026
c8503c7
feat(i18n): comptabilité — vérif reçu, dépense, reçu de paiement, cai…
horacioskrp Nov 1, 2026
d0a4537
feat(i18n): comptabilité — tableau de bord, situation, transactions (…
horacioskrp Nov 1, 2026
033e1cf
feat(i18n): examens — modèles d'évaluation (liste, création, édition,…
horacioskrp Nov 1, 2026
2dfd60e
Merge pull request #330 from wearedalibi/feat/i18n-rest
horacioskrp Nov 1, 2026
8df6bb0
feat(i18n): examens — évaluations par classe (liste + détail) (FR/EN)
horacioskrp Nov 1, 2026
928090e
feat(i18n): examens — saisie des notes (FR/EN)
horacioskrp Nov 1, 2026
8c3d986
feat(i18n): examens — examens officiels + planning (FR/EN)
horacioskrp Nov 2, 2026
2f2fc5f
feat(i18n): notes — bulletins (liste + édition) (FR/EN)
horacioskrp Nov 2, 2026
4d52222
feat(i18n): notes — saisie des notes + bulletin élève (FR/EN)
horacioskrp Nov 2, 2026
27059e9
feat(i18n): notes — réclamations de notes (liste, dépôt, traitement) …
horacioskrp Nov 2, 2026
6fe60e2
feat(i18n): présences — appel, statistiques, permissions d'absence (F…
horacioskrp Nov 2, 2026
d9eb0fe
feat(i18n): RH — personnel, grilles salariales, rubriques & réglages …
horacioskrp Nov 2, 2026
d591c82
feat(i18n): paie — cycles de paie (liste, création, détail) (FR/EN)
horacioskrp Nov 2, 2026
c397c3a
feat(i18n): administration — journal d'audit & accès portail (tuteurs…
horacioskrp Nov 2, 2026
5b4bf5e
feat(i18n): administration — permissions (liste, création, édition, d…
horacioskrp Nov 2, 2026
8b4b4ce
feat(i18n): administration — rôles (liste, création, édition, détail)…
horacioskrp Nov 2, 2026
47f2f80
feat(i18n): administration — affectations de matières (liste, formula…
horacioskrp Nov 2, 2026
cbacef5
feat(i18n): administration — utilisateurs (liste, formulaire, détail,…
horacioskrp Nov 2, 2026
93a444f
Merge pull request #331 from wearedalibi/feat/i18n-rest
horacioskrp Nov 2, 2026
4294cd5
feat(i18n): authentification — mot de passe oublié, 2FA, vérification…
horacioskrp Nov 11, 2026
5874c2c
feat(i18n): élèves — emploi du temps (classe & enseignant) (FR/EN)
horacioskrp Nov 11, 2026
796d895
Merge pull request #332 from wearedalibi/feat/i18n-rest
horacioskrp Nov 11, 2026
a1d282d
feat(i18n): paramètres du compte — profil, mot de passe & double auth…
horacioskrp Nov 11, 2026
5654e6b
Merge pull request #333 from wearedalibi/feat/i18n-rest
horacioskrp Nov 11, 2026
c027070
feat(i18n): divers — page d'accueil & pages d'erreur (FR/EN)
horacioskrp Nov 11, 2026
8df12f9
feat(i18n): archives documentaires — liste & tags (FR/EN)
horacioskrp Nov 11, 2026
0051422
feat(i18n): statistiques — effectifs, finances, réussite, encadrement…
horacioskrp Nov 11, 2026
7b46e76
Merge pull request #334 from wearedalibi/feat/i18n-rest
horacioskrp Nov 11, 2026
3917ff4
feat(i18n): paramètres — pays (liste, création, édition, détail) (FR/EN)
horacioskrp Nov 11, 2026
a47de72
feat(i18n): paramètres — écoles (création, édition, détail) (FR/EN)
horacioskrp Nov 11, 2026
53ae150
feat(i18n): paramètres — à propos (FR/EN)
horacioskrp Nov 11, 2026
9bb3258
feat(i18n): paramètres — types d'évaluation (liste, création, édition…
horacioskrp Nov 11, 2026
c83cf71
feat(i18n): paramètres — calcul des moyennes (liste, création, éditio…
horacioskrp Nov 11, 2026
88c0637
Merge pull request #335 from wearedalibi/feat/i18n-rest
horacioskrp Nov 11, 2026
2d4ec22
feat(i18n): paramètres — matières (liste, création, édition, détail) …
horacioskrp Nov 12, 2026
b2dcdf5
feat(i18n): paramètres — catégories de frais (liste, création, éditio…
horacioskrp Nov 12, 2026
6767cab
feat(i18n): paramètres — types de classes (liste, création, édition, …
horacioskrp Nov 12, 2026
d9ff2a7
feat(i18n): paramètres — bourses (liste, création, édition, détail) (…
horacioskrp Nov 12, 2026
8f8df9d
feat(i18n): paramètres — modèle de bulletin (FR/EN)
horacioskrp Nov 12, 2026
3757162
feat(i18n): paramètres — fichiers & stockage, sauvegardes (FR/EN)
horacioskrp Nov 12, 2026
2362070
feat(i18n): paramètres — années scolaires (liste, création, édition, …
horacioskrp Nov 13, 2026
3fd58d4
feat(i18n): paramètres — périodes académiques (liste, création, éditi…
horacioskrp Nov 13, 2026
3aacdf5
feat(i18n): paramètres — classes & attributions de matières (FR/EN)
horacioskrp Nov 13, 2026
ff47520
feat(i18n): paramètres — documents (modèles, registre, détail, éditio…
horacioskrp Nov 13, 2026
b0991cf
feat(i18n): paramètres — attribution matières/classes (liste, créatio…
horacioskrp Nov 13, 2026
3827ec0
feat(i18n): paramètres — structures de frais (liste, création, éditio…
horacioskrp Nov 13, 2026
a12bb55
Merge pull request #336 from wearedalibi/feat/i18n-rest
horacioskrp Nov 13, 2026
d24d6de
feat(i18n): composants partagés — éditeur de texte riche & illustrati…
horacioskrp Nov 13, 2026
83f7bb4
chore(phpstan): ajoute Larastan niveau 5 avec baseline + étape CI
horacioskrp Nov 13, 2026
f7bbe4f
Merge pull request #338 from wearedalibi/feat/i18n-rest
horacioskrp Nov 13, 2026
7f74d5e
Merge pull request #337 from wearedalibi/chore/phpstan
horacioskrp Nov 13, 2026
3e24a3c
refactor(models): typage générique des relations Eloquent + réduction…
horacioskrp Nov 13, 2026
5d877d0
fix(command): GenerateStudentMatricule — re-pointe le code mort regis…
horacioskrp Nov 13, 2026
ca52f83
refactor(phpstan): supprime les accès nullsafe superflus (?-> -> ->) …
horacioskrp Nov 13, 2026
5771ab8
fix(phpstan): typage self dans le trait Auditable + type UUID dans Un…
horacioskrp Nov 13, 2026
8d52d6f
fix(phpstan): @mixin sur les resources API + type accessor name (124 …
horacioskrp Nov 13, 2026
87f21e1
fix(phpstan): relation installments typée, types de retour forClassTy…
horacioskrp Nov 13, 2026
ec86535
fix(phpstan): shape student_id string + abort_unless explicite (81 → 70)
horacioskrp Nov 13, 2026
f7fe1c2
fix(phpstan): annote relations manquées employeeProfile & classroomTy…
horacioskrp Nov 13, 2026
b3b1cc6
refactor(phpstan): retire les ?? défaut morts sur offsets toujours pr…
horacioskrp Nov 13, 2026
7933396
fix(phpstan): corrige withSum, tempnam & gardes mortes (56 → 47)
horacioskrp Nov 13, 2026
898be3e
fix(phpstan): résorbe les 47 derniers faux positifs, supprime la base…
horacioskrp Nov 13, 2026
bb6111c
fix(seeders): restaure l'accès nullsafe $this->command?-> (corrige Se…
horacioskrp Nov 13, 2026
2600018
Merge pull request #339 from wearedalibi/chore/phpstan-debt-1
horacioskrp Nov 13, 2026
d2135fd
chore(deps): met à jour les dépendances pour corriger 67 avis de sécu…
horacioskrp Nov 13, 2026
e0bc169
chore(deps): pinne maatwebsite/excel à ^4.0
horacioskrp Nov 13, 2026
8656adc
Merge pull request #340 from wearedalibi/chore/deps-security-update
horacioskrp Nov 13, 2026
1ac5f5b
docs(changelog): entrée v1.0.2 (i18n FR/EN, sécurité deps, PHPStan ni…
horacioskrp Nov 13, 2026
a6ac730
Merge pull request #341 from wearedalibi/chore/changelog-v1.0.2
horacioskrp Nov 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,43 @@ jobs:
- name: Lancer la suite de tests
run: php artisan test

static-analysis:
name: Analyse statique (PHPStan)
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Configurer PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: pdo_sqlite, sqlite3, mbstring, zip, gd, intl, bcmath
coverage: none

# `.env` doit exister AVANT composer install : le script post-autoload-dump
# (`package:discover`) boote l'application.
- name: Préparer l'environnement
run: |
cp .env.example .env
touch database/database.sqlite

- name: Cache des dépendances Composer
uses: actions/cache@v4
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
restore-keys: composer-

- name: Installer les dépendances
run: composer install --no-interaction --prefer-dist --no-progress

- name: Générer la clé applicative
run: php artisan key:generate

- name: Lancer PHPStan
run: composer analyse

frontend:
name: Front (types & build)
runs-on: ubuntu-latest
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,30 @@ Toutes les évolutions notables de **Dalibi** sont consignées ici.
Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le versionnage
respecte [SemVer](https://semver.org/lang/fr/).

## [1.0.2] — 2026-11-13

### Ajouté
- **Internationalisation (i18n)** : bascule complète **français / anglais** de l'ensemble
des modules (interface, écrans et composants partagés), avec fichiers de langue dédiés
et partage des traductions côté client via Inertia.

### Sécurité
- Montée des dépendances résorbant **67 avis de sécurité** (`composer audit` désormais
vierge), dont le **bypass d'authentification TokenGuard** de Laravel
([CVE-2026-114509](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-114509),
critique — pertinent pour l'auth par jeton du portail) : Laravel 12.72, Symfony 7.4.23,
Guzzle 7.16, CommonMark 2.10, dompdf 3.1.6, ainsi que les montées majeures
phpoffice/phpspreadsheet 5 et maatwebsite/excel 4.

### Qualité & documentation
- **Analyse statique** : intégration de **PHPStan / Larastan niveau 5** avec étape CI
dédiée, puis résorption **intégrale de la dette** (463 → 0 erreur, sans fichier
*baseline*) — typage générique des relations Eloquent, formes des résultats d'agrégats,
authentification multi-modèle du portail (élève / tuteur).
- Corrections de **bugs réels** mis au jour par l'analyse : contrainte `withSum` ignorée
sur les totaux de caisse, libellé d'archive d'élève vide, positionnement de colonne en
migration, comparaison d'identifiant UUID, accès nullsafe rétablis sur les seeders.

## [1.0.1] — 2026-10-11

### Ajouté
Expand Down
7 changes: 3 additions & 4 deletions app/Concerns/Auditable.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace App\Concerns;

use App\Models\AuditLog;
use Illuminate\Database\Eloquent\Model;

/**
* Enregistre automatiquement les créations / modifications / suppressions
Expand All @@ -16,9 +15,9 @@ trait Auditable
{
public static function bootAuditable(): void
{
static::created(fn (Model $model) => $model->recordAudit('created', null, $model->auditableAttributes()));
static::created(fn (self $model) => $model->recordAudit('created', null, $model->auditableAttributes()));

static::updated(function (Model $model): void {
static::updated(function (self $model): void {
$changed = array_keys($model->getChanges());
$keys = array_diff($changed, $model->auditExcluded());
if ($keys === []) {
Expand All @@ -31,7 +30,7 @@ public static function bootAuditable(): void
$model->recordAudit('updated', $old, $new);
});

static::deleted(fn (Model $model) => $model->recordAudit('deleted', $model->auditableAttributes(), null));
static::deleted(fn (self $model) => $model->recordAudit('deleted', $model->auditableAttributes(), null));
}

/** Champs exclus du journal (secrets et bruit). */
Expand Down
53 changes: 21 additions & 32 deletions app/Console/Commands/GenerateStudentMatricule.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
namespace App\Console\Commands;

use App\Models\Student;
use App\Services\MatriculeService;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Collection;

class GenerateStudentMatricule extends Command
{
Expand All @@ -13,10 +13,10 @@ class GenerateStudentMatricule extends Command
*
* @var string
*/
protected $signature = 'matricule:generate-student
protected $signature = 'matricule:generate-student
{--class-id= : ID de la classe}
{--all : Générer pour tous les élèves}
{--registration : Générer aussi les numéros d\'enregistrement}
{--matricule : Générer aussi les matricules des élèves}
{--force : Forcer la régénération}';

/**
Expand All @@ -31,10 +31,9 @@ class GenerateStudentMatricule extends Command
*/
public function handle(): int
{
$matriculeService = app(MatriculeService::class);
$withStudentMatricule = (bool) $this->option('matricule');

if ($this->option('all')) {
// Générer pour tous les élèves
$students = Student::all();

if ($students->isEmpty()) {
Expand All @@ -44,28 +43,7 @@ public function handle(): int
}

$this->info("Génération des matricules pour {$students->count()} élèves...");

$progressBar = $this->output->createProgressBar($students->count());
$progressBar->start();

foreach ($students as $student) {
// Générer matricule de l'utilisateur
if (! $student->user->natricule) {
$student->user->natricule = $student->user->generateMatricule();
$student->user->save();
}

// Générer numéro d'enregistrement
if (! $student->registration_number) {
$student->registration_number = $student->generateRegistrationNumber();
$student->save();
}

$progressBar->advance();
}

$progressBar->finish();
$this->newLine();
$this->backfillMatricules($students, $withStudentMatricule);
$this->info('✅ Matricules générés avec succès!');

return 0;
Expand All @@ -88,18 +66,32 @@ public function handle(): int
}

$this->info("Génération pour {$students->count()} élèves...");
$this->backfillMatricules($students, $withStudentMatricule);
$this->info('✅ Matricules générés avec succès!');

return 0;
}

/**
* Génère les matricules manquants pour un lot d'élèves.
*
* @param Collection<int, Student> $students
*/
private function backfillMatricules(Collection $students, bool $withStudentMatricule): void
{
$progressBar = $this->output->createProgressBar($students->count());
$progressBar->start();

foreach ($students as $student) {
// Matricule du compte utilisateur associé.
if (! $student->user->natricule) {
$student->user->natricule = $student->user->generateMatricule();
$student->user->save();
}

if (! $student->registration_number) {
$student->registration_number = $student->generateRegistrationNumber();
// Matricule de l'élève (optionnel, via --matricule).
if ($withStudentMatricule && empty($student->matricule)) {
$student->matricule = $student->generateMatricule();
$student->save();
}

Expand All @@ -108,8 +100,5 @@ public function handle(): int

$progressBar->finish();
$this->newLine();
$this->info('✅ Matricules générés avec succès!');

return 0;
}
}
60 changes: 60 additions & 0 deletions app/Constants/Locales.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace App\Constants;

/**
* Langues prises en charge par l'application (interface).
*
* Ajouter une langue = ajouter une entrée ici + les fichiers `lang/{code}/*.php`.
*/
class Locales
{
/** Code de la langue par défaut. */
public const DEFAULT = 'fr';

/**
* @return array<string, array{label: string, flag: string}>
*/
public static function all(): array
{
return [
'fr' => ['label' => 'Français', 'flag' => '🇫🇷'],
'en' => ['label' => 'English', 'flag' => '🇬🇧'],
];
}

/** Codes valides (pour la validation). */
public static function codes(): array
{
return array_keys(self::all());
}

/** Le code est-il une langue prise en charge ? */
public static function isValid(?string $code): bool
{
return $code !== null && array_key_exists($code, self::all());
}

/** Libellé lisible d'un code (repli sur le code). */
public static function label(?string $code): string
{
$code = $code ?: self::DEFAULT;

return self::all()[$code]['label'] ?? $code;
}

/**
* Options prêtes pour un sélecteur côté frontend.
*
* @return array<int, array{code: string, label: string, flag: string}>
*/
public static function options(): array
{
$out = [];
foreach (self::all() as $code => $def) {
$out[] = ['code' => $code, 'label' => $def['label'], 'flag' => $def['flag']];
}

return $out;
}
}
3 changes: 2 additions & 1 deletion app/Exports/StatisticsExport.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
namespace App\Exports;

use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\Export;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;

/** Construit un classeur Excel multi-feuilles à partir des données d'une section. */
class StatisticsExport implements WithMultipleSheets
class StatisticsExport implements Export, WithMultipleSheets
{
private const METHOD_LABELS = [
'CASH' => 'Espèces',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ public function statistics(Request $request): Response
$assignments = $selected
? $this->teacherAssignments($teacherId, $yearFilter)->map(fn ($a) => [
'id' => $a->id,
'subject' => $a->subject?->name ?? '—',
'classroom' => $a->classroom?->name ?? '—',
'subject' => $a->subject->name ?? '—',
'classroom' => $a->classroom->name ?? '—',
'active' => (bool) $a->active,
])
: collect();
Expand Down Expand Up @@ -143,7 +143,7 @@ public function exportStatistics(Request $request)
'assignments' => $this->teacherAssignments($teacher->id, $yearId),
])->setPaper('a4', 'portrait');

return $pdf->stream('affectations-'.Str::slug(trim($teacher->firstname.' '.$teacher->lastname).'-'.($year?->year ?? '')).'.pdf');
return $pdf->stream('affectations-'.Str::slug(trim($teacher->firstname.' '.$teacher->lastname).'-'.($year->year ?? '')).'.pdf');
}

/** Identifiant de l'année académique active (la plus récente marquée active). */
Expand All @@ -170,7 +170,7 @@ private function teacherAssignments(string $teacherId, string $yearId): Collecti
->where('teacher_id', $teacherId)
->where('academic_year_id', $yearId)
->get()
->sortBy(fn ($a) => ($a->subject?->name ?? '').' '.($a->classroom?->name ?? ''))
->sortBy(fn ($a) => ($a->subject->name ?? '').' '.($a->classroom->name ?? ''))
->values();
}

Expand Down
4 changes: 4 additions & 0 deletions app/Http/Controllers/Api/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ abstract class ApiController extends Controller
*/
protected function accessibleStudents(Request $request): Collection
{
// Ces routes API authentifient un élève ou un tuteur (gardes Sanctum
// dédiées), jamais le User par défaut supposé par l'analyse statique.
/** @var Student|Guardian|null $user */
$user = $request->user();

if ($user instanceof Student) {
Expand All @@ -38,6 +41,7 @@ protected function accessibleStudents(Request $request): Collection
*/
protected function resolveStudent(Request $request, string $studentId): Student
{
/** @var Student|Guardian|null $user */
$user = $request->user();

if ($user instanceof Student) {
Expand Down
6 changes: 5 additions & 1 deletion app/Http/Controllers/Api/V1/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public function login(LoginRequest $request): JsonResponse

public function me(Request $request): JsonResponse
{
// Portail parent/élève : le principal authentifié est un tuteur ou un élève.
/** @var Guardian|Student $user */
$user = $request->user();
$type = $user instanceof Guardian ? 'guardian' : 'student';

Expand All @@ -47,7 +49,9 @@ public function me(Request $request): JsonResponse

public function logout(Request $request): JsonResponse
{
$request->user()->currentAccessToken()->delete();
/** @var Guardian|Student $user */
$user = $request->user();
$user->currentAccessToken()->delete();

return response()->json(['message' => 'Déconnecté.']);
}
Expand Down
1 change: 1 addition & 0 deletions app/Http/Controllers/Api/V1/ChildrenController.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public function index(Request $request): AnonymousResourceCollection
{
$withActiveEnrollment = ['enrollments' => fn ($q) => $q->where('status', Enrollment::STATUS_ACTIVE)->with('classroom:id,name')];

/** @var Student|Guardian $user */
$user = $request->user();

if ($user instanceof Student) {
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/Api/V1/DashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function index(Request $request): JsonResponse
'id' => $e->id,
'title' => $e->title,
'type' => $e->type,
'start_date' => $e->start_date?->format('Y-m-d'),
'start_date' => $e->start_date->format('Y-m-d'),
]);

return response()->json([
Expand Down
11 changes: 6 additions & 5 deletions app/Http/Controllers/Archives/ArchiveController.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

class ArchiveController extends Controller
{
private const TYPES = ['student' => Student::class, 'classroom' => Classroom::class];

private function authorizeManage(Request $request): void
{
// Socle de sécurité : les routes portent déjà la permission fine (create/edit/
Expand Down Expand Up @@ -234,9 +232,12 @@ private function linkLabel(ArchivedDocument $d): ?string
return null;
}

return match ($d->documentable_type) {
Student::class => 'Élève : '.$d->documentable->name,
Classroom::class => 'Classe : '.$d->documentable->name,
$linked = $d->documentable;

return match (true) {
// Un élève n'a pas de colonne « name » : on compose nom + prénom.
$linked instanceof Student => 'Élève : '.trim($linked->firstname.' '.$linked->lastname),
$linked instanceof Classroom => 'Classe : '.$linked->name,
default => null,
};
}
Expand Down
Loading
Loading