# 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)