feat: Permettre aux élèves de consulter leur emploi du temps
Les élèves n'avaient aucun moyen de voir leur emploi du temps depuis l'application. Cette fonctionnalité ajoute une page dédiée avec deux modes de visualisation (jour et semaine), la navigation temporelle, et le détail des cours au tap. Le backend résout l'EDT de l'élève en chaînant : affectation classe → créneaux récurrents + exceptions + calendrier scolaire → enrichissement des noms (matières/enseignants). Le frontend utilise un cache offline (Workbox NetworkFirst) pour rester consultable hors connexion.
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetStudentSchedule;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Scolarite\Application\Port\CurrentCalendarProvider;
|
||||
use App\Scolarite\Application\Port\ScheduleDisplayReader;
|
||||
use App\Scolarite\Application\Port\StudentClassReader;
|
||||
use App\Scolarite\Application\Service\ScheduleResolver;
|
||||
use App\Scolarite\Domain\Model\Schedule\ResolvedScheduleSlot;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
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 GetStudentScheduleHandler
|
||||
{
|
||||
public function __construct(
|
||||
private StudentClassReader $studentClassReader,
|
||||
private ScheduleResolver $scheduleResolver,
|
||||
private CurrentCalendarProvider $calendarProvider,
|
||||
private ScheduleDisplayReader $displayReader,
|
||||
) {
|
||||
}
|
||||
|
||||
/** @return array<StudentScheduleSlotDto> */
|
||||
public function __invoke(GetStudentScheduleQuery $query): array
|
||||
{
|
||||
$tenantId = TenantId::fromString($query->tenantId);
|
||||
|
||||
$classId = $this->studentClassReader->currentClassId($query->studentId, $tenantId);
|
||||
|
||||
if ($classId === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$date = new DateTimeImmutable($query->date);
|
||||
$weekStart = $this->mondayOfWeek($date);
|
||||
|
||||
$calendar = $this->calendarProvider->forCurrentYear($tenantId);
|
||||
|
||||
$resolved = $this->scheduleResolver->resolveForWeek(
|
||||
ClassId::fromString($classId),
|
||||
$weekStart,
|
||||
$tenantId,
|
||||
$calendar,
|
||||
);
|
||||
|
||||
return $this->enrichSlots($resolved, $query->tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetStudentSchedule;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
final readonly class GetStudentScheduleQuery
|
||||
{
|
||||
public function __construct(
|
||||
public string $studentId,
|
||||
public string $tenantId,
|
||||
public string $date,
|
||||
) {
|
||||
if (DateTimeImmutable::createFromFormat('Y-m-d', $date) === false) {
|
||||
throw new InvalidArgumentException(sprintf('Date invalide : "%s". Format attendu : YYYY-MM-DD.', $date));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetStudentSchedule;
|
||||
|
||||
use App\Scolarite\Domain\Model\Schedule\ResolvedScheduleSlot;
|
||||
|
||||
final readonly class StudentScheduleSlotDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $slotId,
|
||||
public string $date,
|
||||
public int $dayOfWeek,
|
||||
public string $startTime,
|
||||
public string $endTime,
|
||||
public string $subjectId,
|
||||
public string $subjectName,
|
||||
public string $teacherId,
|
||||
public string $teacherName,
|
||||
public ?string $room,
|
||||
public bool $isModified,
|
||||
public ?string $exceptionId,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function fromResolved(
|
||||
ResolvedScheduleSlot $slot,
|
||||
string $subjectName,
|
||||
string $teacherName,
|
||||
): self {
|
||||
return new self(
|
||||
slotId: (string) $slot->slotId,
|
||||
date: $slot->date->format('Y-m-d'),
|
||||
dayOfWeek: $slot->dayOfWeek->value,
|
||||
startTime: $slot->timeSlot->startTime,
|
||||
endTime: $slot->timeSlot->endTime,
|
||||
subjectId: (string) $slot->subjectId,
|
||||
subjectName: $subjectName,
|
||||
teacherId: (string) $slot->teacherId,
|
||||
teacherName: $teacherName,
|
||||
room: $slot->room,
|
||||
isModified: $slot->isModified,
|
||||
exceptionId: $slot->exceptionId !== null ? (string) $slot->exceptionId : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user