Appearance
BDD-150 — Perte de configuration des transformations après rafraîchissement
Contexte
Plusieurs anomalies de persistance affectaient la configuration des blocs de traitement dans l'éditeur de workflow. La configuration saisie par l'utilisateur pouvait disparaître dans plusieurs situations : déplacement d'un bloc sur le canevas, fermeture ou annulation de la modale de configuration, rafraîchissement de la page, ou réouverture d'un workflow déjà sauvegardé. En outre, le sélecteur de type de traitement ne se verrouillait pas après la première sauvegarde, permettant une modification accidentelle du type.
Causes racines identifiées
handleCancelécrasait la configuration active — lors de la fermeture ou de l'annulation de la modale, tous les setters de configuration (activeMapperConfig,activeNettoyerConfig, etc.) étaient appelés avec les valeurs du snapshot d'ouverture. Pour les sous-types inactifs, ces snapshots valaientnull. Le dernier setter (activeEmpilerConfig) écrasait le nœud avectreatmentType: 'empiler', config: null, puis l'autosauvegarde persistait cet état incorrect.Les événements Vue Flow corrompaient l'état serveur fraîchement chargé — lors du chargement initial ou après une sauvegarde, Vue Flow émettait des événements
update:nodes/update:edgesqui mélangeaient les nouvelles données serveur avec l'ancien état local.Décalage des empreintes de sauvegarde —
lastSavedFingerprintutilisait le formatfingerprintNodesFromApi(coordonnées brutes), tandis que le watcher d'autosauvegarde utilisaitgetPersistedPayload()(coordonnées arrondies). La différence déclenchait une autosauvegarde immédiate après chaque sauvegarde explicite.normalizeParentFlowNodene préservait pastreatmentType— lors du déplacement d'un bloc, la fusion des mises à jour partielles de Vue Flow n'incluait pastreatmentTypenitreatmentTypeSaved, les effaçant du nœud.toFlowNodeinféraittreatmentTypeSaved: truedepuis les données serveur — le type se trouvait donc verrouillé dès la réouverture d'un workflow, avant même toute action explicite de l'utilisateur.
Critères d'acceptance
| # | Critère | Couvert par |
|---|---|---|
| 1 | La configuration d'un bloc traitement est préservée après déplacement sur le canevas | workflow-canvas.sync.js — normalizeParentFlowNode préserve treatmentType et treatmentTypeSaved |
| 2 | La colonne source est sélectionnable dans les blocs Extraire des caractères et Fractionner la colonne | WorkflowExtraireCaracteresPanel.vue, WorkflowDecouperColonnePanel.vue — q-select désactivé pendant le chargement |
| 3 | Le type de traitement ne revient pas à la valeur par défaut après sauvegarde puis réouverture | Guard if (v === null) return dans tous les setters de config ; fixe handleCancel |
| 4 | Le sélecteur de type est verrouillé uniquement après la première sauvegarde explicite | treatmentTypeSaved positionné à true par applyServerCanvasAfterSave (après save) et restauré depuis config.type par toFlowNode (après rafraîchissement) |
| 5 | La configuration n'est pas perdue après rafraîchissement de la page | Même correction que critère 3 ; suppression de la boucle d'autosauvegarde infinie via unification des empreintes |
Fichiers modifiés
Frontend
| Fichier | Rôle |
|---|---|
front/src/composables/useActiveTraitementSubtypeConfig.js | Guard if (v === null) return au début du setter — empêche un snapshot null d'écraser la configuration active |
front/src/composables/useWorkflowEditorFlowSync.js | Paramètre isApplyingServerState ajouté — onFlowNodesUpdate et onFlowEdgesUpdate retournent tôt quand l'état serveur est en cours d'application |
front/src/pages/admin/workflows/WorkflowEditorPage.vue | applyServerCanvasAfterSave met treatmentTypeSaved: true après sauvegarde ; resetSaveFingerprint et lastSavedFingerprint utilisent désormais le même format getPersistedPayload() ; computed isNodeTreatmentTypeLocked ; fonction onResetTreatmentType |
front/src/pages/admin/workflows/workflow-canvas.model.js | toFlowNode restaure treatmentTypeSaved depuis le config.type serveur — true si type non-mapper enregistré, false sinon (nouveaux blocs) |
front/src/pages/admin/workflows/workflow-canvas.sync.js | normalizeParentFlowNode préserve treatmentType et treatmentTypeSaved lors des fusions partielles Vue Flow |
front/src/components/workflows/WorkflowNodeConfigModal.vue | Prop isTreatmentTypeLocked ; emit reset-treatment-type ; sélecteur de type désactivé avec bouton « Modifier » et dialogue de confirmation |
front/src/components/workflows/WorkflowExtraireCaracteresPanel.vue | q-select Colonne source désactivé pendant le chargement des options |
front/src/components/workflows/WorkflowDecouperColonnePanel.vue | Même correction que WorkflowExtraireCaracteresPanel.vue |
Tests
| Fichier | Type | Ce qui est couvert |
|---|---|---|
front/src/__tests__/composables/useActiveTraitementSubtypeConfig.spec.js | Unit | Guard null dans le setter ; getter selon treatmentType et config.type ; non-modification de nœuds non-traitement |
front/src/__tests__/composables/useWorkflowEditorFlowSync.spec.js | Unit | Blocage des mises à jour quand isApplyingServerState est vrai ; reprise après passage à false ; comportement sans le paramètre optionnel |
front/src/__tests__/pages/admin/workflows/workflow-canvas.sync.spec.js | Unit | 11 nouveaux cas sur treatmentType et treatmentTypeSaved dans normalizeParentFlowNode et mergeWorkflowCanvasNodes |
Détails techniques
Mécanisme treatmentTypeSaved
Le drapeau treatmentTypeSaved (booléen dans node.data) matérialise le fait que le type d'un bloc traitement a été confirmé par le serveur après une sauvegarde explicite. Il suit ce cycle de vie :
toFlowNode(chargement API) —truesiconfig.typeest non nul et différent demapper(données sauvegardées),falsesinon (nouveau bloc). Garantit la persistance du verrou après rafraîchissement.applyServerCanvasAfterSave(après sauvegarde) — passe àtruepour les nœuds traitement dont la configuration est non nulle et nonmapper. Cela verrouille le sélecteur.onResetTreatmentType(bouton « Modifier ») — repasse àfalseet réinitialisetreatmentTypeetconfig. L'utilisateur peut alors rechoisir un type.normalizeParentFlowNode(déplacement sur canevas) — préservé tel quel depuis la valeur précédente.
Guard if (v === null) return
WorkflowNodeConfigModal.vue prend un snapshot de la configuration à l'ouverture et le restaure sur annulation en émettant tous les setters de CONFIG_PROP_EMIT_MAP. Les sous-types inactifs ont un snapshot null. Sans ce guard, le dernier setter en date écrasait le nœud avec treatmentType: '<dernier_sous_type>', config: null. L'autosauvegarde persistait ensuite cet état erroné.
Unification des empreintes de sauvegarde
lastSavedFingerprint utilisait auparavant fingerprintNodesFromApi (coordonnées brutes de l'API), alors que le watcher d'autosauvegarde se basait sur getPersistedPayload() (coordonnées arrondies via Math.round). Cette différence systématique déclenchait une autosauvegarde 1 seconde après chaque sauvegarde explicite. Les deux chemins utilisent maintenant JSON.stringify(getPersistedPayload()).
