feat(machine) : single save button + link versioning with restore
Backend: - Enrich machine snapshot with componentLinks/pieceLinks/productLinks - Detect link add/remove in MachineAuditSubscriber onFlush - Add link diff comparison in restore preview - Add link restoration in applyRestore for machines - Add integrity warnings for missing linked entities Frontend (submodule update): - Single save button replacing auto-save-on-blur - Link versioning display in version list and restore modal Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1074
docs/superpowers/plans/2026-03-26-machine-single-save.md
Normal file
1074
docs/superpowers/plans/2026-03-26-machine-single-save.md
Normal file
File diff suppressed because it is too large
Load Diff
117
docs/superpowers/specs/2026-03-26-machine-single-save-design.md
Normal file
117
docs/superpowers/specs/2026-03-26-machine-single-save-design.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# Machine : Bouton Save Unique + Versioning des Liens
|
||||
|
||||
**Date :** 2026-03-26
|
||||
**Statut :** Approuvé
|
||||
|
||||
## Contexte
|
||||
|
||||
La page machine utilise actuellement un auto-save au blur pour chaque champ (info, custom fields, constructeurs). Les pages composant/pièce/produit utilisent un bouton unique "Enregistrer les modifications" en bas du formulaire. L'objectif est d'aligner la page machine sur ce pattern.
|
||||
|
||||
De plus, les ajouts/suppressions de liens composant/pièce/produit sur une machine ne sont pas tracés dans le versioning. Ils doivent l'être.
|
||||
|
||||
## Volet 1 : Bouton Save Unique
|
||||
|
||||
### Comportement cible
|
||||
|
||||
- En mode édition, tous les champs (info machine, custom field values, custom field definitions, constructeurs) sont modifiés localement sans appel API.
|
||||
- Un bouton "Enregistrer les modifications" en bas du formulaire sauvegarde tout d'un coup.
|
||||
- Un bouton "Annuler" réinitialise l'état local et sort du mode édition.
|
||||
- Les documents restent en upload/suppression immédiate (inchangé).
|
||||
- Les ajouts/suppressions de liens composant/pièce/produit restent immédiats via modales (inchangé).
|
||||
|
||||
### Changements frontend
|
||||
|
||||
#### MachineInfoCard.vue
|
||||
- Supprimer les `@blur` → `$emit('blur-field')` sur les inputs (nom, référence)
|
||||
- Supprimer le `@change` qui émet `blur-field` sur le select site
|
||||
- Supprimer les `@blur` → `$emit('update-custom-field', field)` sur tous les champs custom
|
||||
- Conserver `@input` / `@update:*` / `set-custom-field-value` pour la mise à jour de l'état local
|
||||
- Le `MachineCustomFieldDefEditor` perd son bouton save propre : l'état est collecté au submit global
|
||||
|
||||
#### machine/[id].vue
|
||||
- Supprimer le handler `@blur-field`
|
||||
- Supprimer le handler `@update-custom-field`
|
||||
- `@update:constructeur-ids` met à jour l'état local sans save
|
||||
- Ajouter le bloc boutons en bas (pattern identique à component/[id]/index.vue) :
|
||||
- "Annuler" (btn-ghost) → `cancelEdition()` : réinitialise depuis `machine.value` + sort du mode édition
|
||||
- "Enregistrer les modifications" (btn-primary, disabled si `!canSubmit`) → `submitEdition()`
|
||||
|
||||
#### useMachineDetailData.ts
|
||||
- Exposer `saving` ref
|
||||
- Exposer `submitEdition()` :
|
||||
1. `updateMachineInfo()` — PATCH machine (nom, ref, site, constructeurs)
|
||||
2. Batch save custom field values (tous les `visibleMachineCustomFields` avec valeur)
|
||||
3. Save custom field definitions si modifiées (`fieldDefs.saveDefinitions()`)
|
||||
4. `loadMachineData()` pour recharger
|
||||
5. Sortie du mode édition + toast succès
|
||||
- Exposer `cancelEdition()` :
|
||||
1. `initMachineFields()` — réinitialise nom, ref, site, constructeurs depuis `machine.value`
|
||||
2. `syncMachineCustomFields()` — réinitialise les custom fields
|
||||
3. Sort du mode édition
|
||||
|
||||
#### useMachineDetailUpdates.ts
|
||||
- `handleMachineConstructeurChange` ne déclenche plus `updateMachineInfo()`, met juste à jour le ref local
|
||||
|
||||
#### useMachineDetailCustomFields.ts
|
||||
- `updateMachineCustomField` n'est plus appelé au blur — sera appelé en batch par `submitEdition()`
|
||||
- Ajouter méthode `saveAllMachineCustomFields()` qui itère sur les champs visibles et sauvegarde ceux avec valeur
|
||||
|
||||
### Validation (`canSubmit`)
|
||||
- Machine existe
|
||||
- Nom non vide
|
||||
- Pas en cours de sauvegarde (`!saving.value`)
|
||||
- `canEdit` est true
|
||||
|
||||
## Volet 2 : Versioning des Liens Machine
|
||||
|
||||
### Comportement cible
|
||||
|
||||
Quand un composant, pièce ou produit est ajouté ou supprimé d'une machine, cela doit :
|
||||
1. Incrémenter la `version` de la Machine
|
||||
2. Créer une entrée `AuditLog` avec diff et snapshot
|
||||
|
||||
### Changements backend
|
||||
|
||||
#### MachineAuditSubscriber — enrichir le snapshot
|
||||
Ajouter au snapshot machine les liens :
|
||||
```php
|
||||
'componentLinks' => array_map(fn($link) => [
|
||||
'id' => $link->getId(),
|
||||
'composantId' => $link->getComposant()->getId(),
|
||||
'composantName' => $link->getComposant()->getName(),
|
||||
], $entity->getComponentLinks()->toArray()),
|
||||
'pieceLinks' => [...],
|
||||
'productLinks' => [...],
|
||||
```
|
||||
|
||||
#### Nouveau subscriber ou service : MachineLinkAuditService
|
||||
Écouter les événements Doctrine `postPersist` et `postRemove` sur les 3 entités link.
|
||||
Quand un lien est créé/supprimé :
|
||||
1. Récupérer la Machine parente
|
||||
2. Incrémenter `$machine->incrementVersion()`
|
||||
3. Créer un `AuditLog` :
|
||||
- `entityType: 'machine'`
|
||||
- `entityId: $machine->getId()`
|
||||
- `action: 'update'`
|
||||
- `diff: { addedComponent: {id, name} }` ou `{ removedPiece: {id, name} }`
|
||||
- `snapshot:` snapshot complet de la machine (avec liens mis à jour)
|
||||
- `version:` nouvelle version
|
||||
|
||||
### Labels pour le diff (frontend)
|
||||
Ajouter au `historyFieldLabels` de la page machine :
|
||||
```js
|
||||
addedComponent: 'Composant ajouté',
|
||||
removedComponent: 'Composant supprimé',
|
||||
addedPiece: 'Pièce ajoutée',
|
||||
removedPiece: 'Pièce supprimée',
|
||||
addedProduct: 'Produit ajouté',
|
||||
removedProduct: 'Produit supprimé',
|
||||
```
|
||||
|
||||
## Ce qui ne change PAS
|
||||
|
||||
- Upload/suppression de documents (immédiat)
|
||||
- Pattern read/edit toggle dans le header
|
||||
- L'affichage des sections composants/pièces/produits
|
||||
- Les modales d'ajout/suppression de liens (restent immédiates)
|
||||
- Le versioning des autres entités (composant, pièce, produit)
|
||||
Reference in New Issue
Block a user