Commit Graph

33 Commits

Author SHA1 Message Date
louispaulb
70c89b2cea Garde: 2 horaires par règle — semaine (soir 17h-minuit) vs fin de semaine (8h-minuit)
Une règle a maintenant un shift SEMAINE + un shift FIN DE SEMAINE (optionnel). La rotation reste
UNE séquence (même tech sur la semaine) ; la génération choisit l'horaire selon le jour : weekend
(sam/dim) → shiftWeekend, sinon shift semaine. Wipe couvre les 2 shifts. Modèles semés:
« Garde soir 17h-00h » (17:00-23:59) + « Garde fin de sem. 8h-00h » (08:00-23:59), on_call.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 22:33:07 -04:00
louispaulb
0320c8716f Garde: fix « 0 assignations » — rétrocompat des règles sans steps/anchor
Les règles créées avant le refactor (techs[]+periodWeeks, sans steps/anchor) donnaient 0 assignation
(rotationTech voyait 0 étape) + anchor manquant → toujours step 0. Fix: ruleSteps() convertit techs[]+
periodWeeks en étapes {tech,weeks} (collapse des doublons consécutifs) et ruleAnchor() retombe sur une
référence stable (epoch). rotationTech/gardeSeqLabel/editGardeRule passent par ces helpers → les vieilles
règles génèrent + s'affichent correctement, sans devoir tout recréer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 22:29:57 -04:00
louispaulb
e454e3f276 Garde: moteur fiable — séquence d'étapes {tech, weeks} + semaine d'ancrage (parcours déterministe)
Refonte du principe de rotation suite aux bugs (3 sem. au lieu de 2, édition non reflétée) :
- Séquence = étapes {tech, nb de semaines} ; « 2 semaines consécutives » = weeks:2 (fini les
  doublons+periodWeeks qui se multipliaient). Tours inégaux = weeks différents par étape.
- ANCRAGE explicite (semaine de départ, donnée de référence stockée) : on PARCOURT la séquence
  semaine par semaine depuis l'ancrage → déterministe, reflète les éditions, pas de dérive.
