feat(planning): glisser-déposer un reste sur un autre créneau #51

Merged
kyuno merged 1 commit from feat/leftovers-drag-drop into main 2026-09-06 01:06:11 +02:00
Owner

Chantier « gestion des restes » (issue #34) — PR 4/4, la dernière : le glisser-déposer. Closes #34.

@dnd-kit/core

Nouvelle dépendance (+ @dnd-kit/utilities) — première et seule utilisation de DnD de l'app (le réordonnancement de StepListEditor utilise des boutons haut/bas pour éviter d'en ajouter une ; ici le besoin le justifie et le support tactile/clavier vient presque gratuitement).

PlanningPage monte un <DndContext> autour de la grille + du panier :

  • Sensors : MouseSensor + TouchSensor + KeyboardSensor (plutôt que PointerSensor — même couverture, mais pilotable de façon fiable en Cypress via des évènements souris). Distance d'activation 5px → un clic sur « Jeter » ne démarre pas un drag.
  • Collision : pointerWithin (repli closestCenter pour le clavier) — les cases sont jointives, une stratégie par recouvrement de rectangles choisirait une voisine dès que la boîte de l'élément traîné franchit une bordure.

Composants

  • LeftoversBasket — chaque ligne a une poignée ⠿ (useDraggable). Le nœud draggable est la poignée (petite), pas toute la <li> : combiné à pointerWithin, le drop résout proprement vers la seule case sous le curseur. data = { sourceItemId, recipeName, available }.
  • MealCell — useDroppable (data: { date, weekDay, meal }), surbrillance --drop-over. Un item « restes » (sourceItem !== null) porte un badge ♻️ sur .recipe-chip--leftover.
  • LeftoverAssignDialog (nouveau) — au drop, onDragEnd l'ouvre : quantité (défaut = pool disponible, plafonnée), même style que l'étape « portions » de RecipePickerDialog. Confirme → POST /planning/items/leftover (apiClient.assignLeftover) → nouvel item patché dans planning.items.

Tests

apps/web/cypress/e2e/planning-leftovers.feature (Gherkin) :

  • parcours complet : ouvrir le panier → glisser « Lasagnes » sur mercredi midi → confirmer 2 portions (assertion sur le corps POST) → pastille ♻️ visible dans la case → compteur du panier disparu ;
  • jeter un reste depuis le panier → panier vide.

Le drag est piloté par évènements souris bruts (mousedown poignée → mousemove ×2 → mouseup cible). Vérifié localement : 2/2, et aucune régression sur leftovers-basket.cy.ts (3/3), planning-page.cy.ts (7/7), planning.feature (3/3). build + lint verts.

i18n : planning.leftovers.*. Docs : frontend-architecture.md.

🤖 Generated with Claude Code

