Browse Source

feat: add import and export workflow

Ahmad Kholid 3 years ago
parent
commit
2585b0ec3d

+ 1 - 1
src/assets/svg/logo.svg

@@ -1,5 +1,5 @@
 <svg width="600" height="600" viewBox="0 0 600 600" fill="none" xmlns="http://www.w3.org/2000/svg">
 <rect x="53" y="58" width="452" height="452" rx="80" fill="white" stroke="#181818" stroke-width="30" stroke-linejoin="round"/>
 <rect x="95" y="90" width="452" height="452" rx="80" fill="#18181B"/>
-<path d="M293.667 220.666C304.723 198.554 336.277 198.554 347.333 220.666L432.308 390.616C444.814 415.628 419.067 443.212 392.87 433.428C363.012 422.277 332.936 412.348 320.5 412.348C308.064 412.348 277.988 422.277 248.13 433.428C221.933 443.212 196.186 415.628 208.692 390.616L293.667 220.666Z" fill="#FEF08A"/>
+<path d="M293.667 220.666C304.723 198.554 336.277 198.554 347.333 220.666L432.308 390.616C444.814 415.628 419.067 443.212 392.87 433.428C363.012 422.277 332.936 412.348 320.5 412.348C308.064 412.348 277.988 422.277 248.13 433.428C221.933 443.212 196.186 415.628 208.692 390.616L293.667 220.666Z" fill="white"/>
 </svg>

+ 1 - 0
src/background/index.js

