Prompt file imported from YanBerdin/rougecardinalcompany (
.github/prompts/plan-agenda-calendar.prompt.md). Copyright stays with the author.
Plan: Calendrier Interactif /agenda (Mois/Semaine/Jour)
TL;DR
Ajouter une vue calendrier interactive (mois/semaine/jour) à la page /agenda existante, en conservant la vue liste actuelle comme 4ème mode. Implémentation custom avec date-fns (déjà installé v4.1.0) + Tailwind CSS Grid — aucune lib calendrier externe. L'état de navigation est greffé sur AgendaContext. Nouveau fetch DAL fetchEventsForCalendar (fenêtre -6m/+12m, sans filtre "upcoming only") passé en prop calendarEvents via AgendaContainer → AgendaClientContainer.
Phase 1 — DAL & Schémas (base pour tout le reste)
-
Modifier
lib/schemas/agenda.ts— ajouterexport type AgendaView = 'list' | 'month' | 'week' | 'day' -
Modifier
lib/dal/agenda.ts— extraire le mapper internemapEventRow(row)(DRY avecfetchUpcomingEvents), puis ajouter :fetchEventsForCalendar(): même SELECT + JOINs quefetchUpcomingEvents, mais filtredate_debutentre-6 moiset+12 mois(pas de filtre>= now()), sansLIMIT, wrappée aveccache(). RetourneDALResult<Event[]>. Code erreur[ERR_AGENDA_002].
Phase 2 — Context (dépend de Phase 1)
- Modifier
components/features/public-site/agenda/AgendaContext.tsx— ajouter :AgendaState:view: AgendaView,calendarDate: Date,calendarEvents: Event[]AgendaActions:setView(view),navigatePrev(),navigateNext(),navigateToday()AgendaProviderProps: ajoutercalendarEvents: Event[]navigatePrev/Next: utilisentsubMonths/addMonths,subWeeks/addWeeks,subDays/addDaysselonview(importdate-fns)calendarDateinitialisé ànew Date()(aujourd'hui)- Les vues calendrier utilisent
calendarEventsbruts (pas de filtrefilterType— le filtre type reste réservé à la vue liste)
Phase 3 — Composants calendrier (dépend de Phase 2, parallélisable entre eux)
Tous dans components/features/public-site/agenda/calendar/, tous des Client Components.
-
CalendarEventChip.tsx(< 60 lignes) — puce compacte affichant heure + titre tronqué. Props:event: Event,compact?: boolean.compact=true→ point coloré uniquement (pour mobile month view). Couleur par statut:scheduled/confirme→bg-primary,cancelled/annule→bg-muted line-through,completed→bg-muted. -
CalendarDayCell.tsx(< 80 lignes) — cellule jour réutilisée dans month ET week. Props:date: Date,events: Event[],isCurrentMonth?: boolean,onClick?: (date: Date) => void. Affiche numéro + chips (max 2 + "+N" badge).aria-label="[jour long]",aria-current="date"si today,role="gridcell",tabIndexgéré par roving tabindex. -
CalendarNav.tsx(< 80 lignes) — barre de navigation. Props: viauseAgendaContext(). Boutons< Prev/Aujourd'hui/Next >+ titre date formaté (ex: "mai 2026") + Tabs shadcn/ui pour switcherMois | Semaine | Jour | Liste. Responsive: tabs en<select>ouScrollAreasur mobile. -
CalendarMonth.tsx(< 150 lignes) — grille mois. UtiliseeachDayOfInterval(startOfWeek(startOfMonth(date), {weekStartsOn:1}), endOfWeek(endOfMonth(date), {weekStartsOn:1}))pour obtenir 35-42 jours. Headerrole="row"avec 7role="columnheader"(Lun…Dim). CSSgrid-cols-7. Chaque cellule =CalendarDayCell. Sur mobile (< sm): cellules réduites, chips en modecompact. Clic sur cellule →setView('day')+setCalendarDate(date). Keyboard: roving tabindex sur les gridcells. -
CalendarWeek.tsx(< 120 lignes) — vue semaine.eachDayOfInterval(startOfWeek(date, {weekStartsOn:1}), endOfWeek(date, {weekStartsOn:1})). Desktop:grid-cols-7, chaque colonne =CalendarDayCelltaille pleine. Mobile:overflow-x-autosur le grid avecmin-w-[90px]par colonne. -
CalendarDay.tsx(< 100 lignes) — vue jour. FiltrecalendarEventsavecisSameDay(parseISO(event.date), calendarDate). Affiche titre date ("lundi 5 mai 2026"), liste des événements avec heure/lieu/lien billetterie. Si aucun événement: message vide illustré. -
CalendarView.tsx(< 60 lignes) — dispatcher. Litview+calendarEventsdepuis contexte. Rend<CalendarNav />+{view === 'month' ? <CalendarMonth /> : view === 'week' ? <CalendarWeek /> : <CalendarDay />}. -
calendar/index.ts— barrel export deCalendarView.
Phase 4 — Intégration (dépend de Phase 2 et Phase 3)
-
Modifier
components/features/public-site/agenda/types.ts— ajoutercalendarEvents: Event[]àAgendaClientContainerProps. -
Modifier
components/features/public-site/agenda/AgendaContainer.tsx— ajouterfetchEventsForCalendar()auPromise.all, passercalendarEventsen prop. -
Modifier
components/features/public-site/agenda/AgendaClientContainer.tsx— acceptercalendarEvents, le passer auAgendaProvider. Remplacer le bloc<AgendaFilters /><AgendaEventList />par rendu conditionnel :view === 'list' → <AgendaFilters /> + <AgendaEventList /> else → <CalendarView />Utiliser
useAgendaContext()pour lireview.
Fichiers concernés
Créer:
components/features/public-site/agenda/calendar/CalendarEventChip.tsxcomponents/features/public-site/agenda/calendar/CalendarDayCell.tsxcomponents/features/public-site/agenda/calendar/CalendarNav.tsxcomponents/features/public-site/agenda/calendar/CalendarMonth.tsxcomponents/features/public-site/agenda/calendar/CalendarWeek.tsxcomponents/features/public-site/agenda/calendar/CalendarDay.tsxcomponents/features/public-site/agenda/calendar/CalendarView.tsxcomponents/features/public-site/agenda/calendar/index.ts
Modifier:
lib/schemas/agenda.ts— ajouterAgendaViewlib/dal/agenda.ts— ajouterfetchEventsForCalendar+ mapper extraitcomponents/features/public-site/agenda/AgendaContext.tsx— state/actions calendriercomponents/features/public-site/agenda/types.ts—calendarEventsdans propscomponents/features/public-site/agenda/AgendaContainer.tsx— fetch + propcomponents/features/public-site/agenda/AgendaClientContainer.tsx— rendu conditionnel
Accessibilité (WCAG 2.2 — obligatoire per instructions)
role="grid"sur le conteneur calendrier,role="row"sur chaque ligne,role="columnheader"sur les en-têtes jour,role="gridcell"sur chaque cellulearia-label="lundi 4 mai 2026"sur chaque gridcell (date longue)aria-current="date"sur la cellule d'aujourd'hui- Roving tabindex sur les cellules du calendrier (arrow keys pour naviguer)
CalendarNavboutons avecaria-labelexplicites ("Mois précédent", "Mois suivant")- View switcher:
role="tablist"+role="tab"+aria-selected - Contraste: événements utilisent les CSS vars
--primary,--muteddéjà conformes AA
Vérification
pnpm build— zéro erreur TypeScriptpnpm lint— zéro warning ESLint- Navigation manuelle
/agenda:- Vue Mois: grille 7×5 avec events de mai 2026 visibles
- Clic "< Prev" → avril 2026, clic "Aujourd'hui" → retour mai 2026
- Clic cellule jour → bascule sur vue Jour, événements filtrés
- Vue Semaine: 7 colonnes, scroll horizontal mobile
- Onglet "Liste" → retour à la vue liste existante (comportement inchangé)
- Resize browser < 640px: calendrier mois en mode compact (dots)
- Keyboard:
Tab→CalendarNav, arrow keys → navigate cells dans le grid
Décisions
- Pas de lib calendrier externe — date-fns v4 déjà installé + Tailwind Grid suffisent
- Vue liste conservée comme 4ème onglet — aucune régression UX
- Filtre type (dropdown genres) uniquement sur vue liste — pas de filtre sur calendrier
- Fetch séparé pour le calendrier (
fetchEventsForCalendar) — préserve le comportement "upcoming only" de la liste - Hors scope : export ICS global du calendrier mois (le bouton ICS par event reste), time-slot grid (Google Calendar style), drag & drop, infinite scroll, récurrence (données présentes mais non développées)