From f36833183d71bd9edcf2dc08607c30444bbe7a82 Mon Sep 17 00:00:00 2001
From: VIAUD Nathan <nathan.viaud@inria.fr>
Date: Mon, 13 Nov 2023 11:10:01 +0100
Subject: [PATCH 1/2] delete empty badges

---
 src/features/ePocFlow/ePocFlow.vue              |  8 ++++----
 src/features/ePocFlow/nodes/ActivityNode.vue    |  6 +-----
 src/features/ePocFlow/nodes/ChapterNode.vue     |  4 ++--
 src/features/ePocFlow/nodes/PageNode.vue        |  6 +-----
 .../ePocFlow/nodes/content/DraggableNode.vue    |  6 +-----
 src/features/ePocFlow/nodes/ePocNode.vue        |  4 ++--
 src/features/forms/FormPanel.vue                |  4 ++--
 src/shared/services/editor.service.ts           |  5 +++--
 src/shared/services/graph.service.ts            | 17 ++++++++++++++++-
 .../services/{ => graph}/badge.service.ts       | 16 ++++++++++++++++
 src/shared/services/graph/content.service.ts    |  4 ++--
 src/shared/services/graph/element.service.ts    |  4 ++--
 src/shared/services/graph/index.ts              |  3 ++-
 src/shared/services/graph/node.service.ts       |  6 +++---
 src/shared/services/import.service.ts           |  2 +-
 src/shared/services/index.ts                    |  2 +-
 src/shared/services/undoRedo.service.ts         |  3 ++-
 src/shared/stores/editorStore.ts                |  9 ---------
 18 files changed, 61 insertions(+), 48 deletions(-)
 rename src/shared/services/{ => graph}/badge.service.ts (93%)

diff --git a/src/features/ePocFlow/ePocFlow.vue b/src/features/ePocFlow/ePocFlow.vue
index 5da1895d..aaca2124 100644
--- a/src/features/ePocFlow/ePocFlow.vue
+++ b/src/features/ePocFlow/ePocFlow.vue
@@ -24,7 +24,7 @@ import AddChapterNode from './nodes/AddChapterNode.vue';
 import { NodeElement, SideAction } from '@/src/shared/interfaces';
 import { addPage, createPageFromContent, removeContentFromPage, graphCopy, getSelectedNodes } from '@/src/shared/services/graph';
 import { saveState, saveGivenState, getCurrentState } from '@/src/shared/services/undoRedo.service';
-import { graphService } from '@/src/shared/services';
+import { closeFormPanel, graphService } from '@/src/shared/services';
 
 const { vueFlowRef, project, updateEdge, edges, nodes, findNode, setTransform }  = useVueFlow({ id: 'main' });
 
