# Calendrier des jours validés (écran Heures — vue Jour) ## Objectif Sur l'écran **Heures**, en **vue Jour**, le sélecteur de date est un calendrier (composant `MalioDate` du layer `@malio/layer-ui`) qui **peint en vert** les jours entièrement validés par un admin. La RH repère ainsi d'un coup d'œil les jours où il reste de la validation à faire. ## Définition « jour validé » (vert) Un jour est vert ssi, dans le **périmètre complet** de l'utilisateur : - il porte **au moins une ligne `WorkHour`** dans le scope ciblé ce jour-là, **et** - **aucune** de ces lignes n'est en attente de validation (`isValid = false`). La même mécanique sert les **deux écrans**, avec un scope opposé : écran **Heures** → non-conducteurs (défaut) ; écran **Heures Conducteurs** → conducteurs (`?driver=1`). Conséquences : - Un jour **sans aucune ligne** (rien saisi, ex. week-end, jour futur) reste **neutre** (jamais vert) — « rien fait » n'est pas « tout validé ». - On se base sur la **seule** colonne `work_hours.is_valid` (validation admin/RH). `isSiteValid` (chef de site) n'entre pas en compte → modifier une validation site ne change pas la couleur. - **Scope conducteur** : écran Heures → conducteurs exclus ; écran Heures Conducteurs → seuls les conducteurs (le filtre est inversé via `?driver=1`). ## Périmètre - `ROLE_ADMIN` → tous les employés / tous les sites. - Chef de site → ses sites uniquement. - Le **filtre sites de l'écran est volontairement ignoré** : le vert reflète tout le périmètre (objectif : repérer le moindre jour incomplet, où qu'il soit). Changer le filtre sites de la vue Jour ne recalcule pas le calendrier. ## Chargement des données - Endpoint : `GET /work-hours/validation-status?from=YYYY-MM-DD&to=YYYY-MM-DD[&driver=1]` (`ROLE_USER`). Réponse : `{ from, to, validatedDays: string[] }` (dates `Y-m-d`). - Provider : `App\State\WorkHourValidationStatusProvider` (ressource `App\ApiResource\WorkHourValidationStatus`). - `EmployeeRepository::findScoped($user)` pour le périmètre (ignore tout `siteIds`). - Une requête `WorkHourReadRepositoryInterface::findByDateRangeAndEmployees`. - Filtrage conducteur **par date** via `EmployeeContractResolver::resolveIsDriverForEmployeeAndDate` (mémoïsé par couple employé/jour) : `if ($isDriver !== $driverOnly) continue;` (`driverOnly` = `?driver=1`). - Agrégation par jour : vert ⇔ `total > 0` (lignes du scope) et `pending = 0` (aucune `isValid=false`). - Garde-fou : plage bornée à 366 jours. - Le chargement est **à la volée par mois affiché** (jamais préchargé sur plusieurs années) : `MalioDate` émet `@month-change { month, year }` à l'ouverture du popover et à chaque navigation ; le front fetch la **grille visible** (lundi avant le 1er → dimanche après le dernier jour, pour colorer aussi les jours débordants) et met le résultat en cache par mois (`useHoursPage` / `useDriverHoursPage` → `validatedDaysByMonth` ; ce dernier appelle le service avec `{ driver: true }`). La prop réactive `markedDates` (ISO → `'success'`) recolore la grille. ## Rafraîchissement Toute action qui touche la validation d'un jour recharge le mois concerné s'il est en cache (`reloadValidationMonth`), donc le calendrier se recolore aussitôt : - validation admin d'une ligne (`toggleValidation`) ou en masse (`toggleValidationBulk`) ; - sauvegarde d'heures (`handleSave`) — toute modification réelle remet `isValid=false` ; - création / suppression d'absence (`refreshAfterAbsenceChange`). La validation **site** ne déclenche pas de rechargement (sans effet sur le vert). ## Périmètre d'affichage - **Vue Jour uniquement** : le vert (calendrier `MalioDate` + `markedDates`) est à la maille jour, sur les **deux écrans** (Heures et Heures Conducteurs, via `showValidationCalendar`). La **vue Semaine** utilise un `MalioDateWeek` (sélecteur de semaine, sans coloration). Le `PeriodStepperPicker` ne subsiste que comme fallback de la vue Jour quand `showValidationCalendar` est absent (aucun appelant actuel, conservé par flexibilité). - Précédence d'affichage dans la grille (côté layer) : sélection (fond plein primary) > variante marquée ; le **jour courant** (`today`) garde sa bordure **et** reçoit le fond vert s'il est validé. ## Dépendance layer Nécessite `@malio/layer-ui >= 1.7.x` : prop `markedDates` (`Record<"YYYY-MM-DD", 'success' | 'danger'>`) + event `month-change` sur `MalioDate` (ticket Malio UI MUI-45).