# 1 – Right-click + Move Node avec sélection universelle (insertion précise)

## 0. Contexte et règle métier ciblée

- Clic droit sur un node (leaf ou container) → menu contextuel minimal avec l’entrée **« Déplacer… »**.
- Après clic sur « Déplacer… », l’utilisateur choisit une cible via la **sélection universelle** :
  - s’il clique sur un **container** : le node est déplacé en **tête** de ce container (`toIndex = 0`).
  - s’il clique sur un **item** à l’intérieur d’un container : le node est déplacé **juste au-dessus** de cet item dans son container.
- La mutation passe exclusivement par la commande `MoveNode` (pipeline `/api/commands`), aucune route ad hoc.
- Objectif : intégrer proprement le clic droit et la sélection sans dupliquer de logique DnD et en restant aligné V4 (UI = intentions, moteur = décisions).

---

## 1. Phase A – Cartographier et cadrer le flux MoveNode

**But :** connaître l’état actuel pour ne rien casser et éviter la logique parallèle.

- A.1 – Relire `public/assets/apps/board/modules/dnd.js` :
  - comment `toIndex` est calculé aujourd’hui (items vs containers),
  - où sont faits les appels `sendCommandRef('MoveNode', …)`,
  - comment sont gérées les erreurs (toasts, `handleCommandRejection`).
- A.2 – Noter le contrat MoveNode (payload attendu : `{ nodeId, toParentId, toIndex }`) et les validations côté back (invariants structurels).

**Critère de sortie :** vision claire de la logique actuelle et des points de réutilisation.

---

## 2. Phase B – Helper unique de déplacement (stratégies de position)

**But :** centraliser le calcul de `toIndex` pour éviter la duplication DnD / clic droit.

- B.1 – Créer un module léger (ex. `move-node.js`) exposant :
  - `moveNode({ nodeId, toParentId, toIndex })` : enrobe `sendCommandRef` + gestion d’erreurs.
  - `computeInsertion({ anchorNodeId?, parentId, mode })` :
    - `mode: 'prepend'` → `toIndex = 0`.
    - `mode: 'before-anchor'` → calcule l’index de `anchorNodeId` dans `parentId` et renvoie `toIndex = index(anchor)`.
- B.2 – Raccorder DnD sur `moveNode` (comportement inchangé) pour garantir qu’un seul chemin émet des commandes.

**Critère de sortie :** tout déplacement (DnD ou futur clic droit) passe par ce helper et bénéficie des mêmes toasts/erreurs.

---

## 3. Phase C – Service UiSelection (sélection universelle front-only)

**But :** permettre à toute feature de demander un node cible avec filtres de forme.

- C.1 – API : `requestSelection({ label, allowedShapes, multi = false })`, `cancelSelection()`.
- C.2 – UI :
  - overlay/bandeau avec le `label` + bouton “Annuler”.
  - état global `selectionActive` pour court-circuiter les autres actions.
- C.3 – Capture :
  - intercepter les clics sur `[data-node]`,
  - vérifier la forme (`container` ou `leaf`) selon `allowedShapes`,
  - résoudre la Promise avec `{ boardId, nodeId, shape }` (permet de savoir si l’ancre est un container ou un item).

**Critère de sortie :** n’importe quelle action peut obtenir une adresse `{ boardId, nodeId, shape }` via UiSelection.

---

## 4. Phase D – Menu contextuel clic droit

**But :** surface UX minimaliste pour déclencher la sélection.

- D.1 – `context-menu.js` : `openContextMenu({ x, y, nodeId })`, `closeContextMenu()`, une entrée `data-action="context-move-node"`.
- D.2 – Gestionnaire global `contextmenu` dans `initUiActions` :
  - `preventDefault()`,
  - détection du `nodeId` cible,
  - ouverture du menu positionné (`clientX/clientY`),
  - fermeture sur clic hors menu.
- D.3 – Switch d’actions UI :
  - `context-move-node` → ferme le menu → appelle `moveNodeViaSelection(nodeId)`.

**Critère de sortie :** clic droit ouvre un menu avec « Déplacer… » prêt à déclencher la phase E.

---

## 5. Phase E – Action « Déplacer… » avec insertion précise

**But :** appliquer les règles métier demandées (tête de container ou avant un item).

- E.1 – `moveNodeViaSelection(nodeId)` :
  - récupère le board courant,
  - appelle `requestSelection({ label: "Choisissez la cible du déplacement", allowedShapes: ["container","leaf"] })`.
- E.2 – Décision d’insertion :
  - si sélection = container → `toParentId = selected.nodeId`, `toIndex = 0` (prepend).
  - si sélection = item (leaf ou container enfant) → `toParentId = parent(selected.nodeId)`, `toIndex = index(selected)` (before anchor).
  - refus si `boardId` ≠ board courant ou si `parentId` introuvable.
- E.3 – Exécution :
  - appel `moveNode({ nodeId, toParentId, toIndex })`,
  - toasts en cas de 409/422 (cycle, leaf cible, droits).

**Critère de sortie :** le comportement précis est respecté : clic container → tête ; clic item → au-dessus de l’item.

---

## 6. Phase F – Durcissements et réutilisation

**But :** préparer les futures intégrations.

- F.1 – Teams (quand dispo) : option `requiresEdit` dans `requestSelection` pour filtrer les cibles non éditables ; vérif côté commande déjà assurée par le back.
- F.2 – Réutilisation : documenter que UiSelection + `computeInsertion` servent de base pour Relations, Interactions (porteur → scope), Library/Community (choix de container d’insertion).
- F.3 – Anti-dérive : garder le menu contextuel déclaratif (la liste d’actions se branche sur le switch, pas de logique dans le DOM).

---

## 7. Pourquoi cette approche est alignée V4

- **Une seule porte de mutation** : `MoveNode` via `/api/commands`, pas de REST parallèle.
- **UI = intention** : la sélection reste front-only, le back valide les invariants structurels.
- **Centralisation** : calcul de `toIndex` factorisé, limitant les divergences (DnD, clic droit, futures actions).
- **Extensibilité** : UiSelection est générique et réutilisable par Relations/Interactions ; le menu contextuel reste déclaratif.
- **Sécurité/Propreté** : vérifications back préservent `sys.shape`, ordre, acyclicité ; erreurs surfacent via toasts sans silent fail.
