---
name: project_mass_update
description: Sélection multiple + popup de modification groupée des options de réservation, ajouté le 11 juillet 2026
metadata:
  type: project
---

Sélection multiple de réservations dans `subnet.php` + modification groupée de leurs options
(branche `feature/mass-update-reservations`, 11 juillet 2026, v0.14). Suit [[project_reservation_groups]].

- **Sélection** : cases à cocher permanentes (admin|tech), case « tout » par tableau/groupe,
  barre d'action **en bas** de la liste. Sélection gérée par `host_id` dans un `Set` JS, conservée
  au changement de mode groupés/plat et au filtrage. Limitée à un subnet (subnet.php est mono-subnet).
- **Popup** : modale Bootstrap construite **côté JS** (instantané) à partir des `$rows` — pour ça
  `getBySubnet()` a été enrichi pour charger le **détail des options** (gateway, no_gateway, routes,
  ntp_enabled) de toutes les résa en **une requête groupée** (`loadOptionsForHosts`, fin du N+1).
- **États initiaux (logique b)** : valeur commune affichée ; case cochée/décochée si homogène,
  indéterminée si « valeurs mêlées » ; routes « divergentes » + bouton « Définir pour tous… ».
- **Cases 3 états** (lease court, no-gateway, NTP) : clic = ON→OFF ; seul le **reset** revient à
  indéterminé. Le ↺ reset n'apparaît que si le champ diffère de son état initial.
- **Seuls les champs modifiés sont écrits** : marqueur `changed[<champ>]` côté POST →
  `Reservation::massUpdate(array $hostIds, array $changes): int` qui **recompose l'état complet
  des options** de chaque hôte avant `saveHostOptions()` (sinon un champ non modifié serait effacé).
- **Exclusivité RFC 3442** : arbitrée dans `massUpdate()`. Règle clé (corrigée en cours de dev) :
  **le champ explicitement présent dans $changes l'emporte** sur un champ non modifié (issu de
  l'état courant). no-gateway imposé vide routes/gateway ; routes imposées vident gateway/no-gateway.
  Fondement gateway/no-gateway=0.0.0.0 : voir [[feedback_kea_cancelled]].
- **Backend** : handler POST `reservation_bulk.php` (admin|tech, vérifie appartenance host_ids au
  subnet), appelle `massUpdate()`, un seul `SyncService::sync()`, `AuditLog 'reservation.bulk_update'`.
- Petit ajout au passage : bouton ✕ de reset sur le filtre de la liste des réservations.

**STATUT au 14 juillet 2026** : **fusionné dans `master`** (fast-forward, branche
`feature/mass-update-reservations` supprimée). Validé visuellement sur le serveur maison
(mode lab/FreeBSD). Le 14 juillet, l'utilisateur a testé le no-gateway sur un hôte en prod
AlmaLinux sans souci (cf. [[feedback_kea_cancelled]]) — le chemin `SyncService::sync()` →
`config-reload` API Kea est donc validé en prod. Reste éventuellement à confirmer un
« Appliquer » du popup de mass update lui-même en prod (probablement OK, même chemin).
Déploiement prod via le `dhcpman-current.tar.gz` régénéré depuis `master` par `~/sync.sh`
(cf. [[project_release_deploy]]).

Spec : `docs/superpowers/specs/2026-07-11-mass-update-reservations-design.md`.
Plan : `docs/superpowers/plans/2026-07-11-mass-update-reservations.md`.
