Skip to content

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

  1. 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 valaient null. Le dernier setter (activeEmpilerConfig) écrasait le nœud avec treatmentType: 'empiler', config: null, puis l'autosauvegarde persistait cet état incorrect.

  2. 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:edges qui mélangeaient les nouvelles données serveur avec l'ancien état local.

  3. Décalage des empreintes de sauvegardelastSavedFingerprint utilisait le format fingerprintNodesFromApi (coordonnées brutes), tandis que le watcher d'autosauvegarde utilisait getPersistedPayload() (coordonnées arrondies). La différence déclenchait une autosauvegarde immédiate après chaque sauvegarde explicite.

  4. normalizeParentFlowNode ne préservait pas treatmentType — lors du déplacement d'un bloc, la fusion des mises à jour partielles de Vue Flow n'incluait pas treatmentType ni treatmentTypeSaved, les effaçant du nœud.

  5. toFlowNode inférait treatmentTypeSaved: true depuis 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èreCouvert par
1La configuration d'un bloc traitement est préservée après déplacement sur le canevasworkflow-canvas.sync.jsnormalizeParentFlowNode préserve treatmentType et treatmentTypeSaved
2La colonne source est sélectionnable dans les blocs Extraire des caractères et Fractionner la colonneWorkflowExtraireCaracteresPanel.vue, WorkflowDecouperColonnePanel.vueq-select désactivé pendant le chargement
3Le type de traitement ne revient pas à la valeur par défaut après sauvegarde puis réouvertureGuard if (v === null) return dans tous les setters de config ; fixe handleCancel
4Le sélecteur de type est verrouillé uniquement après la première sauvegarde explicitetreatmentTypeSaved positionné à true par applyServerCanvasAfterSave (après save) et restauré depuis config.type par toFlowNode (après rafraîchissement)
5La configuration n'est pas perdue après rafraîchissement de la pageMême correction que critère 3 ; suppression de la boucle d'autosauvegarde infinie via unification des empreintes

Fichiers modifiés

Frontend

FichierRôle
front/src/composables/useActiveTraitementSubtypeConfig.jsGuard if (v === null) return au début du setter — empêche un snapshot null d'écraser la configuration active
front/src/composables/useWorkflowEditorFlowSync.jsParamètre isApplyingServerState ajouté — onFlowNodesUpdate et onFlowEdgesUpdate retournent tôt quand l'état serveur est en cours d'application
front/src/pages/admin/workflows/WorkflowEditorPage.vueapplyServerCanvasAfterSave 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.jstoFlowNode 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.jsnormalizeParentFlowNode préserve treatmentType et treatmentTypeSaved lors des fusions partielles Vue Flow
front/src/components/workflows/WorkflowNodeConfigModal.vueProp isTreatmentTypeLocked ; emit reset-treatment-type ; sélecteur de type désactivé avec bouton « Modifier » et dialogue de confirmation
front/src/components/workflows/WorkflowExtraireCaracteresPanel.vueq-select Colonne source désactivé pendant le chargement des options
front/src/components/workflows/WorkflowDecouperColonnePanel.vueMême correction que WorkflowExtraireCaracteresPanel.vue

Tests

FichierTypeCe qui est couvert
front/src/__tests__/composables/useActiveTraitementSubtypeConfig.spec.jsUnitGuard null dans le setter ; getter selon treatmentType et config.type ; non-modification de nœuds non-traitement
front/src/__tests__/composables/useWorkflowEditorFlowSync.spec.jsUnitBlocage 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.jsUnit11 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)true si config.type est non nul et différent de mapper (données sauvegardées), false sinon (nouveau bloc). Garantit la persistance du verrou après rafraîchissement.
  • applyServerCanvasAfterSave (après sauvegarde) — passe à true pour les nœuds traitement dont la configuration est non nulle et non mapper. Cela verrouille le sélecteur.
  • onResetTreatmentType (bouton « Modifier ») — repasse à false et réinitialise treatmentType et config. 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()).