Appearance
BDD-151 — Corrections UI : bouton vider le canvas, verrouillage mapper et modale de modification
Contexte
Plusieurs anomalies visuelles et comportementales ont été identifiées dans l'éditeur de workflow :
- Le bouton « Vider le canvas » apparaissait toujours surligné en gris, comme s'il était sélectionné en permanence.
- Le type de traitement Mapper n'était jamais verrouillé après sauvegarde, contrairement à tous les autres types.
- La modale de confirmation de changement de type affichait un libellé trop long (« Réinitialiser et modifier »).
- Le renommage de colonnes dans le Mapper devenait impossible via la touche Suppr (l'ancien nom se restaurait à chaque suppression).
- Lorsque le bloc amont du Mapper produisait 0 ligne, l'exécution échouait avec un message trompeur (« entrée amont indisponible »).
- Les deux boutons d'export (Markdown / PDF) étaient dupliqués visuellement sans hiérarchie claire.
Critères d'acceptance
| # | Critère | Couvert par |
|---|---|---|
| 1 | Le bouton « Vider le canvas » n'est plus sélectionné par défaut | CSS — background déplacé sur :hover |
| 2 | Le type Mapper est verrouillé après la première sauvegarde, comme les autres types | Sentinel _savedByUser + markActiveMapperAsSaved() |
| 3 | La modale de modification affiche uniquement « Annuler » et « Oui » | WorkflowNodeConfigModal — label remplacé |
Corrections supplémentaires incluses
Alignement des éléments du menu débordement
Les trois éléments du menu « ··· » (Exporter la doc, Vider le canvas) n'étaient pas alignés : le bouton destructif utilisait un padding (10px 12px) et un min-width d'avatar (40px) différents des valeurs par défaut Quasar (8px 16px et 56px).
Correctif : alignement sur les valeurs Quasar par défaut dans .toolbar-overflow-destructive-tile.
Fusion des boutons d'export
Les boutons « Exporter la doc (Markdown) » et « Exporter la doc (PDF) » ont été fusionnés en un seul bouton « Exporter la doc ». Un clic ouvre une boîte de dialogue permettant de choisir le format. Les fonctions exportWorkflowDocumentationMarkdown() et exportWorkflowDocumentationPdf() sont inchangées.
Correction du renommage de colonnes dans le Mapper
Dans WorkflowMapperPanel, l'event @update:model-value utilisait $event || row.source : dès que l'utilisateur vidait le champ, la valeur revenait à l'ancien nom car une chaîne vide est falsy. L'utilisateur était bloqué dans une boucle infinie de suppression.
Correctif : @update:model-value passe $event directement (la chaîne vide est autorisée pendant la saisie). Un @blur restaure l'ancien nom uniquement si le champ est laissé vide à la perte de focus.
Correction — Mapper sur entrée amont à 0 ligne (staging)
Dans workflow-run-staging-execution.service.ts, la vérification d'accessibilité de l'entrée amont utilisait countRows(). countRows retourne 0 à la fois quand la table de staging n'existe pas et quand elle existe mais est vide. La condition !countRows() lançait donc une BadRequestException (« Bloc Traitement : entrée amont indisponible ») dès que le bloc amont produisait 0 ligne, même si la configuration était correcte.
Correctif : ajout de la méthode nodeTableExists(runUuid, nodeUuid) dans WorkflowRunStagingService (vérifie uniquement l'existence de la table, sans compter les lignes). La condition utilise désormais nodeTableExists : elle lève une erreur uniquement si la table est absente (bloc amont non exécuté ou en échec), et laisse passer le cas 0 ligne.
Architecture
Verrouillage du type Mapper après première sauvegarde
Le verrouillage des types de traitement repose sur le flag treatmentTypeSaved dans le state Vue Flow. Pour les types non-Mapper, ce flag est activé dès que config.type est non nul dans la réponse API — ce qui fonctionne car ces types n'ont pas de config par défaut avant que l'utilisateur sauvegarde.
Le Mapper génère systématiquement une config par défaut { type: 'mapper', columns: [] } dès la première persistance automatique (autosave canvas), ce qui rendait config.type non nul dès le début, avant toute action utilisateur. Il était donc impossible de détecter « l'utilisateur a sauvegardé » par ce seul flag.
Solution — sentinel _savedByUser
Un champ _savedByUser: true est injecté dans la config Mapper uniquement lors d'une sauvegarde explicite (clic sur « Sauvegarder » dans la modale), via la fonction markActiveMapperAsSaved() appelée dans handleModalSave.
Ce champ est persisté dans l'API avec le reste de la config JSONB, et ignoré par le backend à l'exécution. Lors du rechargement de la page ou de la re-synchronisation serveur, buildCanvasNodeFromApi et applyServerCanvasAfterSave vérifient config._savedByUser === true pour activer treatmentTypeSaved.
| Scénario | Résultat |
|---|---|
| Nouveau nœud Mapper, autosave déclenché | _savedByUser absent → non verrouillé |
| Utilisateur clique « Sauvegarder » | _savedByUser: true injecté → verrouillé immédiatement |
| Rechargement page après sauvegarde | API retourne _savedByUser: true → verrouillé |
| Utilisateur clique « Modifier le type » | onResetTreatmentType remet config: null → sentinel effacé → déverrouillé |
Fichiers modifiés
| Fichier | Modification |
|---|---|
front/src/pages/admin/workflows/WorkflowEditorPage.vue | CSS bouton destructif, fusion export, verrouillage Mapper (markActiveMapperAsSaved, _savedByUser), handleModalSave |
front/src/pages/admin/workflows/workflow-canvas.model.js | buildCanvasNodeFromApi — condition treatmentTypeSaved étendue au Mapper via _savedByUser |
front/src/components/workflows/WorkflowNodeConfigModal.vue | Label « Oui » dans la modale de confirmation de changement de type |
front/src/components/workflows/WorkflowMapperPanel.vue | Correctif @update:model-value / @blur pour le renommage de colonnes |
back/src/api/workflows/staging/workflow-run-staging.service.ts | Ajout de nodeTableExists() |
back/src/api/workflows/staging/workflow-run-staging-execution.service.ts | Remplacement de countRows par nodeTableExists pour la vérification amont |
