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>