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

@@ -94,6 +94,8 @@ final class EvaluationResource
public ?DateTimeImmutable $updatedAt = null;
public ?DateTimeImmutable $gradesPublishedAt = null;
public static function fromDomain(
Evaluation $evaluation,
?string $className = null,
@@ -114,6 +116,7 @@ final class EvaluationResource
$resource->subjectName = $subjectName;
$resource->createdAt = $evaluation->createdAt;
$resource->updatedAt = $evaluation->updatedAt;
$resource->gradesPublishedAt = $evaluation->gradesPublishedAt;
return $resource;
}

View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace App\Scolarite\Infrastructure\Api\Resource;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use App\Scolarite\Domain\Model\Grade\Grade;
use App\Scolarite\Infrastructure\Api\Processor\PublishGradesProcessor;
use App\Scolarite\Infrastructure\Api\Processor\SaveGradesProcessor;
use App\Scolarite\Infrastructure\Api\Provider\GradeCollectionProvider;
use DateTimeImmutable;
#[ApiResource(
shortName: 'Grade',
operations: [
new GetCollection(
uriTemplate: '/evaluations/{evaluationId}/grades',
uriVariables: ['evaluationId'],
provider: GradeCollectionProvider::class,
itemUriTemplate: '/grades/{id}',
name: 'get_evaluation_grades',
),
new Put(
uriTemplate: '/evaluations/{evaluationId}/grades',
uriVariables: ['evaluationId'],
read: false,
processor: SaveGradesProcessor::class,
name: 'save_evaluation_grades',
),
new Post(
uriTemplate: '/evaluations/{evaluationId}/publish',
uriVariables: ['evaluationId'],
read: false,
processor: PublishGradesProcessor::class,
name: 'publish_evaluation_grades',
),
],
)]
final class GradeResource
{
#[ApiProperty(identifier: true)]
public ?string $id = null;
public ?string $evaluationId = null;
public ?string $studentId = null;
public ?string $studentName = null;
public ?float $value = null;
public ?string $status = null;
public ?DateTimeImmutable $createdAt = null;
public ?DateTimeImmutable $updatedAt = null;
/** @var array<array{studentId: string, value: ?float, status: string}>|null */
public ?array $grades = null;
public ?bool $published = null;
public ?DateTimeImmutable $gradesPublishedAt = null;
public static function fromDomain(Grade $grade, ?string $studentName = null): self
{
$resource = new self();
$resource->id = (string) $grade->id;
$resource->evaluationId = (string) $grade->evaluationId;
$resource->studentId = (string) $grade->studentId;
$resource->studentName = $studentName;
$resource->value = $grade->value?->value;
$resource->status = $grade->status->value;
$resource->createdAt = $grade->createdAt;
$resource->updatedAt = $grade->updatedAt;
return $resource;
}
}