Browse Source

feat(newtab): add log details

Ahmad Kholid 3 years ago
parent
commit
cc7c32553f

+ 6 - 2
src/assets/css/tailwind.css

@@ -7,7 +7,10 @@ body {
   font-size: 16px;
   @apply bg-gray-50 dark:bg-gray-900;
 }
-
+table td,
+table tr {
+  @apply p-2;
+}
 input:focus,
 button:focus,
 textarea:focus,
@@ -16,12 +19,13 @@ select:focus,
 	outline: none;
 	@apply ring-2 ring-accent dark:ring-gray-200;
 }
+
+
 .text-overflow {
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
 }
-
 .line-clamp {
   display: -webkit-box;
   -webkit-line-clamp: 2;

+ 3 - 5
src/background/blocks-handler.js

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

+ 2 - 1
src/background/workflow-engine.js

@@ -233,11 +233,11 @@ class WorkflowEngine {
     }, this.workflow.settings.timeout || 120000);
 
     workflowState.update(this.id, this.state);
-    console.log(this.logs);
     console.log(`${block.name}:`, block);
 
     this.currentBlock = block;
 
+    const started = Date.now();
     const isInteraction = tasks[block.name].category === 'interaction';
     const handlerName = isInteraction
       ? 'interactionHandler'
@@ -253,6 +253,7 @@ class WorkflowEngine {
               type: 'success',
               name: tasks[block.name].name,
               data: result.data,
+              duration: Math.round(Date.now() - started),
             });
 
             this._blockHandler(this.blocks[result.nextBlockId], result.data);

+ 5 - 1
src/components/newtab/logs/LogsExportData.vue → src/components/newtab/logs/LogsDataViewer.vue

@@ -25,9 +25,9 @@
   <prism-editor
     :model-value="dataStr"
     :highlight="highlighter"
+    :class="editorClass"
     readonly
     class="my-editor p-4 bg-gray-900 rounded-lg mt-4"
-    style="max-height: calc(100vh - 12rem)"
   ></prism-editor>
 </template>
 <script setup>
@@ -45,6 +45,10 @@ const props = defineProps({
     type: Object,
     default: () => ({}),
   },
+  editorClass: {
+    type: String,
+    default: '',
+  },
 });
 
 const data = generateJSON(Object.keys(props.log.data), props.log.data);

+ 8 - 1
src/components/newtab/workflow/edit/EditAttributeValue.vue

@@ -6,7 +6,14 @@
       class="mt-3 w-full"
       @change="updateData({ attributeName: $event })"
     />
-    <div class="flex items-center mt-3">
+    <ui-checkbox
+      :model-value="data.saveData"
+      class="mt-3"
+      @change="updateData({ saveData: $event })"
+    >
+      Save data
+    </ui-checkbox>
+    <div v-if="data.saveData" class="flex items-center mt-1">
       <ui-select
         :model-value="data.dataColumn"
         placeholder="Data column"

+ 9 - 1
src/components/newtab/workflow/edit/EditGetText.vue

@@ -1,5 +1,6 @@
 <template>
   <edit-interaction-base v-bind="{ data }" @change="updateData">
+    {{ data }}
     <div class="flex rounded-lg bg-input px-4 items-center transition mt-2">
       <span>/</span>
       <input
@@ -25,7 +26,14 @@
         </div>
       </ui-popover>
     </div>
-    <div class="flex items-center mt-3">
+    <ui-checkbox
+      :model-value="data.saveData"
+      class="mt-3"
+      @change="updateData({ saveData: $event })"
+    >
+      Save data
+    </ui-checkbox>
+    <div v-if="data.saveData" class="flex items-center mt-1">
       <ui-select
         :model-value="data.dataColumn"
         placeholder="Data column"

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

