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:
@@ -1,7 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type { DemoData } from '$types';
|
||||
import type { ScheduleSlot } from '$lib/features/schedule/api/schedule';
|
||||
import { fetchDaySchedule, fetchNextClass } from '$lib/features/schedule/api/schedule';
|
||||
import { recordSync } from '$lib/features/schedule/stores/scheduleCache';
|
||||
import DashboardSection from '$lib/components/molecules/DashboardSection.svelte';
|
||||
import SkeletonList from '$lib/components/atoms/Skeleton/SkeletonList.svelte';
|
||||
import ScheduleWidget from '$lib/components/organisms/StudentSchedule/ScheduleWidget.svelte';
|
||||
import { getActiveRole } from '$features/roles/roleContext.svelte';
|
||||
|
||||
let {
|
||||
demoData,
|
||||
@@ -14,6 +19,47 @@
|
||||
hasRealData?: boolean;
|
||||
isMinor?: boolean;
|
||||
} = $props();
|
||||
|
||||
let isEleve = $derived(getActiveRole() === 'ROLE_ELEVE');
|
||||
|
||||
// Schedule widget state (AC1: "0 tap" — visible dès le dashboard)
|
||||
let scheduleSlots = $state<ScheduleSlot[]>([]);
|
||||
let scheduleNextSlotId = $state<string | null>(null);
|
||||
let scheduleLoading = $state(false);
|
||||
let scheduleError = $state<string | null>(null);
|
||||
|
||||
function formatLocalDate(d: Date): string {
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(d.getDate()).padStart(2, '0');
|
||||
return `${y}-${m}-${day}`;
|
||||
}
|
||||
|
||||
async function loadTodaySchedule() {
|
||||
scheduleLoading = true;
|
||||
scheduleError = null;
|
||||
|
||||
try {
|
||||
const today = formatLocalDate(new Date());
|
||||
scheduleSlots = await fetchDaySchedule(today);
|
||||
recordSync();
|
||||
|
||||
try {
|
||||
const next = await fetchNextClass();
|
||||
scheduleNextSlotId = next?.slotId ?? null;
|
||||
} catch {
|
||||
scheduleNextSlotId = null;
|
||||
}
|
||||
} catch (e) {
|
||||
scheduleError = e instanceof Error ? e.message : 'Erreur de chargement';
|
||||
} finally {
|
||||
scheduleLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isEleve) {
|
||||
loadTodaySchedule();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="dashboard-student">
|
||||
@@ -45,11 +91,18 @@
|
||||
<!-- EDT Section -->
|
||||
<DashboardSection
|
||||
title="Mon emploi du temps"
|
||||
subtitle={hasRealData ? "Aujourd'hui" : undefined}
|
||||
isPlaceholder={!hasRealData}
|
||||
subtitle={isEleve ? "Aujourd'hui" : (hasRealData ? "Aujourd'hui" : undefined)}
|
||||
isPlaceholder={!isEleve && !hasRealData}
|
||||
placeholderMessage={isMinor ? "Ton emploi du temps sera bientôt disponible" : "Votre emploi du temps sera bientôt disponible"}
|
||||
>
|
||||
{#if hasRealData}
|
||||
{#if isEleve}
|
||||
<ScheduleWidget
|
||||
slots={scheduleSlots}
|
||||
nextSlotId={scheduleNextSlotId}
|
||||
isLoading={scheduleLoading}
|
||||
error={scheduleError}
|
||||
/>
|
||||
{:else if hasRealData}
|
||||
{#if isLoading}
|
||||
<SkeletonList items={4} message="Chargement de l'emploi du temps..." />
|
||||
{:else}
|
||||
|
||||
Reference in New Issue
Block a user