**Chantier « gestion des restes » ([issue #34](https://git.dev.kyuno.fr/kyuno/batchCooking/issues/34)) — PR 4/4, la dernière : le glisser-déposer.** `Closes #34`. ## `@dnd-kit/core` Nouvelle dépendance (+ `@dnd-kit/utilities`) — première et seule utilisation de DnD de l'app (le réordonnancement de `StepListEditor` utilise des boutons haut/bas *pour éviter* d'en ajouter une ; ici le besoin le justifie et le support tactile/clavier vient presque gratuitement). `PlanningPage` monte un `<DndContext>` autour de la grille + du panier : - **Sensors** : `MouseSensor` + `TouchSensor` + `KeyboardSensor` (plutôt que `PointerSensor` — même couverture, mais pilotable de façon fiable en Cypress via des évènements souris). Distance d'activation 5px → un clic sur « Jeter » ne démarre pas un drag. - **Collision** : `pointerWithin` (repli `closestCenter` pour le clavier) — les cases sont jointives, une stratégie par recouvrement de rectangles choisirait une voisine dès que la boîte de l'élément traîné franchit une bordure. ## Composants - `LeftoversBasket` — chaque ligne a une poignée `⠿` (`useDraggable`). Le **nœud draggable est la poignée** (petite), pas toute la `<li>` : combiné à `pointerWithin`, le drop résout proprement vers la seule case sous le curseur. `data` = `{ sourceItemId, recipeName, available }`. - `MealCell` — `useDroppable` (`data: { date, weekDay, meal }`), surbrillance `--drop-over`. Un item « restes » (`sourceItem !== null`) porte un badge ♻️ sur `.recipe-chip--leftover`. - `LeftoverAssignDialog` (nouveau) — au drop, `onDragEnd` l'ouvre : quantité (défaut = pool disponible, plafonnée), même style que l'étape « portions » de `RecipePickerDialog`. Confirme → `POST /planning/items/leftover` (`apiClient.assignLeftover`) → nouvel item patché dans `planning.items`. ## Tests `apps/web/cypress/e2e/planning-leftovers.feature` (Gherkin) : - **parcours complet** : ouvrir le panier → glisser « Lasagnes » sur mercredi midi → confirmer 2 portions (assertion sur le corps `POST`) → pastille ♻️ visible dans la case → compteur du panier disparu ; - jeter un reste depuis le panier → panier vide. Le drag est piloté par évènements souris bruts (`mousedown` poignée → `mousemove` ×2 → `mouseup` cible). **Vérifié localement : 2/2**, et **aucune régression** sur `leftovers-basket.cy.ts` (3/3), `planning-page.cy.ts` (7/7), `planning.feature` (3/3). `build` + `lint` verts. i18n : `planning.leftovers.*`. Docs : `frontend-architecture.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
kyuno added 1 commit 2026-09-06 00:48:14 +02:00
feat(planning): glisser-déposer un reste sur un autre créneau
All checks were successful
CI / lint (push) Successful in 4m35s
CI / build (push) Successful in 4m51s
CI / intent-service-test (push) Successful in 5m42s
CI / e2e (push) Successful in 10m29s
CI / test (push) Successful in 17m43s
1bc9304cdc
Dernière PR (4/4) du chantier « gestion des restes » (issue #34).

`@dnd-kit/core` (+ `@dnd-kit/utilities`) — première et seule utilisation
de DnD de l'app. `PlanningPage` monte un `<DndContext>` (Mouse + Touch +
Keyboard sensors, distance d'activation 5px pour ne pas voler le clic
« Jeter ») autour de la grille et du panier.

- `LeftoversBasket` : chaque ligne a une poignée `⠿` `useDraggable` — le
  nœud draggable est la poignée elle-même (petite), pas toute la `<li>`,
  pour qu'une détection de collision au pointeur (`pointerWithin`, repli
  `closestCenter` pour le clavier) résolve proprement vers la seule case
  sous le curseur. `data` porte `sourceItemId`/`recipeName`/`available`.
- `MealCell` : `useDroppable` (`data: { date, weekDay, meal }`),
  surbrillance `--drop-over` au survol. Un item « restes »
  (`sourceItem !== null`) porte un badge ♻️ sur sa pastille.
- Au drop, `onDragEnd` ouvre `LeftoverAssignDialog` (quantité, défaut =
  disponible, plafonné) → `POST /planning/items/leftover`
  (`apiClient.assignLeftover`) → nouvel item patché dans `planning.items`.

Tests : `planning-leftovers.feature` (Gherkin) — parcours complet drag →
confirmation quantité → pastille ♻️ visible → compteur du panier à jour ;
+ jet d'un reste depuis le panier. Le drag est piloté par événements
souris bruts (mousedown poignée → mousemove ×2 → mouseup cible).
Vérifié localement : 2/2, et aucune régression sur
`leftovers-basket.cy.ts` / `planning-page.cy.ts` / `planning.feature`.

i18n : `planning.leftovers.*` (badge, poignée, dialogue). Docs :
`frontend-architecture.md`.

Closes #34

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
kyuno merged commit d1d68b0bf7 into main 2026-09-06 01:06:11 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: kyuno/batchCooking#51
No description provided.