Complete the six application screens

Adds the day timeline, team list, employee record and leave calendar.
The design export shipped data for these but no markup, so their layout
is designed here from the data shapes and the established style; only
Aperçu and the week grid are ports.

The day view carries a per-hour headcount band. Where the week view
answers "who works how much", this one answers "who is on the floor at
14:00", and the band makes coverage gaps visible without reading every
lane.

Two fixes the tests and linter caught:

- The poste derivation variables were missing from globals.css. Hues
  and tiers were ported but not the --post-*-bg/fg/edge that consume
  them, so every shift chip would have rendered colourless. The palette
  test now asserts all 36 exist.
- ThemeToggle synced state in an effect. The document's data-theme
  attribute is the real store — it is set by the inline script before
  React exists — so it now subscribes with useSyncExternalStore instead
  of keeping a copy that is wrong on first render.

Counter tests cover the DST cases in both directions: a 22:00–06:00
shift lasts 7 or 9 hours on the changeover nights, never 8.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
This commit is contained in:
Claude committed 2026-08-07 22:16:52 +00:00
1 parent a24c1b821c
commit 4012f32f7f
12 files changed
+1158 -11

No files matched your search

+58
View File
@@ -0,0 +1,58 @@
import type { AbsenceKind } from '@/components/planning/AbsenceBar';
export interface DemoCalendarAbsence {
who: string;
type: string;
kind: AbsenceKind;
/** Jours du mois, inclusifs. */
from: number;
to: number;
}
export const MONTH_LABEL = 'Août 2026';
/** Le 1er août 2026 tombe un samedi : la grille démarre le lundi 27 juillet. */
export const MONTH_OFFSET = 5;
export const MONTH_LENGTH = 31;
export const CALENDAR_ABSENCES: DemoCalendarAbsence[] = [
{ who: 'Nour Zaidi', type: 'CP', kind: 'cp', from: 10, to: 14 },
{ who: 'Malik Ouedraogo', type: 'Maladie', kind: 'maladie', from: 13, to: 14 },
{ who: 'Rémi Chartier', type: 'RTT', kind: 'rtt', from: 18, to: 19 },
{ who: 'Elsa Tavares', type: 'CP', kind: 'cp', from: 24, to: 28 },
{ who: 'Inès Bakhti', type: 'Sans solde', kind: 'sans-solde', from: 21, to: 21 },
{ who: 'Alix Perron', type: 'Maladie', kind: 'maladie', from: 5, to: 6 },
{ who: 'Chloé Vasseur', type: 'CP', kind: 'cp', from: 26, to: 28 },
];
export interface DemoPendingRequest {
who: string;
type: string;
range: string;
days: string;
note: string;
}
export const PENDING_REQUESTS: DemoPendingRequest[] = [
{
who: 'Nour Zaidi',
type: 'Congés payés',
range: '10 – 14 août',
days: '5 j',
note: 'Solde restant après validation : 9,5 j',
},
{
who: 'Rémi Chartier',
type: 'RTT',
range: '18 – 19 août',
days: '2 j',
note: 'Chevauche un jour de forte affluence',
},
{
who: 'Alix Perron',
type: 'Sans solde',
range: '31 août',
days: '1 j',
note: 'Aucune contrainte de planning',
},
];