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>
This commit is contained in:
Adam Belkacemi 2026-10-13 18:21:28 +02:00
parent a41951d4e6
commit 592cba9c4c
89 changed files with 2890 additions and 138 deletions

View file

@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Séparation vitrine/application (demande d'Adam, 2026-10-13) : thème
* clair/sombre et taille du texte, choisis dans le menu Paramètres.
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->json('preferences')->nullable()->after('password');
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('preferences');
});
}
};

View file

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Séparation vitrine/application (demande d'Adam, 2026-10-13). preferences :
* thème et taille du texte. derniere_activite_a : horodatage mis à jour à
* chaque requête authentifiée (middleware MetAJourActivite), utilisé pour
* savoir qui est connecté dans « Ma classe » — pas de présence en temps
* réel, une simple fenêtre de quelques minutes.
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('eleves', function (Blueprint $table) {
$table->json('preferences')->nullable()->after('code_secret');
$table->timestamp('derniere_activite_a')->nullable()->after('derniere_connexion_a');
});
}
public function down(): void
{
Schema::table('eleves', function (Blueprint $table) {
$table->dropColumn(['preferences', 'derniere_activite_a']);
});
}
};

View file

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Compétences du lab.yaml (déjà écrites par Adam, jamais lues jusqu'ici) :
* utilisées pour grouper « Mes notes » par compétence plutôt que d'inventer
* un référentiel séparé.
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('labs', function (Blueprint $table) {
$table->json('competences')->nullable()->after('niveau');
});
}
public function down(): void
{
Schema::table('labs', function (Blueprint $table) {
$table->dropColumn('competences');
});
}
};

View file

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Annonces du prof à sa classe (onglet « Ma classe » de l'élève, séparation
* vitrine/application, 2026-10-13).
*/
return new class extends Migration
{
public function up(): void
{
Schema::create('annonces', function (Blueprint $table) {
$table->id();
$table->foreignId('classe_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('titre');
$table->text('contenu');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('annonces');
}
};

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* « Mes raccourcis » dans la barre latérale (séparation vitrine/application,
* 2026-10-13) : pages épinglées par le prof ou l'élève. Exactement un des
* deux (user_id, eleve_id) est rempli, vérifié en application plutôt qu'en
* contrainte base, comme ailleurs dans ce projet.
*/
return new class extends Migration
{
public function up(): void
{
Schema::create('raccourcis', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->nullable()->constrained()->cascadeOnDelete();
$table->foreignId('eleve_id')->nullable()->constrained()->cascadeOnDelete();
$table->string('label');
$table->string('url');
$table->unsignedSmallInteger('ordre')->default(0);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('raccourcis');
}
};

View file

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* « Signaler un problème » du menu avatar (séparation vitrine/application,
* 2026-10-13) : relu par Adam, pas de traitement automatique.
*/
return new class extends Migration
{
public function up(): void
{
Schema::create('signalements', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->nullable()->constrained()->cascadeOnDelete();
$table->foreignId('eleve_id')->nullable()->constrained()->cascadeOnDelete();
$table->string('page_url')->nullable();
$table->text('message');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('signalements');
}
};

View file

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Observations (notes rapides sur un élève pour le conseil de classe) :
* dernière fonction fictive du tableau de bord démo, chantier « vrai
* cockpit » — construite ici en même temps que « Résultats » dans la
* nouvelle barre latérale prof (2026-10-13).
*/
return new class extends Migration
{
public function up(): void
{
Schema::create('observations', function (Blueprint $table) {
$table->id();
$table->foreignId('classe_id')->constrained()->cascadeOnDelete();
$table->foreignId('eleve_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->text('contenu');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('observations');
}
};

View file

@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Le prof peut couper le chat élève ↔ prof de sa classe (règle d'Adam,
* 2026-10-13, minimisation pour mineurs).
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('classes', function (Blueprint $table) {
$table->boolean('chat_actif')->default(true)->after('code_classe');
});
}
public function down(): void
{
Schema::table('classes', function (Blueprint $table) {
$table->dropColumn('chat_actif');
});
}
};

View file

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Chat Messenger élève ↔ prof de sa classe (règles d'Adam, 2026-10-13) :
* un seul fil par élève, jamais entre élèves. eleve_id unique : un élève
* n'a jamais qu'un seul prof (le sien, via classe_id).
*/
return new class extends Migration
{
public function up(): void
{
Schema::create('fils_conversation', function (Blueprint $table) {
$table->id();
$table->foreignId('classe_id')->constrained()->cascadeOnDelete();
$table->foreignId('eleve_id')->unique()->constrained()->cascadeOnDelete();
$table->timestamp('lu_par_prof_a')->nullable();
$table->timestamp('lu_par_eleve_a')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('fils_conversation');
}
};

View file

@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('messages_chat', function (Blueprint $table) {
$table->id();
// constrained() devinerait "fil_conversations" (pluriel anglais) :
// notre table s'appelle "fils_conversation".
$table->foreignId('fil_conversation_id')->constrained('fils_conversation')->cascadeOnDelete();
$table->string('expediteur')->comment('prof, eleve');
$table->text('contenu');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('messages_chat');
}
};

View file

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Catalogue des cours.md de paillasse-contenus/ (cours:synchroniser, même
* esprit que labs:synchroniser). Un seul lab (08-deux-lan-routeur) a un
* cours.md complet aujourd'hui : « Mes cours » n'affiche que du vrai
* contenu, pas de ligne vide pour les séquences qui n'en ont pas encore.
*/
return new class extends Migration
{
public function up(): void
{
Schema::create('cours', function (Blueprint $table) {
$table->id();
$table->string('identifiant')->unique()->comment('= identifiant du lab, ex: 08-deux-lan-routeur');
$table->string('titre');
$table->text('contenu_markdown');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('cours');
}
};

View file

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Lien vers l'ENT de l'établissement (CLAUDE.md : « pas de CPE ni de
* secrétariat pour l'instant, un lien vers l'ENT suffit »), affiché dans
* « Ma classe » quand il est connu. Nul par défaut : jamais inventé.
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('etablissements', function (Blueprint $table) {
$table->string('url_ent')->nullable()->after('uai');
});
}
public function down(): void
{
Schema::table('etablissements', function (Blueprint $table) {
$table->dropColumn('url_ent');
});
}
};