@@ -67,6 +67,7 @@ browser.runtime.onInstalled.addListener((details) => {
         logs: [],
         workflows: [],
         workflowState: [],
+        isFirstTime: true,
         visitWebTriggers: [],
       })
       .then(() => {

+ 12 - 15
src/components/newtab/workflow/WorkflowCard.vue

@@ -14,22 +14,16 @@
             <v-remixicon name="riMoreLine" />
           </button>
         </template>
-        <ui-list class="w-40 space-y-1">
+        <ui-list class="w-36 space-y-1">
           <ui-list-item
+            v-for="item in menu"
+            :key="item.name"
             v-close-popover
             class="cursor-pointer"
-            @click="$emit('rename', workflow)"
+            @click="$emit(item.name, workflow)"
           >
-            <v-remixicon name="riPencilLine" class="mr-2 -ml-1" />
-            <span>Rename</span>
-          </ui-list-item>
-          <ui-list-item
-            v-close-popover
-            class="cursor-pointer"
-            @click="$emit('delete', workflow)"
-          >
-            <v-remixicon name="riDeleteBin7Line" class="mr-2 -ml-1" />
-            <span>Delete</span>
+            <v-remixicon :name="item.icon" class="mr-2 -ml-1" />
+            <span class="capitalize">{{ item.name }}</span>
           </ui-list-item>
         </ui-list>
       </ui-popover>
@@ -45,8 +39,6 @@
   </ui-card>
 </template>
 <script setup>
-/* eslint-disable no-undef */
-
 import dayjs from '@/lib/dayjs';
 
 const props = defineProps({
@@ -59,7 +51,12 @@ const props = defineProps({
     default: true,
   },
 });
-defineEmits(['delete', 'rename', 'execute']);
+defineEmits(['delete', 'export', 'rename', 'execute']);
 
 const formatDate = () => dayjs(props.workflow.createdAt).fromNow();
+const menu = [
+  { name: 'export', icon: 'riDownloadLine' },
+  { name: 'rename', icon: 'riPencilLine' },
+  { name: 'delete', icon: 'riDeleteBin7Line' },
+];
 </script>

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

@@ -82,6 +82,14 @@
           <v-remixicon name="riPencilLine" class="mr-2 -ml-1" />
           <span>Rename</span>
         </ui-list-item>
+        <ui-list-item
+          v-close-popover
+          class="cursor-pointer"
+          @click="$emit('export', workflow)"
+        >
+          <v-remixicon name="riDownloadLine" class="mr-2 -ml-1" />
+          <span>Export</span>
+        </ui-list-item>
         <ui-list-item
           v-close-popover
           class="cursor-pointer"
@@ -162,6 +170,7 @@ defineProps({
 });
 defineEmits([
   'save',
+  'export',
   'update',
   'rename',
   'delete',

+ 2 - 0
src/lib/v-remixicon.js

@@ -46,6 +46,7 @@ import {
   riFocusLine,
   riCursorLine,
   riDownloadLine,
+  riUploadLine,
   riCommandLine,
   riParagraph,
   riImageLine,
@@ -107,6 +108,7 @@ export const icons = {
   riFocusLine,
   riCursorLine,
   riDownloadLine,
+  riUploadLine,
   riCommandLine,
   riParagraph,
   riImageLine,

+ 6 - 0
src/newtab/pages/Workflows.vue

@@ -24,6 +24,10 @@
           </option>
         </ui-select>
       </div>
+      <ui-button @click="importWorkflow">
+        <v-remixicon name="riUploadLine" class="mr-2 -ml-1" />
+        Import workflow
+      </ui-button>
       <ui-button variant="accent" @click="newWorkflow">
         New workflow
       </ui-button>
@@ -44,6 +48,7 @@
         v-for="workflow in workflows"
         :key="workflow.id"
         v-bind="{ workflow }"
+        @export="exportWorkflow"
         @delete="deleteWorkflow"
         @rename="renameWorkflow"
         @execute="executeWorkflow"
@@ -55,6 +60,7 @@
 import { computed, shallowReactive } from 'vue';
 import { useDialog } from '@/composable/dialog';
 import { sendMessage } from '@/utils/message';
+import { exportWorkflow, importWorkflow } from '@/utils/workflow-data';
 import WorkflowCard from '@/components/newtab/workflow/WorkflowCard.vue';
 import Workflow from '@/models/workflow';
 

+ 2 - 0
src/newtab/pages/workflows/[id].vue

@@ -14,6 +14,7 @@
         :workflow="workflow"
         :data-changed="state.isDataChanged"
         @save="saveWorkflow"
+        @export="exportWorkflow"
         @execute="executeWorkflow"
         @update="updateWorkflow"
         @showDataColumns="state.showDataColumnsModal = true"
@@ -128,6 +129,7 @@ import emitter from 'tiny-emitter/instance';
 import { sendMessage } from '@/utils/message';
 import { debounce } from '@/utils/helper';
 import { useDialog } from '@/composable/dialog';
+import { exportWorkflow } from '@/utils/workflow-data';
 import Log from '@/models/log';
 import Workflow from '@/models/workflow';
 import WorkflowBuilder from '@/components/newtab/workflow/WorkflowBuilder.vue';

+ 6 - 2
src/store/index.js

@@ -32,14 +32,18 @@ const store = createStore({
             data: data[entity],
           });
         });
-        const isFirstTime =
-          (await browser.storage.local.get('isFirstTime')?.isFirstTime) ?? true;
+        const { isFirstTime } = await browser.storage.local.get('isFirstTime');
 
+        console.log(
+          await browser.storage.local.get('isFirstTime'),
+          isFirstTime
+        );
         if (isFirstTime) {
           await dispatch('entities/insert', {
             entity: 'workflows',
             data: firstWorkflows,
           });
+          await browser.storage.local.set({ isFirstTime: false });
         }
 
         return await Promise.allSettled(promises);

File diff suppressed because it is too large
+ 2 - 0
src/utils/shared.js


+ 54 - 0
src/utils/workflow-data.js

@@ -0,0 +1,54 @@
+import { fileSaver } from './helper';
+import Workflow from '@/models/workflow';
+
+export function importWorkflow() {
+  const input = document.createElement('input');
+  input.type = 'file';
+  input.accept = 'application/json';
+
+  input.onchange = (event) => {
+    const file = event.target.files[0];
+
+    if (!file || file.type !== 'application/json') {
+      alert('Invalid file');
+      return;
+    }
+
+    const reader = new FileReader();
+
+    reader.onload = ({ target }) => {
+      try {
+        const workflow = JSON.parse(target.result);
+
+        Workflow.insert({ data: workflow });
+      } catch (error) {
+        console.error(error);
+      }
+    };
+
+    reader.readAsText(file);
+  };
+
+  input.click();
+}
+
+export function exportWorkflow(workflow) {
+  const keys = ['dataColumns', 'drawflow', 'icon', 'name', 'settings'];
+  const content = {};
+
+  keys.forEach((key) => {
+    content[key] = workflow[key];
+  });
+
+  const blob = new Blob([JSON.stringify(content)], {
+    type: 'application/json',
+  });
+  const url = URL.createObjectURL(blob);
+
+  fileSaver(`${workflow.name}.json`, url);
+}
+
+export default {
+  export: exportWorkflow,
+  import: importWorkflow,
+};

Some files were not shown because too many files changed in this diff