Quellcode durchsuchen

feat(newtab): add export log data

Ahmad Kholid vor 3 Jahren
Ursprung
Commit
b34fd9d291

+ 2 - 0
package.json

@@ -28,10 +28,12 @@
     "drawflow": "^0.0.49",
     "nanoid": "3.1.28",
     "papaparse": "^5.3.1",
+    "prismjs": "^1.25.0",
     "tiny-emitter": "^2.1.0",
     "tippy.js": "^6.3.1",
     "v-remixicon": "^0.1.1",
     "vue": "3.2.19",
+    "vue-prism-editor": "^2.0.0-alpha.2",
     "vue-router": "^4.0.11",
     "vuedraggable": "^4.1.0",
     "vuex": "^4.0.2",

+ 13 - 9
src/background/blocks-handler.js

@@ -178,18 +178,22 @@ export function interactionHandler(block) {
       delay: block.name === 'link' ? 5000 : 0,
       callback: (data) => {
         if (objectHasKey(block.data, 'dataColumn')) {
-          const { name, type } = Object.values(this.workflow.dataColumns).find(
+          const column = Object.values(this.workflow.dataColumns).find(
             (item) => item.name === block.data.dataColumn
-          ) || { name: 'column', type: 'text' };
+          );
 
-          if (!objectHasKey(this.data, name)) this.data[name] = [];
+          if (column) {
+            const { name, type } = column;
 
-          if (Array.isArray(data)) {
-            data.forEach((item) => {
-              this.data[name].push(convertData(item, type));
-            });
-          } else {
-            this.data[name].push(convertData(data, type));
+            if (!objectHasKey(this.data, name)) this.data[name] = [];
+
+            if (Array.isArray(data)) {
+              data.forEach((item) => {
+                this.data[name].push(convertData(item, type));
+              });
+            } else {
+              this.data[name].push(convertData(data, type));
+            }
           }
         }
 

+ 2 - 7
src/components/block/BlockExportData.vue

@@ -30,7 +30,7 @@
       required
     >
       <option value="" disabled selected>Export as</option>
-      <option v-for="type in exportTypes" :key="type.id" :value="type.id">
+      <option v-for="type in dataExportTypes" :key="type.id" :value="type.id">
         {{ type.name }}
       </option>
     </select>
@@ -40,6 +40,7 @@
 import { watch } from 'vue';
 import { VRemixIcon as VRemixicon } from 'v-remixicon';
 import emitter from 'tiny-emitter/instance';
+import { dataExportTypes } from '@/utils/shared';
 import { icons } from '@/lib/v-remixicon';
 import { debounce } from '@/utils/helper';
 import { useComponentId } from '@/composable/componentId';
@@ -55,12 +56,6 @@ const props = defineProps({
 const componentId = useComponentId('block-delay');
 const block = useEditorBlock(`#${componentId}`, props.editor);
 
-const exportTypes = [
-  { name: 'JSON', id: 'json' },
-  { name: 'CSV', id: 'csv' },
-  { name: 'Plain text', id: 'plain-text' },
-];
-
 watch(
   () => block.data,
   debounce((value, oldValue) => {

+ 72 - 0
src/components/newtab/logs/LogsExportData.vue

@@ -0,0 +1,72 @@
+<template>
+  <div class="flex items-center">
+    <ui-input v-model="fileName" placeholder="File name" title="File name" />
+    <div class="flex-grow"></div>
+    <ui-popover>
+      <template #trigger>
+        <ui-button variant="accent">
+          <span>Export data</span>
+          <v-remixicon name="riArrowDropDownLine" class="ml-2 -mr-1" />
+        </ui-button>
+      </template>
+      <ui-list class="space-y-1">
+        <ui-list-item
+          v-for="type in dataExportTypes"
+          :key="type.id"
+          v-close-popover
+          class="cursor-pointer"
+          @click="exportData(type.id)"
+        >
+          as {{ type.name }}
+        </ui-list-item>
+      </ui-list>
+    </ui-popover>
+  </div>
+  <prism-editor
+    :model-value="dataStr"
+    :highlight="highlighter"
+    readonly
+    class="my-editor p-4 bg-gray-900 rounded-lg mt-4"
+    style="max-height: calc(100vh - 12rem)"
+  ></prism-editor>
+</template>
+<script setup>
+import { ref } from 'vue';
+import { PrismEditor } from 'vue-prism-editor';
+import { highlight, languages } from 'prismjs/components/prism-core';
+import { dataExportTypes } from '@/utils/shared';
+import dataExporter, { generateJSON } from '@/utils/data-exporter';
+import 'vue-prism-editor/dist/prismeditor.min.css';
+import 'prismjs/components/prism-json';
+import 'prismjs/themes/prism-tomorrow.css';
+
+const props = defineProps({
+  log: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+
+const data = generateJSON(Object.keys(props.log.data), props.log.data);
+const dataStr = JSON.stringify(data, null, 2);
+
+const fileName = ref(props.log.name);
+const highlighter = (code) => highlight(code, languages.json);
+
+function exportData(type) {
+  dataExporter(data, { name: fileName.value, type }, true);
+}
+</script>
+<style scoped>
+.my-editor {
+  color: #ccc;
+  font-family: JetBrains Mono, Fira code, Fira Mono, Consolas, Menlo, Courier,
+    monospace;
+  font-size: 14px;
+  line-height: 1.5;
+  padding: 5px;
+}
+.prism-editor__textarea:focus {
+  outline: none;
+}
+</style>

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

@@ -169,7 +169,10 @@ const taskList = Object.keys(tasks).reduce((arr, key) => {
 }, {});
 const icons = [
   'riGlobalLine',
+  'riFileTextLine',
   'riEqualizerLine',
+  'riTimerLine',
+  'riCalendarLine',
   'riFlashlightLine',
   'riLightbulbFlashLine',
   'riDatabase2Line',

+ 2 - 2
src/components/newtab/workflow/edit/EditTrigger.vue

@@ -24,10 +24,10 @@
         class="w-full mr-2"
         label="Interval (minutes)"
         placeholder="5-120"
-        min="5"
+        min="10"
         max="120"
         @change="
-          updateIntervalInput($event, { key: 'interval', min: 5, max: 120 })
+          updateIntervalInput($event, { key: 'interval', min: 10, max: 120 })
         "
       />
       <ui-input

+ 14 - 2
src/newtab/pages/logs.vue

@@ -66,6 +66,10 @@
                 v-if="Object.keys(log.data).length !== 0"
                 name="riFileTextLine"
                 class="cursor-pointer"
+                @click="
+                  exportDataModal.show = true;
+                  exportDataModal.log = log;
+                "
               />
               <v-remixicon
                 name="riDeleteBin7Line"
@@ -78,6 +82,10 @@
         </tr>
       </tbody>
     </table>
+    <ui-modal v-model="exportDataModal.show">
+      <template #header> Data </template>
+      <logs-export-data :log="exportDataModal.log" />
+    </ui-modal>
   </div>
 </template>
 <script setup>
@@ -85,6 +93,7 @@ import { shallowReactive, computed } from 'vue';
 import { useStore } from 'vuex';
 import dayjs from '@/lib/dayjs';
 import Log from '@/models/log';
+import LogsExportData from '@/components/newtab/logs/LogsExportData.vue';
 
 const filters = ['all', 'success', 'stopped', 'error'];
 const sorts = [
@@ -105,6 +114,10 @@ const state = shallowReactive({
   sortOrder: 'desc',
   sortBy: 'startedAt',
 });
+const exportDataModal = shallowReactive({
+  show: false,
+  log: {},
+});
 
 const logs = computed(() =>
   Log.query()
@@ -134,8 +147,7 @@ function getDuration(started, ended) {
   const seconds = parseInt(duration % 60, 10);
 
   const getText = (num, suffix) => (num > 0 ? `${num}${suffix}` : '');
-  console.log(duration, minutes, seconds);
-  console.log(started, ended);
+
   return `${getText(minutes, 'm')} ${getText(seconds, 's')}`;
 }
 function deleteLog(id) {

+ 19 - 19
src/utils/data-exporter.js

@@ -1,6 +1,21 @@
 import Papa from 'papaparse';
 
-function generateJSON(keys, data) {
+const files = {
+  'plain-text': {
+    mime: 'text/plain',
+    ext: '.txt',
+  },
+  json: {
+    mime: 'application/json',
+    ext: '.json',
+  },
+  csv: {
+    mime: 'text/csv',
+    ext: '.csv',
+  },
+};
+
+export function generateJSON(keys, data) {
   const result = [];
 
   keys.forEach((key) => {
@@ -18,31 +33,16 @@ function generateJSON(keys, data) {
   return result;
 }
 
-const files = {
-  'plain-text': {
-    mime: 'text/plain',
-    ext: '.txt',
-  },
-  json: {
-    mime: 'application/json',
-    ext: '.json',
-  },
-  csv: {
-    mime: 'text/csv',
-    ext: '.csv',
-  },
-};
-
-export default function (data, { name, type }) {
+export default function (data, { name, type }, converted) {
   let result = data;
 
   if (type === 'csv' || type === 'json') {
-    const jsonData = generateJSON(Object.keys(data), data);
+    const jsonData = converted ? data : generateJSON(Object.keys(data), data);
 
     result =
       type === 'csv'
         ? `data:text/csv;charset=utf-8,${Papa.unparse(jsonData)}`
-        : JSON.stringify(jsonData);
+        : JSON.stringify(jsonData, null, 2);
   } else if (type === 'plain-text') {
     result = Object.values(data).join(' ');
   }

+ 6 - 0
src/utils/shared.js

@@ -348,3 +348,9 @@ export const eventList = [
   { id: 'submit', name: 'Submit', type: 'submit-event' },
   { id: 'wheel', name: 'Wheel', type: 'wheel-event' },
 ];
+
+export const dataExportTypes = [
+  { name: 'JSON', id: 'json' },
+  { name: 'CSV', id: 'csv' },
+  { name: 'Plain text', id: 'plain-text' },
+];

+ 10 - 0
yarn.lock

@@ -5179,6 +5179,11 @@ pretty-hrtime@^1.0.3:
   resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
   integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
 
+prismjs@^1.25.0:
+  version "1.25.0"
+  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756"
+  integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==
+
 process-nextick-args@~2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -6540,6 +6545,11 @@ vue-loader@16.8.1:
     hash-sum "^2.0.0"
     loader-utils "^2.0.0"
 
+vue-prism-editor@^2.0.0-alpha.2:
+  version "2.0.0-alpha.2"
+  resolved "https://registry.yarnpkg.com/vue-prism-editor/-/vue-prism-editor-2.0.0-alpha.2.tgz#aa53a88efaaed628027cbb282c2b1d37fc7c5c69"
+  integrity sha512-Gu42ba9nosrE+gJpnAEuEkDMqG9zSUysIR8SdXUw8MQKDjBnnNR9lHC18uOr/ICz7yrA/5c7jHJr9lpElODC7w==
+
 vue-router@^4.0.11:
   version "4.0.11"
   resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.0.11.tgz#cd649a0941c635281763a20965b599643ddc68ed"