Browse Source

feat(editor): auto-save workflow into storage

Ahmad Kholid 3 years ago
parent
commit
329c38d132

+ 1 - 1
src/assets/css/drawflow.css

@@ -31,7 +31,7 @@
 .drawflow .drawflow-node {
   position: absolute;
   background: white;
-  min-width: 100px;
+  min-width: 150px;
   min-height: 40px;
   z-index: 2;
   cursor: move;

+ 5 - 3
src/components/block/BlockBase.vue

@@ -1,5 +1,5 @@
 <template>
-  <div ref="rootRef" class="group relative">
+  <div ref="rootRef" class="group relative overflow-x-hiddenx">
     <div
       class="
         z-10
@@ -16,10 +16,12 @@
         :class="categories[state.blockData.category]?.color"
         class="inline-block p-2 mr-2 rounded-lg bg-green-200"
       >
-        <v-remixicon :path="icons[state.blockData.icon]" />
+        <v-remixicon
+          :path="icons[state.blockData.icon] || icons.riGlobalLine"
+        />
       </span>
       <div style="max-width: 220px">
-        <p class="font-semibold leading-none">
+        <p class="font-semibold leading-none whitespace-nowrap">
           {{ state.blockData.name }}
         </p>
         <p class="text-gray-600 text-overflow leading-tight">

+ 41 - 23
src/components/newtab/workflow/WorkflowBuilder.vue

@@ -1,12 +1,11 @@
 <template>
-  <div class="relative">
-    <div
-      id="drawflow"
-      class="h-full w-full"
-      @drop="dropHandler"
-      @dragover.prevent
-    ></div>
-    <div class="absolute m-4 bottom-0 left-0">
+  <div
+    id="drawflow"
+    class="parent-drawflow relative"
+    @drop="dropHandler"
+    @dragover.prevent
+  >
+    <div class="absolute p-4 bottom-0 left-0">
       <button class="p-2 rounded-lg bg-white mr-2" @click="editor.zoom_reset()">
         <v-remixicon name="riFullscreenLine" />
       </button>
@@ -23,18 +22,24 @@
   </div>
 </template>
 <script>
-import { onMounted, shallowRef } from 'vue';
+import { onMounted, onUnmounted, shallowRef } from 'vue';
 import drawflow from '@/lib/drawflow';
 
 export default {
-  setup() {
+  props: {
+    data: {
+      type: Object,
+      default: null,
+    },
+  },
+  emits: ['addBlock', 'deleteBlock', 'saveWorkflow'],
+  setup(props, { emit }) {
     const editor = shallowRef(null);
 
     function dropHandler({ dataTransfer, clientX, clientY }) {
       const block = JSON.parse(dataTransfer.getData('block') || null);
 
       if (!block) return;
-      console.log(block);
 
       const xPosition =
         clientX *
@@ -63,24 +68,37 @@ export default {
         'vue'
       );
     }
+    function saveWorkflow() {
+      emit('saveWorkflow', editor.value.export());
+    }
 
     onMounted(() => {
+      window.addEventListener('beforeunload', saveWorkflow);
+      console.log(props, props.data.drawflow.Home.data);
       const element = document.querySelector('#drawflow');
 
       editor.value = drawflow(element);
-      console.log(editor.value);
       editor.value.start();
-      editor.value.addNode(
-        'trigger',
-        0,
-        1,
-        50,
-        300,
-        'trigger',
-        {},
-        'BlockBase',
-        'vue'
-      );
+
+      if (props.data) {
+        editor.value.import(props.data);
+      } else {
+        editor.value.addNode(
+          'trigger',
+          0,
+          1,
+          50,
+          300,
+          'trigger',
+          {},
+          'BlockBase',
+          'vue'
+        );
+      }
+    });
+    onUnmounted(() => {
+      saveWorkflow();
+      window.removeEventListener('beforeunload', saveWorkflow);
     });
 
     return {

+ 9 - 2
src/components/newtab/workflow/WorkflowDetailsCard.vue

@@ -21,10 +21,17 @@
         {{ workflow.name }}
       </p>
     </div>
-    <ui-tabs v-model="state.activeTab" fill class="mx-4 mb-4">
+    <div class="flex px-4 mt-2">
+      <ui-button variant="accent" class="flex-1 mr-4"> Execute </ui-button>
+      <ui-button icon class="text-red-500">
+        <v-remixicon name="riDeleteBin7Line" />
+      </ui-button>
+    </div>
+    <hr class="m-4 border-gray-100" />
+    <!-- <ui-tabs v-model="state.activeTab" fill class="mx-4 mb-4">
       <ui-tab value="blocks">Blocks</ui-tab>
       <ui-tab value="data-schema">Data Columns</ui-tab>
-    </ui-tabs>
+    </ui-tabs> -->
     <!-- <div class="px-4 mb-2">
       <ui-input prepend-icon="riSearch2Line" class="w-full" placeholder="Search..." />
     </div> -->

+ 0 - 3
src/models/task.js

@@ -7,12 +7,9 @@ class Task extends Model {
   static fields() {
     return {
       id: this.uid(() => nanoid()),
-      name: this.string(''),
       description: this.string(''),
       type: this.string(''),
-      createdAt: this.number(),
       data: this.attr(null),
-      order: this.number(0),
       workflowId: this.attr(null),
     };
   }

+ 1 - 0
src/models/workflow.js

@@ -13,6 +13,7 @@ class Workflow extends Model {
       name: this.string(''),
       icon: this.string('riGlobalLine'),
       data: this.attr(null),
+      drawflow: this.attr(null),
       dataSchema: this.attr([]),
       lastRunAt: this.number(),
       createdAt: this.number(),

+ 27 - 5
src/newtab/pages/workflows/[id].vue

@@ -1,12 +1,19 @@
 <template>
   <div class="flex h-screen">
     <workflow-details-card :workflow="workflow" />
-    <workflow-builder class="flex-1" />
+    <workflow-builder
+      class="flex-1"
+      :data="workflow.drawflow"
+      @addBlock="addBlock"
+      @deleteBlock="deleteBlock"
+      @saveWorkflow="saveWorkflow"
+    />
   </div>
 </template>
 <script setup>
 import { computed, onMounted } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
+import Task from '@/models/task';
 import Workflow from '@/models/workflow';
 import WorkflowBuilder from '@/components/newtab/workflow/WorkflowBuilder.vue';
 import WorkflowDetailsCard from '@/components/newtab/workflow/WorkflowDetailsCard.vue';
@@ -14,12 +21,27 @@ import WorkflowDetailsCard from '@/components/newtab/workflow/WorkflowDetailsCar
 const route = useRoute();
 const router = useRouter();
 
-const workflow = computed(() => Workflow.find(route.params.id) || {});
+const workflowId = route.params.id;
+const workflow = computed(() => Workflow.find(workflowId) || {});
+
+function addBlock(data) {
+  Task.insert({
+    data: { ...data, workflowId },
+  });
+}
+function deleteBlock(id) {
+  Task.delete(id);
+}
+function saveWorkflow(data) {
+  console.log('saved', workflowId, data);
+  Workflow.update({
+    where: workflowId,
+    data: { drawflow: data },
+  });
+}
 
 onMounted(() => {
-  const isWorkflowExists = Workflow.query()
-    .where('id', route.params.id)
-    .exists();
+  const isWorkflowExists = Workflow.query().where('id', workflowId).exists();
 
   if (!isWorkflowExists) {
     router.push('/workflows');

+ 6 - 6
src/utils/shared.js

@@ -42,7 +42,7 @@ export const tasks = {
   'export-data': {
     name: 'Export data',
     icon: 'riDownloadLine',
-    component: 'task-',
+    component: 'BlockBase',
     category: 'general',
     inputs: 1,
     outputs: 1,
@@ -62,7 +62,7 @@ export const tasks = {
   'get-attribute': {
     name: 'Get attribute',
     icon: 'riBracketsLine',
-    component: 'task-',
+    component: 'BlockBase',
     category: 'interaction',
     inputs: 1,
     outputs: 1,
@@ -72,7 +72,7 @@ export const tasks = {
   'open-website': {
     name: 'Open website',
     icon: 'riGlobalLine',
-    component: 'task-',
+    component: 'BlockBase',
     category: 'general',
     inputs: 1,
     outputs: 1,
@@ -82,7 +82,7 @@ export const tasks = {
   'text-input': {
     name: 'Text input',
     icon: 'riInputCursorMove',
-    component: 'task-',
+    component: 'BlockBase',
     category: 'interaction',
     inputs: 1,
     outputs: 1,
@@ -92,7 +92,7 @@ export const tasks = {
   'repeat-task': {
     name: 'Repeat tasks',
     icon: 'riRepeat2Line',
-    component: 'task-',
+    component: 'BlockBase',
     category: 'general',
     inputs: 1,
     outputs: 1,
@@ -102,7 +102,7 @@ export const tasks = {
   'trigger-element-events': {
     name: 'Trigger element events',
     icon: 'riLightbulbFlashLine',
-    component: 'task-',
+    component: 'BlockBase',
     category: 'interaction',
     inputs: 1,
     outputs: 1,