feat: Permettre à l'enseignant de saisir les notes dans une grille inline
Some checks failed
CI / Backend Tests (push) Has been cancelled
CI / Frontend Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Naming Conventions (push) Has been cancelled
CI / Build Check (push) Has been cancelled

L'enseignant avait besoin d'un moyen rapide de saisir les notes après
une évaluation. La grille inline permet de compléter 30 élèves en moins
de 3 minutes grâce à la navigation clavier (Tab/Enter/Shift+Tab),
la validation temps réel, l'auto-save debounced (500ms) et les
raccourcis /abs et /disp pour marquer absents/dispensés.

Les notes restent en brouillon jusqu'à publication explicite (avec
confirmation modale). Une fois publiées, les élèves les voient
immédiatement ; les parents après un délai de 24h (VisibiliteNotesPolicy).
Le mode offline stocke les notes en IndexedDB et synchronise
automatiquement au retour de la connexion.

Chaque modification est auditée dans grade_events via un event
subscriber qui écoute NoteSaisie/NoteModifiee sur le bus d'événements.
This commit is contained in:
2026-03-29 09:55:45 +02:00
parent 98be1951bf
commit b70d5ec2ad
45 changed files with 3902 additions and 11 deletions

View File

@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20260327211549 extends AbstractMigration
{
public function getDescription(): string
{
return 'Création des tables grades et grade_events pour la saisie de notes et l\'audit';
}
public function up(Schema $schema): void
{
$this->addSql(<<<'SQL'
CREATE TABLE grades (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
evaluation_id UUID NOT NULL REFERENCES evaluations(id),
student_id UUID NOT NULL REFERENCES users(id),
value DECIMAL(5,2),
status VARCHAR(20) NOT NULL DEFAULT 'graded',
created_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (evaluation_id, student_id)
)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX idx_grades_tenant ON grades(tenant_id)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX idx_grades_evaluation ON grades(evaluation_id)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX idx_grades_student ON grades(student_id)
SQL);
$this->addSql(<<<'SQL'
CREATE TABLE grade_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
grade_id UUID NOT NULL REFERENCES grades(id),
event_type VARCHAR(50) NOT NULL,
old_value DECIMAL(5,2),
new_value DECIMAL(5,2),
old_status VARCHAR(20),
new_status VARCHAR(20),
created_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX idx_grade_events_grade ON grade_events(grade_id)
SQL);
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE IF EXISTS grade_events');
$this->addSql('DROP TABLE IF EXISTS grades');
}
}