@@ -84,7 +84,7 @@ function onEdgeClick (event: EdgeMouseEvent) {
 }
 
 function selectionStart() {
-    editorStore.closeFormPanel();
+    closeFormPanel();
 }
 
 function update(event: EdgeUpdateEvent) {
@@ -99,7 +99,7 @@ function update(event: EdgeUpdateEvent) {
 function nodeChange(event: NodeChange[]) {
     const { type } = event[0];
 
-    if(type === 'remove') editorStore.closeFormPanel();
+    if(type === 'remove') closeFormPanel();
 }
 
 function onDragOver() {
@@ -251,7 +251,7 @@ function onPaneReady() {
         @dragover.prevent="onDragOver"
         @dragenter.prevent
         @edge-click="onEdgeClick"
-        @pane-click="editorStore.closeFormPanel()"
+        @pane-click="closeFormPanel()"
         @connect="connect"
         @connect-start="onConnectStart"
         @connect-end="onConnectEnd"
diff --git a/src/features/ePocFlow/nodes/ActivityNode.vue b/src/features/ePocFlow/nodes/ActivityNode.vue
index 9351f11f..d532e342 100644
--- a/src/features/ePocFlow/nodes/ActivityNode.vue
+++ b/src/features/ePocFlow/nodes/ActivityNode.vue
@@ -3,7 +3,7 @@ import { Handle, Position, getConnectedEdges, useVueFlow, NodeProps, Emits } fro
 import { computed, ref } from 'vue';
 import { useEditorStore } from '@/src/shared/stores';
 import { getSelectedNodes } from '@/src/shared/services/graph';
-import { exitSelectNodeMode, getConnectedBadges, graphService } from '@/src/shared/services';
+import { closeFormPanel, exitSelectNodeMode, getConnectedBadges, graphService } from '@/src/shared/services';
 
 import DraggableNode from '@/src/features/ePocFlow/nodes/content/DraggableNode.vue';
 
@@ -31,10 +31,6 @@ function openPageForm(id: string, formType: string) {
     }
 }
 
-function closeFormPanel() {
-    editorStore.closeFormPanel();
-}
-
 function addHoverEffect() {
     page.value.classList.add('hover');
 }
diff --git a/src/features/ePocFlow/nodes/ChapterNode.vue b/src/features/ePocFlow/nodes/ChapterNode.vue
index f687ab2b..616ef9c4 100644
--- a/src/features/ePocFlow/nodes/ChapterNode.vue
+++ b/src/features/ePocFlow/nodes/ChapterNode.vue
@@ -4,7 +4,7 @@ import { Handle, useVueFlow, getConnectedEdges, NodeProps, Emits } from '@vue-fl
 import { Position } from '@vue-flow/core';
 import { computed } from 'vue';
 import ContentButton from '@/src/components/ContentButton.vue';
-import { exitSelectNodeMode, getConnectedBadges, graphService } from '@/src/shared/services';
+import { closeFormPanel, exitSelectNodeMode, getConnectedBadges, graphService } from '@/src/shared/services';
 
 const editorStore = useEditorStore();
 
@@ -46,7 +46,7 @@ function openForm() {
 }
 
 function mouseDown() {
-    editorStore.closeFormPanel();
+    closeFormPanel();
     
     // unselect all nodes except current node
     nodes.value.forEach((node) => node.selected = currentNode.id === node.id);
diff --git a/src/features/ePocFlow/nodes/PageNode.vue b/src/features/ePocFlow/nodes/PageNode.vue
index 11d0e02f..7ccc0001 100644
--- a/src/features/ePocFlow/nodes/PageNode.vue
+++ b/src/features/ePocFlow/nodes/PageNode.vue
@@ -4,7 +4,7 @@ import { computed, Ref, ref } from 'vue';
 import { useEditorStore } from '@/src/shared/stores';
 import { NodeElement } from '@/src/shared/interfaces';
 import { unselectAllNodes } from '@/src/shared/services/graph';
-import { exitSelectNodeMode, getConnectedBadges, graphService } from '@/src/shared/services';
+import { closeFormPanel, exitSelectNodeMode, getConnectedBadges, graphService } from '@/src/shared/services';
 import { questions } from '@/src/shared/data';
 
 import DraggableNode from './content/DraggableNode.vue';
@@ -32,10 +32,6 @@ function openPageForm(id: string, formType: string) {
     }
 }
 
-function closeFormPanel() {
-    editorStore.closeFormPanel();
-}
-
 function addHoverEffect() {
     page.value.classList.add('hover');
 }
diff --git a/src/features/ePocFlow/nodes/content/DraggableNode.vue b/src/features/ePocFlow/nodes/content/DraggableNode.vue
index ea3aba7a..f0c7ea09 100644
--- a/src/features/ePocFlow/nodes/content/DraggableNode.vue
+++ b/src/features/ePocFlow/nodes/content/DraggableNode.vue
@@ -6,7 +6,7 @@ import { NodeElement, DraggableChange } from '@/src/shared/interfaces';
 import { saveState } from '@/src/shared/services/undoRedo.service';
 import { addContentToPage, changeContentOrder, removeContentFromPage, openFormPanel } from '@/src/shared/services/graph';
 import { useVueFlow } from '@vue-flow/core';
-import { getConnectedBadges, graphService } from '@/src/shared/services';
+import { closeFormPanel, getConnectedBadges, graphService } from '@/src/shared/services';
 
 import ContentButton from '@/src/components/ContentButton.vue';
 
@@ -98,10 +98,6 @@ function dragStart(event: DragEvent, element: NodeElement, index: number) {
     editorStore.draggedElement.element = element;
 }
 
-function closeFormPanel() {
-    editorStore.closeFormPanel();
-}
-
 function onContextMenu(contentId: string) {
     graphService.openContextMenu('content', { pageId: currentNode.value.id, id: contentId });
 }
diff --git a/src/features/ePocFlow/nodes/ePocNode.vue b/src/features/ePocFlow/nodes/ePocNode.vue
index 8d49b006..d8b9fb4d 100644
--- a/src/features/ePocFlow/nodes/ePocNode.vue
+++ b/src/features/ePocFlow/nodes/ePocNode.vue
@@ -2,7 +2,7 @@
 import { useEditorStore } from '@/src/shared/stores';
 import { Emits, NodeProps, useVueFlow } from '@vue-flow/core';
 import ContentButton from '@/src/components/ContentButton.vue';
-import { exitSelectNodeMode, graphService } from '@/src/shared/services';
+import { closeFormPanel, exitSelectNodeMode, graphService } from '@/src/shared/services';
 import { computed } from 'vue';
 
 const editorStore = useEditorStore();
@@ -48,7 +48,7 @@ function onContextMenu() {
             subtitle="ePoc"
             :rotate="true"
             @click="openForm()"
-            @mousedown="editorStore.closeFormPanel()"
+            @mousedown="closeFormPanel()"
             @contextmenu="onContextMenu"
         />
     </div>
diff --git a/src/features/forms/FormPanel.vue b/src/features/forms/FormPanel.vue
index 8019194d..d33766e4 100644
--- a/src/features/forms/FormPanel.vue
+++ b/src/features/forms/FormPanel.vue
@@ -4,7 +4,7 @@ import { useEditorStore } from '../../shared/stores';
 import FormButton from './components/FormButton.vue';
 import GenericField from './components/GenericField.vue';
 import { Input, NodeElement } from '@/src/shared/interfaces';
-import { addNewBadge, deleteBadge, editorService } from '@/src/shared/services';
+import { addNewBadge, closeFormPanel, deleteBadge, editorService } from '@/src/shared/services';
 import { createToaster } from '@meforma/vue-toaster';
 import {
     confirmDelete,
@@ -107,7 +107,7 @@ function minimizeFormPanel() {
     <div class="command-buttons">
         <button v-if="isMaximized" class="btn" @click="minimizeFormPanel"><i class="icon-minimize-2"></i></button>
         <button v-else class="btn" @click="maximizeFormPanel"><i class="icon-maximize-2"></i></button>
-        <button class="btn" @click="editorStore.closeFormPanel"><i class="icon-x"></i></button>
+        <button class="btn" @click="closeFormPanel"><i class="icon-x"></i></button>
     </div>
     <div class="title">
         <div class="form-icon"><i :class="editorStore.formPanel.form.icon"></i></div>
diff --git a/src/shared/services/editor.service.ts b/src/shared/services/editor.service.ts
index b1f750bd..f96fae4c 100644
--- a/src/shared/services/editor.service.ts
+++ b/src/shared/services/editor.service.ts
@@ -3,7 +3,7 @@ import { router } from '@/src/router';
 import { useEditorStore, useGraphStore, useUndoRedoStore } from '@/src/shared/stores';
 import { ePocProject } from '@/src/shared/interfaces';
 import { createToaster } from '@meforma/vue-toaster';
-import { graphService } from '@/src/shared/services/graph.service';
+import { closeFormPanel, graphService } from '.';
 import { createGraphEpocFromData } from '@/src/shared/services/import.service';
 import { FlowExportObject, useVueFlow } from '@vue-flow/core';
 
@@ -81,7 +81,8 @@ const setup = function () {
             editorStore.loading = false;
             return;
         }
-        editorStore.closeFormPanel();
+        
+        closeFormPanel();
         editorStore.currentProject = ePocProject;
 
         parsedData.flow = changeScreenToPage(parsedData.flow);
diff --git a/src/shared/services/graph.service.ts b/src/shared/services/graph.service.ts
index faa5ac81..c6c2f62f 100644
--- a/src/shared/services/graph.service.ts
+++ b/src/shared/services/graph.service.ts
@@ -15,13 +15,14 @@ import {
 import { questions } from '@/src/shared/data';
 import { useEditorStore } from '@/src/shared/stores';
 import {
+    deleteEmptyBadges,
     findContent,
     getContentIdFromId,
     getElementByContentId,
     setNodesSelectability
 } from '@/src/shared/services/graph';
 import { Question } from '@epoc/epoc-types/src/v2';
-import { createRule, getConditions, getValidBadges } from '@/src/shared/services/badge.service';
+import { createRule, getConditions, getValidBadges } from '@/src/shared/services/graph/badge.service';
 import { Badge, NodeElement } from '@/src/shared/interfaces';
 
 declare const api: ApiInterface;
@@ -395,4 +396,18 @@ export function exportBadgesToPage(badges: Record<string, Badge>): Record<string
     }
 
     return res;
+}
+
+export function closeFormPanel() {
+    const editorStore = useEditorStore();
+    
+    if(editorStore.selectNodeMode) return;
+    
+    if(this.formPanel.form && this.formPanel.form.type === 'badge') {
+        deleteEmptyBadges();
+    }
+
+    editorStore.formPanel.form = null;
+    editorStore.openedElementId = null;
+    editorStore.openedNodeId = null;
 }
\ No newline at end of file
diff --git a/src/shared/services/badge.service.ts b/src/shared/services/graph/badge.service.ts
similarity index 93%
rename from src/shared/services/badge.service.ts
rename to src/shared/services/graph/badge.service.ts
index cd7cd745..89b5b33c 100644
--- a/src/shared/services/badge.service.ts
+++ b/src/shared/services/graph/badge.service.ts
@@ -215,6 +215,22 @@ export function isBadgeValid(badge): boolean {
     return badge.rule.and.length > 0;
 }
 
+export function isBadgeEmpty(badge): boolean {
+    return badge.title === ''
+        && badge.icon === ''
+        && badge.description === ''
+        && badge.rule.and.length === 0;
+}
+
+export function deleteEmptyBadges() {
+    const epocNode = findNode('1');
+    const badges = epocNode.data.formValues.badges;
+
+    for(const badgeId in badges) {
+        if(isBadgeEmpty(badges[badgeId])) delete badges[badgeId];
+    }
+}
+
 export function getValidBadges(){
     const epocNode = findNode('1');
     const badges = epocNode.data.formValues.badges;
diff --git a/src/shared/services/graph/content.service.ts b/src/shared/services/graph/content.service.ts
index a90ad0c6..6f569056 100644
--- a/src/shared/services/graph/content.service.ts
+++ b/src/shared/services/graph/content.service.ts
@@ -2,7 +2,7 @@ import { useVueFlow } from '@vue-flow/core';
 import { useEditorStore } from '../../stores';
 import {NodeElement, SideAction} from '../../interfaces';
 import { deleteNode } from './node.service';
-import {generateContentId, generateId} from '../graph.service';
+import { closeFormPanel, generateContentId, generateId } from '../graph.service';
 import * as forms from '@/src/shared/data/forms';
 import { deleteConnectedConditions } from '@/src/shared/services';
 
@@ -47,7 +47,7 @@ export function changeContentOrder(startIndex: number, finalIndex: number, pageI
 
 //? The parameter pageMoved is used when openedParentId is not usable
 export function removeContentFromPage(index: number, pageId: string, pageMoved?: boolean): void {
-    editorStore.closeFormPanel();
+    closeFormPanel();
 
     const pageNode = findNode(pageId);
     pageNode.data.elements.splice(index, 1);
diff --git a/src/shared/services/graph/element.service.ts b/src/shared/services/graph/element.service.ts
index 3ab86de8..ba1c5af2 100644
--- a/src/shared/services/graph/element.service.ts
+++ b/src/shared/services/graph/element.service.ts
@@ -1,7 +1,7 @@
 import { useVueFlow, Node, MarkerType, Edge } from '@vue-flow/core';
 import { deleteContent, deleteNode, getContentByContentId } from './';
 import { useEditorStore } from '@/src/shared/stores';
-import { exitSelectNodeMode, generateId } from '../graph.service';
+import { closeFormPanel, exitSelectNodeMode, generateId } from '../graph.service';
 import { ElementType, NodeElement } from '../../interfaces';
 
 const { nodes, findNode, addEdges } = useVueFlow({ id: 'main' });
@@ -17,7 +17,7 @@ export function deleteElement(id: string, pageId?: string): void {
     if(pageId || !pageToDelete) deleteContent(pageId ?? editorStore.openedNodeId, id);
     else deleteNode(id);
 
-    editorStore.closeFormPanel();
+    closeFormPanel();
 }
 
 export function createEdge(sourceId: string, targetId: string): void {
diff --git a/src/shared/services/graph/index.ts b/src/shared/services/graph/index.ts
index b735ddb1..88587507 100644
--- a/src/shared/services/graph/index.ts
+++ b/src/shared/services/graph/index.ts
@@ -1,4 +1,5 @@
 export * from './content.service';
 export * from './node.service';
 export * from './element.service';
-export * from './copyPaste.service';
\ No newline at end of file
+export * from './copyPaste.service';
+export * from './badge.service';
\ No newline at end of file
diff --git a/src/shared/services/graph/node.service.ts b/src/shared/services/graph/node.service.ts
index c358a003..3a70fad5 100644
--- a/src/shared/services/graph/node.service.ts
+++ b/src/shared/services/graph/node.service.ts
@@ -5,7 +5,7 @@ import { useVueFlow, Node, getConnectedEdges } from '@vue-flow/core';
 import { NodeElement, SideAction } from '../../interfaces';
 import { nextTick, toRaw, watch } from 'vue';
 
-import { deleteConnectedConditions } from '@/src/shared/services';
+import { closeFormPanel, deleteConnectedConditions } from '@/src/shared/services';
 import { addContentToPage } from './content.service';
 import { generateContentId, generateId, graphService } from '../graph.service';
 import { deleteElement, deleteSelection, createEdge } from '.';
@@ -314,7 +314,7 @@ export function duplicatePage(pageId?: string): void {
     };
 
     addNodes([newPage]);
-    editorStore.closeFormPanel();
+    closeFormPanel();
 }
 
 export function updateNextChapter(chapterId: string): void {
@@ -333,7 +333,7 @@ export function transformActivityToPage(): void {
     pageNode.type = 'page';
     pageNode.data.formType = 'page';
     delete pageNode.data.formValues.summary;
-    editorStore.closeFormPanel();
+    closeFormPanel();
     editorStore.openFormPanel(pageNode.id, pageNode.data.formType);
 }
 
diff --git a/src/shared/services/import.service.ts b/src/shared/services/import.service.ts
index aca76156..64cf68e9 100644
--- a/src/shared/services/import.service.ts
+++ b/src/shared/services/import.service.ts
@@ -9,7 +9,7 @@ import {
 import { generateId } from '@/src/shared/services/graph.service';
 import { questions, standardPages } from '@/src/shared/data';
 import { Assessment, ChoiceCondition, SimpleQuestion } from '@epoc/epoc-types/src/v1';
-import { createRule, getConditions } from '@/src/shared/services/badge.service';
+import { createRule, getConditions } from '@/src/shared/services/graph/badge.service';
 import { Node } from '@vue-flow/core';
 import { Badge } from '@/src/shared/interfaces';
 
diff --git a/src/shared/services/index.ts b/src/shared/services/index.ts
index c76024e2..56a35ba2 100644
--- a/src/shared/services/index.ts
+++ b/src/shared/services/index.ts
@@ -1,3 +1,3 @@
 export * from './editor.service';
 export * from './graph.service';
-export * from './badge.service';
\ No newline at end of file
+export * from './graph';
\ No newline at end of file
diff --git a/src/shared/services/undoRedo.service.ts b/src/shared/services/undoRedo.service.ts
index 46378bc6..6763388b 100644
--- a/src/shared/services/undoRedo.service.ts
+++ b/src/shared/services/undoRedo.service.ts
@@ -2,6 +2,7 @@ import { useVueFlow } from '@vue-flow/core';
 import { useUndoRedoStore, useGraphStore, useEditorStore } from '../stores';
 import { ePocState } from '../interfaces';
 import { ApiInterface } from '../interfaces/api.interface';
+import { closeFormPanel } from '.';
 
 const { toObject }  = useVueFlow({ id: 'main' });
 
@@ -65,7 +66,7 @@ export function revertToState(state: string): string {
     const graphStore = useGraphStore();
     const editorStore = useEditorStore();
     
-    editorStore.closeFormPanel();
+    closeFormPanel();
 
     const { flow, form } = JSON.parse(state);
 
diff --git a/src/shared/stores/editorStore.ts b/src/shared/stores/editorStore.ts
index 66cddb5e..cb33366d 100644
--- a/src/shared/stores/editorStore.ts
+++ b/src/shared/stores/editorStore.ts
@@ -197,15 +197,6 @@ export const useEditorStore = defineStore('editor', {
             });
         },
 
-        closeFormPanel(): void {
-            //? prevent closing the form panel when selecting a node
-            if(this.selectNodeMode) return;
-
-            this.formPanel.form = null;
-            this.openedElementId = null;
-            this.openedNodeId = null;
-        },
-
         closeValidationModal(): void {
             this.validationModal = false;
         },
-- 
GitLab


From e640643e0e863e591ffb8b1f76991da25cdfa263 Mon Sep 17 00:00:00 2001
From: VIAUD Nathan <nathan.viaud@inria.fr>
Date: Mon, 13 Nov 2023 11:37:29 +0100
Subject: [PATCH 2/2] fix

---
 src/shared/services/graph.service.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/shared/services/graph.service.ts b/src/shared/services/graph.service.ts
index c6c2f62f..dcbe4f64 100644
--- a/src/shared/services/graph.service.ts
+++ b/src/shared/services/graph.service.ts
@@ -403,7 +403,7 @@ export function closeFormPanel() {
     
     if(editorStore.selectNodeMode) return;
     
-    if(this.formPanel.form && this.formPanel.form.type === 'badge') {
+    if(editorStore.formPanel.form && editorStore.formPanel.form.type === 'badge') {
         deleteEmptyBadges();
     }
 
-- 
GitLab