- Vérifié: A×2,B,C ancré 8 juin → A,A,B,C,A,A,B,C (A toujours 2 consécutives) ; réordonner reflète.
- Aperçu et génération utilisent le même parcours. Migration auto des anciennes règles (techs+period→steps).
  Rappel: après édition, re-cliquer « Générer la garde » (l'horizon est réécrit, wipe ciblé du shift).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 22:15:50 -04:00
louispaulb
761498d65c Planification: marquer une absence d'1 jour depuis la grille (touche A + menu)
Clic sur une case → touche « A » (bascule) ou menu « Marquer absent ce jour / Retirer l'absence » →
crée/supprime une Tech Availability d'1 jour APPROUVÉE (hachurée tout de suite). Multi-sélection
supportée (A marque toutes, re-A retire). Endpoint POST /roster/absence/set {tech,date,type,remove}
(remove ne touche que les absences d'un jour, pas les vacances multi-jours). Type défaut Congé.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 22:02:39 -04:00
louispaulb
58253d2e2f Garde: génération sur un HORIZON multi-semaines (évènement récurrent) au lieu d'une seule semaine
Avant: « Appliquer à la semaine » n'écrivait qu'une semaine → 1 seul tech (rotation hebdo). Maintenant
« Générer la garde » matérialise la rotation sur N semaines (4/8/12/26) d'un coup, écrit direct (publié),
navigable semaine par semaine — comme un évènement récurrent. Endpoint /roster/garde/apply : wipe ciblé
des shifts de garde dans l'horizon + recréation (idempotent, reflète l'édition de la séquence). Saut
d'absent conservé. (source='manuel' car le Select n'autorise que solveur/manuel.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 21:58:15 -04:00
louispaulb
2b71d1c78c Garde: presets Soirs de semaine/Fin de semaine (combinables) + rotation par semaine (défaut) + aperçu
- Plages combinables: « Soirs de semaine » (L-V) + « Fin de semaine » (S-D) en toggles (+ chips fins).
- Rotation par défaut = PAR SEMAINE (garde = bloc hebdo) → corrige la séquence brisée (par jour, un
  week-end pouvait avoir 2 personnes). Index de semaine continu dans le temps → ordre respecté en
  naviguant. Séquence [A,A,B] = A 2 semaines consécutives, puis B. (Sélecteur jour/semaine conservé.)
- APERÇU live : qui est de garde sur les 8 prochaines semaines, reflète la file en cours d'édition →
  on voit l'ordre respecté + l'effet des modifs avant d'appliquer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 21:47:04 -04:00
louispaulb
16325ed967 Garde: la rotation avance par jour de garde (la séquence continue sur les jours affichés)
Avant: rotation par semaine → dans une vue d'1 semaine, un seul membre apparaissait. Maintenant la
rotation avance par OCCURRENCE de garde (occurrenceIndex = nb de jours matching weekdays depuis l'époque)
→ chaque jour de garde prend le membre suivant de la séquence (A,A,B,C…), visible sur toute la vue.
Sélecteur « Rotation : par jour de garde / par semaine » (+ N consécutifs). Défaut = par jour ;
les règles existantes (sans unit) basculent en par-jour. Saut d'absent + doublons conservés.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 21:17:25 -04:00
louispaulb
d0ab57b1b5 Garde: séquence de rotation éditable — doublons permis + remplacer un tech par position
Le multi-select est remplacé par un constructeur de SUITE ordonnée : « Ajouter un tech à la suite »
(push, doublons autorisés → tours inégaux ex. A,A,B,C) ; chaque position a un select pour REMPLACER
le tech, + ↑/↓ pour réordonner, + ✕ pour retirer. Couvre rotations inégales et substitutions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 21:01:42 -04:00
louispaulb
05b5b16a5d Garde: dept libre + liste techs complète + réordonner la rotation + éditer + 2 sem. consécutives
Fix listes vides: département = champ libre optionnel (existants OU texte), liste des techs = TOUS
(plus de désactivation sur dept). Réordonnancement de la rotation (↑/↓), édition d'une règle (crayon
→ recharge dans le formulaire → Mettre à jour). Champ « Sem. consécutives / tech » (mettre 2 = un tech
fait 2 semaines de suite). Annuler l'édition.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:53:26 -04:00
louispaulb
fe60eeb485 Planification: drapeau « longue durée » sur absence (maternité/invalidité = à remplacer)
Tech Availability gagne long_term (Check). Case à cocher « Longue durée » dans le dialogue Congés.
absencesByTechDay encode « (longue durée) » → applyTemplate force « à remplacer » (pas juste sauter
comme des vacances), même si l'absence ne couvre pas toute la semaine. Complète l'intelligence
permanent vs vacances (avant: déduit de « absent toute la semaine »).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:31:39 -04:00
louispaulb
8d946daf8d Planification: rotation de garde par département (récurrence + rotation)
Dialogue « Garde » : règles par département (tech_group) = {shift de garde, jours, période (toutes
les X sem.), techs en rotation ordonnés}. Indépendantes entre départements (non synchronisées).
« Appliquer à la semaine » génère les gardes : pour chaque jour ciblé, le tech de garde = rotation
(index de semaine / période % liste) ; un tech absent est SAUTÉ au profit du suivant. Règles persistées
(localStorage roster-garde-rules-v1). Les gardes s'affichent en pointillé ambre (on_call), hors heures
travaillées/booking déjà en place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:27:24 -04:00
louispaulb
060ee578c3 Planification: modèle par défaut (★) appliqué en 1 clic
Marquer un modèle de semaine comme défaut (★ dans le menu Modèles, un seul à la fois) →
bouton ★ <nom> dans la barre pour l'appliquer en 1 clic (avec l'intelligence d'absences déjà en place).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:23:40 -04:00
louispaulb
cdd72856a4 Planification: application de modèle consciente des absences (permanent vs vacances)
applyTemplate respecte maintenant les absences : on n'assigne pas un tech absent ce jour-là.
- Absent toute la semaine (≈ congé permanent: maternité/blessure) → signalé « à remplacer » (warning).
- Absent quelques jours (≈ vacances) → ces jours sautés, le reste du patron tient.
Le toast résume: N assignations, absences partielles ignorées, et qui est à remplacer (avec le type).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:16:05 -04:00
louispaulb
9261692c7f Planification: menu de case court (2 raccourcis + slider en haut) — Appliquer toujours atteignable
- Menu réduit: 2 raccourcis (Normal 8–17, Soir 16–20) + slider d'ajustement remontés EN HAUT
  (près du clic) ; Appliquer dans la rangée du slider → reste visible même si la case est en bas
  (max-height 85vh + flip auto Quasar). Retrait de la longue liste des modèles.
- quickShift(min,max) + applyWindow() factorisés. Shifts en place + Copier/Coller/Vider en icônes
  compactes sous le slider. Nettoyage cellCode/cellColor/codeByShift/colorByShift/addFromMenu inutilisés.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:55:32 -04:00
louispaulb
ece0ccb6ff Planification: Suppr/Backspace pour vider les cases sélectionnées (ou la case active)
onKey: garde anti-champ (n'intercepte pas dans input/textarea/select/contenteditable) + empêche
le 'retour arrière' du navigateur (preventDefault). Suppr/⌫ vide la sélection (ou la case cliquée),
avec pushHistory (annulable). Hint de légende mis à jour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:46:04 -04:00
louispaulb
89a366d197 Planification: hachuré = ABSENT (congé/pause), garde = pointillé ambre (sur appel hors heures)
- Hachuré gris = ABSENT (Tech Availability approuvée + En pause). Nouvel endpoint /roster/absences
  (En pause global + congés approuvés par jour) → la cellule d'un tech absent est hachurée (tooltip = type).
  Remplace l'ancien 'P' pause.
- GARDE = nouveau visuel: bande à CONTOUR POINTILLÉ AMBRE + fond ambre léger (sur appel, hors heures
  d'ouverture) — distinct du travail planifié et de l'absent.
- Légende: dispo (matin→soir) · occupation · absent (hachuré) · garde (pointillé). Retrait 'P pause'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:43:47 -04:00
louispaulb
021417f29f Planification: contour 1px foncé autour de la bande de disponibilité (vs fond du timeline)
La bande pâle se confondait avec le fond gris du timeline → contour 1px bleu-violet foncé
(rgba 55,65,120,.5) box-sizing border-box pour ne pas déborder. Garde = contour brun.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:36:07 -04:00
louispaulb
5ad17c0d19 Planification: fix slider d'ajustement (menu) — sélection de texte + resize continu
- user-select:none sur le menu (rendu en portail → n'héritait pas du no-select de la grille)
  → le glissement ne sélectionne plus le texte.
- Retrait des bulles de label de la q-range (8h/18:30h) qui changeaient de largeur → le menu ne
  se redimensionne plus. La valeur reste affichée en direct sous le slider (8h–18:30h).
- Largeur du menu fixée (260px) + @mousedown.stop sur le slider.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:29:57 -04:00
louispaulb
94c7566dd3 Planification: barre de temps pâle + barre de statut opaque vert→orange (occupation)
- Barre de TEMPS (dispo) = pâle (bleu très pâle matin → violet pâle soir) : repère discret du quand.
- Barre de STATUT (occupé) = OPAQUE vert (peu) → orange (plein) → rouge (surbooké), positionnée
  aux vraies heures des jobs → ressort nettement sur le fond pâle ; les trous pâles = offrables.
- Légende: dispo (matin→soir) pâle + occupation (vert→orange) + garde.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:22:05 -04:00
louispaulb
d7867e2a62 Planification: cellule = barre de dispo seule sur une échelle de temps (règle horaire en-tête)
- En-tête de chaque jour: règle horaire (graduations alignées sur l'axe adaptatif, ex 8/12/16/20).
- Cellule réduite à LA barre de dispo (dégradé matin→soir, occupé assombri, garde hachuré) —
  plus de texte d'intervalle : on lit la position contre la règle, détails au survol.
- Barre un peu plus haute (11px). Retrait cellLabel/cell-int/cell-chips.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:18:38 -04:00
louispaulb
738d315785 Planification: barre de dispo dégradée par l'heure (bleu matin → violet soir), fini les lettres
- Cellules: plus de chip-lettre (J/S/M/D). La bande de la timeline est colorée par l'HEURE :
  dégradé bleu pâle le matin → violet le soir (hsl 210→270). On lit 'quand' d'un coup d'œil.
- Occupé = assombrit la bande (clair = libre/offrable, foncé = pris) ; rouge si surbooké.
  (Remplace le feu vert/orange/rouge — plus sobre.) Garde reste hachurée.
- Légende: échantillon dégradé « matin → soir » + « garde » (hachuré) au lieu de la liste de lettres.
- Intervalle (8–16) gardé en texte. occColor retiré.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:12:27 -04:00
louispaulb
142ce45755 Planification: ne montrer que les presets nommés + coller multi-cases fiable + fix Cmd+C/V
- Nettoyage des modèles auto en double fait côté données (8h–16h→Jour, 7h–15h→Matinal,
  9h–17h→Décalé, 8h–17h→Jour, 8h–22h→Soir) — restent 5 presets propres.
- Barre d'assignation + légende = presetTemplates (modèles NOMMÉS seulement) → restent propres
  même si des modèles auto réapparaissent.
- Fix copier-coller : le clic ouvrait le menu ET vidait la sélection → Cmd+C voyait 0 cellule.
  Maintenant on mémorise activeCell (dernière case cliquée) ; Cmd+C/V ferment le menu et marchent
  sans multi-sélection. Coller = vers la sélection si multi, sinon la case active.
- Indicateur « N copié(s) » visible. Coller multi-cases via la barre (déjà) + Cmd+V sur sélection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:04:37 -04:00
louispaulb
7f6d314cc0 Planification: fix menu (régression cellHours) + copier/coller + slider d'ajustement dans le menu
BUG: le menu de case plantait sur les cases occupées (cellHours retiré mais encore appelé l.259)
→ c'est ce qui cassait le copier-coller au clic. Corrigé ({{ a.hours }}h).

- Copier/Coller déplacés dans le MENU de la case (clic simple, plus besoin de Cmd+clic) :
  « Copier cette case » / « Coller ». (Boutons barre + Ctrl/Cmd+C/V conservés.)
- « Ajuster l'horaire (glisser) » : q-range dans le menu → élargir/réduire le créneau de dispo,
  + toggle Garde. Applique = trouve/crée un modèle auto-nommé (8h–18h) et remplace la case.
  → la dispo élargie est aussitôt offerte au booking (modèle standard).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 17:24:07 -04:00
louispaulb
c2f3e4d666 Planification: copier-coller de cases + créneaux custom (slider q-range) + auto-nommage
- Copier-coller pour bâtir l'horaire vite : sélectionne une case → Copier (ses shifts) →
  sélectionne des cibles → Coller (duplique). Boutons dans la barre + raccourcis Ctrl+C / Ctrl+V.
  Copier une case vide puis Coller = vider les cases.
- Créneaux CUSTOM : nouveau modèle créé via slider q-range (2 poignées, pas 0.5 h) → plus besoin
  de prévoir tous les types. Nom AUTO si vide (« 8h–17h » d'après les heures).
- Presets standards semés : 7h–15h, 9h–17h, 8h–17h (+ Jour 8h-16h existant) — triés par usage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 16:02:15 -04:00
louispaulb
dfefd7822f Planification: cellule sans icônes — juste intervalle + timeline
Retrait des icônes en cellule (☀/🌆/🌙/🛡️). Le libellé = uniquement l'intervalle début–fin
(ex 8–16) ; le timeline (bande pleine vs garde hachurée) porte le reste visuellement.
Tag garde dans l'infobulle: '(garde)' au lieu de l'emoji.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:55:02 -04:00
louispaulb
17d8442b98 Roster: la garde ne compte PAS comme heures travaillées (mise en dispo)
Garde (on_call) exclue partout des heures travaillées + du coût:
- hub statsByDay: heures = somme des shifts NON-garde ; nouveau compteur on_call/jour (techs en dispo).
- Ops: hoursOf (heures/tech + alerte heures supp) et costByDate/weekCost excluent la garde.
- nouvelle ligne de pied '🛡️ Garde' = nb de techs en disponibilité/jour (si applicable).
Cohérent avec l'occupation (déjà hors-garde) : la garde = réserve d'urgence, ni offerte ni facturée.
Vérifié 8 juin: 112 h travaillées (garde 6 h exclue), garde=1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:52:01 -04:00
louispaulb
72845e2057 Planification: axe timeline adaptatif + intervalle texte + modèles triés par usage
#1 axe trop large (0-24) → axe ADAPTATIF calé sur l'amplitude réelle des shifts réguliers de la
semaine (garde n'élargit pas) → barres plus larges, position lisible. Intervalle début–fin REMIS
en texte dans la cellule (☀ 8–16 🛡️) = référence sans survol. Infobulle = capacité offrable
(corrige aussi un bug: shiftH→bookableH).
#2 modèles d'assignation TRIÉS PAR USAGE (les plus utilisés en premier) + infobulle nom.
(Rappel: créer des modèles custom = éditeur « Types de shift ».)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:48:47 -04:00
louispaulb
1ab9f64b48 Roster: quart de Garde (on_call) = réserve d'urgence, jamais offert au booking
Modèle: champ on_call (Check) sur Shift Template. Un quart garde:
- N'est JAMAIS offert au booking client (techGaps retourne null) — vérifié: tech Jour+Garde
  n'offre que la fenêtre Jour, aucun créneau dans la plage de garde.
- Est EXCLU du dénominateur d'occupation (heures offrables), affiché à part.
- Timeline: bande HACHURÉE (vs neutre pour l'offrable) + 🛡️ dans le label + tag (garde) en infobulle.
- Éditeur de modèles: bascule '🛡️ Garde' pour créer/marquer un quart de garde.
hub: fetchTemplates expose on_call; create/update template le gèrent. Champ ajouté à
setup_dispatch_custom_fields.py (persistance). Démo: Garde 18h-minuit marquée on_call.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:37:34 -04:00
louispaulb
049897e021 Planification: micro-timeline 24h, multi-shift, neutre/coloré, label h utilisées + icône période
Refonte selon retour: axe 24h (00→24). Chaque shift = bande NEUTRE positionnée (multi-shift OK:
Jour + Garde affichés en 2 bandes, gère le passage minuit). Jobs pris = traits COLORÉS (charge:
vert/orange/rouge). Label compact = icône période (☀/🌆/🌙) + heures utilisées/total (ex 4/8).
Intervalles exacts par shift dans l'infobulle. Tick à midi (50%).

Démo 8 juin: TECH-4738 = Jour 8-16 + Garde 18h-minuit (multi-shift), Matinal 7-15 / Décalé 9-17.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:27:32 -04:00
louispaulb
341c8e5a64 Planification: mini-timeline positionnée (fenêtre réelle du shift + blocs pris)
Avant: 'J8' ne distinguait pas 7-15 de 9-17 → mêmes créneaux apparents, dispo réelle différente.
Maintenant chaque cellule affiche: chip (lettre) + intervalle '7–15', et une mini-timeline sur un
axe de journée (06:00→21:00) où la fenêtre du shift est positionnée (donc 7-15 à gauche, 9-17 à
droite = visuellement distinctes) avec les blocs de jobs pris (couleur selon charge) → les TROUS
restants = créneaux offrables. Infobulle = intervalle + h occupées/h (%).

- hub occupancyByTechDay renvoie {h, blocks:[{s,e}]} (heures de début réelles des jobs).
- ops: cellWindow/axisPos/shiftStyle/blockStyle, rendu .tl/.tl-shift/.tl-blk + tick midi.
- démo 8 juin: modèles Matinal 7-15 + Décalé 9-17, techs alignés (7→13.8, 9→18.6 surbooké).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:21:04 -04:00
louispaulb
49795f858b Planification: taux d'occupation par cellule (jobs assignés / heures du shift)
Chaque cellule tech×jour avec un shift affiche, sous le chip (J8), une mini-barre + % colorés
(vert <70, orange 70-99, rouge >=100 surbooké) + infobulle = intervalle du shift + h occupées/h.
Occupation = Σ duration_h des Dispatch Jobs planifiés assignés ce jour ÷ Σ heures du shift.

- hub: occupancyByTechDay(start,days) + GET /roster/occupancy → map 'TECH|date': heures.
- ops api: getOccupancy ; PlanificationPage: occCells (computed), cellOcc/occColor/cellInterval,
  rendu barre + q-tooltip, chargé dans loadStats. Données démo semaine 8 juin (45/85/120%).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:13:27 -04:00
louispaulb
1f47ee4eae Ops cohérence: composants partagés TechSelect (autosuggest) + SkillSelect (chips)
Corrige les manques signalés: champ technicien (congés) → autosuggest typeahead; compétences
(demande de shift + éditeur équipe) → chips au lieu de texte libre. Composants réutilisables
pour une UX cohérente partout (et le copilote/réassignation à venir).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 11:57:58 -04:00
louispaulb
f4138cdd75 Roster AI (planification) + prise de rendez-vous client
Solveur OR-Tools (services/roster-solver) : couverture, compétences,
équité, coût chargé, cadence/efficacité, capacité-par-job ; contraintes
dures/souples façon Timefold.

Hub (lib/roster.js) : génération via solveur, publication par réécriture
de semaine (anti-doublons), demande (effectif ou nb de jobs), cadence/coût/
compétences par tech, pause, congés (Tech Availability + approbation),
booking (slots roster-aware / fit 3-dispos / confirm) + portail public /book.
Réessai sur serialization failures frappe_pg ; appels ERP séquentiels.

Ops : page Planification (grille compacte « J8 », multi-shift, drag-select
+ undo/redo, modèles de semaine, éditeur cadence&coût, congés, SMS opt-in),
page Rendez-vous (répartiteur), jobColor tech en pause → tickets rouges.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 16:42:44 -04:00