@@ -1,6 +1,7 @@
 import vRemixicon from 'v-remixicon';
 import {
   riHome5Line,
+  riErrorWarningLine,
   riCalendarLine,
   riFileTextLine,
   riArrowGoBackLine,
@@ -54,10 +55,12 @@ import {
   riTimerLine,
   riLightbulbFlashLine,
   riFlashlightLine,
+  riFlagLine,
 } from 'v-remixicon/icons';
 
 export const icons = {
   riHome5Line,
+  riErrorWarningLine,
   riCalendarLine,
   riFileTextLine,
   riArrowGoBackLine,
@@ -111,6 +114,7 @@ export const icons = {
   riTimerLine,
   riLightbulbFlashLine,
   riFlashlightLine,
+  riFlagLine,
   mdiEqual: 'M19,10H5V8H19V10M19,16H5V14H19V16Z',
   mdiDrag:
     'M7,19V17H9V19H7M11,19V17H13V19H11M15,19V17H17V19H15M7,15V13H9V15H7M11,15V13H13V15H11M15,15V13H17V15H15M7,11V9H9V11H7M11,11V9H13V11H11M15,11V9H17V11H15M7,7V5H9V7H7M11,7V5H13V7H11M15,7V5H17V7H15Z',

+ 19 - 18
src/newtab/pages/logs.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="container pt-8 pb-4">
+  <div class="container pt-8 pb-4 logs-list">
     <h1 class="text-2xl font-semibold mb-6">Logs</h1>
     <div class="flex items-center mb-6 space-x-4">
       <ui-input
@@ -34,7 +34,12 @@
       <tbody>
         <tr v-for="log in logs" :key="log.id" class="hoverable border-b">
           <td style="min-width: 150px">
-            {{ log.name }}
+            <router-link
+              :to="`/logs/${log.id}`"
+              class="block w-full h-full text-overflow"
+            >
+              {{ log.name }}
+            </router-link>
           </td>
           <td>
             <span
@@ -55,7 +60,7 @@
           </td>
           <td class="log-time" title="Duration">
             <v-remixicon name="riTimerLine"></v-remixicon>
-            <span>{{ getDuration(log.startedAt, log.endedAt) }}</span>
+            <span>{{ countDuration(log.startedAt, log.endedAt) }}</span>
           </td>
           <td>
             <div
@@ -84,7 +89,10 @@
     </table>
     <ui-modal v-model="exportDataModal.show">
       <template #header> Data </template>
-      <logs-export-data :log="exportDataModal.log" />
+      <logs-data-viewer
+        :log="exportDataModal.log"
+        editor-class="logs-list-data"
+      />
     </ui-modal>
   </div>
 </template>
@@ -93,7 +101,8 @@ 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';
+import { countDuration } from '@/utils/helper';
+import LogsDataViewer from '@/components/newtab/logs/LogsDataViewer.vue';
 
 const filters = ['all', 'success', 'stopped', 'error'];
 const sorts = [
@@ -141,26 +150,18 @@ function formatDate(date, format) {
 
   return dayjs(date).format(format);
 }
-function getDuration(started, ended) {
-  const duration = dayjs(ended).diff(started, 'second');
-  const minutes = parseInt((duration / 60) % 60, 10);
-  const seconds = parseInt(duration % 60, 10);
-
-  const getText = (num, suffix) => (num > 0 ? `${num}${suffix}` : '');
-
-  return `${getText(minutes, 'm')} ${getText(seconds, 's')}`;
-}
 function deleteLog(id) {
   Log.delete(id).then(() => {
     store.dispatch('saveToStorage', 'logs');
   });
 }
 </script>
-<style scoped>
-.logs-table td {
-  @apply p-2;
+<style>
+.logs-list-data {
+  max-height: calc(100vh - 12rem);
 }
-
+</style>
+<style scoped>
 .log-time {
   @apply text-gray-600 dark:text-gray-200;
 }

+ 88 - 0
src/newtab/pages/logs/[id].vue

@@ -0,0 +1,88 @@
+<template>
+  <div class="container pt-8 pb-4">
+    <div class="flex items-center mb-8">
+      <div>
+        <h1 class="text-2xl max-w-sm text-overflow font-semibold">
+          {{ activeLog.name }}
+        </h1>
+        <p class="text-gray-600">
+          <span class="capitalize">
+            {{
+              activeLog.status === 'success' ? 'succeeded' : activeLog.status
+            }}
+          </span>
+          <span
+            :title="dayjs(activeLog.startedAt).format('DD MMM YYYY, hh:mm A')"
+          >
+            on {{ dayjs(activeLog.startedAt).format('DD MMM') }}
+          </span>
+          in {{ countDuration(activeLog.startedAt, activeLog.endedAt) }}
+        </p>
+      </div>
+      <div class="flex-grow"></div>
+      <ui-input prepend-icon="riSearch2Line" placeholder="Search..." />
+    </div>
+    <div class="flex items-start">
+      <ui-list class="w-7/12 mr-6" style="height: 900px">
+        <ui-list-item v-for="(item, index) in activeLog.history" :key="index">
+          <span
+            :title="item.message || item.type"
+            :class="logsType[item.type].color"
+            class="p-1 rounded-lg align-middle inline-block mr-2"
+          >
+            <v-remixicon :name="logsType[item.type].icon" size="20" />
+          </span>
+          <p class="flex-1 text-overflow">
+            {{ item.name }}
+          </p>
+          <p class="text-gray-600">
+            {{ countDuration(0, item.duration || 0) }}
+          </p>
+        </ui-list-item>
+      </ui-list>
+      <div class="w-5/12 logs-details sticky top-10">
+        <logs-data-viewer :log="activeLog" />
+      </div>
+    </div>
+  </div>
+</template>
+<script setup>
+import { computed } from 'vue';
+import { useRoute } from 'vue-router';
+import Log from '@/models/log';
+import dayjs from '@/lib/dayjs';
+import { countDuration } from '@/utils/helper';
+import LogsDataViewer from '@/components/newtab/logs/LogsDataViewer.vue';
+
+const logsType = {
+  success: {
+    color: 'bg-green-200',
+    icon: 'riCheckLine',
+  },
+  stop: {
+    color: 'bg-yellow-200',
+    icon: 'riStopLine',
+  },
+  error: {
+    color: 'bg-red-200',
+    icon: 'riErrorWarningLine',
+  },
+  finish: {
+    color: 'bg-blue-200',
+    icon: 'riFlagLine',
+  },
+};
+
+const route = useRoute();
+
+const activeLog = computed(() => Log.find(route.params.id));
+
+setTimeout(() => {
+  console.log(activeLog.value);
+}, 2000);
+</script>
+<style>
+.logs-details .my-editor {
+  max-height: calc(100vh - 12rem);
+}
+</style>

+ 6 - 0
src/newtab/router.js

@@ -3,6 +3,7 @@ import Home from './pages/Home.vue';
 import Workflows from './pages/Workflows.vue';
 import WorkflowDetails from './pages/workflows/[id].vue';
 import Logs from './pages/Logs.vue';
+import LogsDetails from './pages/logs/[id].vue';
 
 const routes = [
   {
@@ -25,6 +26,11 @@ const routes = [
     path: '/logs',
     component: Logs,
   },
+  {
+    name: 'logs-details',
+    path: '/logs/:id',
+    component: LogsDetails,
+  },
 ];
 
 export default createRouter({

+ 10 - 0
src/utils/helper.js

@@ -1,3 +1,13 @@
+export function countDuration(started, ended) {
+  const duration = Math.round((ended - started) / 1000);
+  const minutes = parseInt((duration / 60) % 60, 10);
+  const seconds = parseInt(duration % 60, 10);
+
+  const getText = (num, suffix) => (num > 0 ? `${num}${suffix}` : '');
+
+  return `${getText(minutes, 'm')} ${seconds}s`;
+}
+
 export function toCamelCase(str) {
   const result = str.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => {
     return index === 0 ? letter.toLowerCase() : letter.toUpperCase();

+ 2 - 0
src/utils/shared.js

@@ -130,6 +130,7 @@ export const tasks = {
       regex: '',
       regexExp: ['g'],
       dataColumn: '',
+      saveData: true,
     },
   },
   'export-data': {
@@ -206,6 +207,7 @@ export const tasks = {
       multiple: false,
       attributeName: '',
       dataColumn: '',
+      saveData: true,
     },
   },
   forms: {