feat(heures) : calendrier des jours validés (vue Jour) + harmonisation Malio UI
- Calendrier MalioDate en vue Jour (Heures + Heures Conducteurs) : jours entièrement validés (admin) peints en vert. Endpoint GET /work-hours/validation-status?from=&to=[&driver=1] (scope conducteur inversé), chargement à la volée par mois, refresh après validation/saisie/absence. - Suite à @malio/layer-ui 1.7.11 : reserveMessageSpace=false sur les champs ; tous les drawers migrés sur MalioDrawer (titre via slot #header, AppDrawer custom supprimé) ; boutons d'action en MalioButton (deux boutons partagent l'espace) ; inputs date en MalioDate ; MalioDateWeek en vue Semaine. - Boutons d'ajout uniformisés sur « Ajouter » + icône. - .env : EXCLUDED_PUBLIC_HOLIDAYS="null". - Doc : doc/hours-validated-days.md, documentation-content.ts, CLAUDE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user