feat: Permettre aux parents de consulter l'emploi du temps de leurs enfants
Les parents avaient accès au lien "Emploi du temps" dans la navigation,
mais le dashboard n'affichait aucune donnée réelle : la section EDT
restait un placeholder vide ("L'emploi du temps sera disponible...").
Cette implémentation connecte le dashboard parent aux vrais endpoints API
(GET /api/me/children/{childId}/schedule/day|week/{date} et le résumé
multi-enfants), affiche le ScheduleWidget avec le prochain cours mis en
évidence (AC1), permet de cliquer sur chaque enfant dans le résumé pour
voir son EDT détaillé (AC2), et met en cache les endpoints parent dans le
Service Worker pour le mode offline (AC5).
Le handler backend est optimisé pour ne résoudre que l'enfant demandé
(via childId optionnel dans la query) au lieu de tous les enfants à chaque
appel, et les fonctions utilitaires dupliquées (formatSyncDate, timezone)
sont factorisées.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetChildrenSchedule;
|
||||
|
||||
use App\Scolarite\Application\Query\GetStudentSchedule\StudentScheduleSlotDto;
|
||||
|
||||
final readonly class ChildScheduleDto
|
||||
{
|
||||
/**
|
||||
* @param array<StudentScheduleSlotDto> $slots
|
||||
*/
|
||||
public function __construct(
|
||||
public string $childId,
|
||||
public string $firstName,
|
||||
public string $lastName,
|
||||
public array $slots,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetChildrenSchedule;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Scolarite\Application\Port\CurrentCalendarProvider;
|
||||
use App\Scolarite\Application\Port\ParentChildrenReader;
|
||||
use App\Scolarite\Application\Port\ScheduleDisplayReader;
|
||||
use App\Scolarite\Application\Port\StudentClassReader;
|
||||
use App\Scolarite\Application\Query\GetStudentSchedule\StudentScheduleSlotDto;
|
||||
use App\Scolarite\Application\Service\ScheduleResolver;
|
||||
use App\Scolarite\Domain\Model\Schedule\ResolvedScheduleSlot;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
use function array_filter;
|
||||
use function array_map;
|
||||
use function array_unique;
|
||||
use function array_values;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
|
||||
#[AsMessageHandler(bus: 'query.bus')]
|
||||
final readonly class GetChildrenScheduleHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ParentChildrenReader $parentChildrenReader,
|
||||
private StudentClassReader $studentClassReader,
|
||||
private ScheduleResolver $scheduleResolver,
|
||||
private CurrentCalendarProvider $calendarProvider,
|
||||
private ScheduleDisplayReader $displayReader,
|
||||
) {
|
||||
}
|
||||
|
||||
/** @return array<ChildScheduleDto> */
|
||||
public function __invoke(GetChildrenScheduleQuery $query): array
|
||||
{
|
||||
$tenantId = TenantId::fromString($query->tenantId);
|
||||
$allChildren = $this->parentChildrenReader->childrenOf($query->parentId, $tenantId);
|
||||
|
||||
if ($allChildren === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// When a specific child is requested, only resolve that child's schedule
|
||||
$children = $query->childId !== null
|
||||
? array_values(array_filter($allChildren, static fn (array $c): bool => $c['studentId'] === $query->childId))
|
||||
: $allChildren;
|
||||
|
||||
if ($children === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$date = new DateTimeImmutable($query->date);
|
||||
$weekStart = $this->mondayOfWeek($date);
|
||||
$calendar = $this->calendarProvider->forCurrentYear($tenantId);
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($children as $child) {
|
||||
$classId = $this->studentClassReader->currentClassId($child['studentId'], $tenantId);
|
||||
|
||||
if ($classId === null) {
|
||||
$result[] = new ChildScheduleDto(
|
||||
childId: $child['studentId'],
|
||||
firstName: $child['firstName'],
|
||||
lastName: $child['lastName'],
|
||||
slots: [],
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$resolved = $this->scheduleResolver->resolveForWeek(
|
||||
ClassId::fromString($classId),
|
||||
$weekStart,
|
||||
$tenantId,
|
||||
$calendar,
|
||||
);
|
||||
|
||||
$slots = $this->enrichSlots($resolved, $query->tenantId);
|
||||
|
||||
$result[] = new ChildScheduleDto(
|
||||
childId: $child['studentId'],
|
||||
firstName: $child['firstName'],
|
||||
lastName: $child['lastName'],
|
||||
slots: $slots,
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<ResolvedScheduleSlot> $slots
|
||||
*
|
||||
* @return array<StudentScheduleSlotDto>
|
||||
*/
|
||||
private function enrichSlots(array $slots, string $tenantId): array
|
||||
{
|
||||
if ($slots === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$subjectIds = array_values(array_unique(
|
||||
array_map(static fn (ResolvedScheduleSlot $s): string => (string) $s->subjectId, $slots),
|
||||
));
|
||||
$teacherIds = array_values(array_unique(
|
||||
array_map(static fn (ResolvedScheduleSlot $s): string => (string) $s->teacherId, $slots),
|
||||
));
|
||||
|
||||
$subjectNames = $this->displayReader->subjectNames($tenantId, ...$subjectIds);
|
||||
$teacherNames = $this->displayReader->teacherNames($tenantId, ...$teacherIds);
|
||||
|
||||
return array_map(
|
||||
static fn (ResolvedScheduleSlot $s): StudentScheduleSlotDto => StudentScheduleSlotDto::fromResolved(
|
||||
$s,
|
||||
$subjectNames[(string) $s->subjectId] ?? '',
|
||||
$teacherNames[(string) $s->teacherId] ?? '',
|
||||
),
|
||||
$slots,
|
||||
);
|
||||
}
|
||||
|
||||
private function mondayOfWeek(DateTimeImmutable $date): DateTimeImmutable
|
||||
{
|
||||
$dayOfWeek = (int) $date->format('N');
|
||||
|
||||
if ($dayOfWeek === 1) {
|
||||
return $date;
|
||||
}
|
||||
|
||||
return $date->modify('-' . ($dayOfWeek - 1) . ' days');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetChildrenSchedule;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
final readonly class GetChildrenScheduleQuery
|
||||
{
|
||||
public function __construct(
|
||||
public string $parentId,
|
||||
public string $tenantId,
|
||||
public string $date,
|
||||
public ?string $childId = null,
|
||||
) {
|
||||
if (DateTimeImmutable::createFromFormat('Y-m-d', $date) === false) {
|
||||
throw new InvalidArgumentException(sprintf('Date invalide : "%s". Format attendu : YYYY-MM-DD.', $date));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user