Sfoglia il codice sorgente

feat: running workflows page

Ahmad Kholid 2 anni fa
parent
commit
c838ba6b18

+ 3 - 2
src/background/workflowEngine/engine.js

@@ -342,13 +342,14 @@ class WorkflowEngine {
       tabIds: [],
       currentBlock: [],
       name: this.workflow.name,
+      logs: this.history.slice(-5),
       startedTimestamp: this.startedTimestamp,
     };
 
     this.workers.forEach((worker) => {
-      const { id, name } = worker.currentBlock;
+      const { id, name, startedAt } = worker.currentBlock;
 
-      state.currentBlock.push({ id, name });
+      state.currentBlock.push({ id, name, startedAt });
       state.tabIds.push(worker.activeTab.id);
     });
 

+ 2 - 3
src/background/workflowEngine/worker.js

@@ -110,8 +110,9 @@ class Worker {
       return;
     }
 
+    const startExecuteTime = Date.now();
     const prevBlock = this.currentBlock;
-    this.currentBlock = block;
+    this.currentBlock = { ...block, startedAt: startExecuteTime };
 
     if (!isRetry) {
       await this.engine.updateState({
@@ -120,8 +121,6 @@ class Worker {
       });
     }
 
-    const startExecuteTime = Date.now();
-
     const blockHandler = this.engine.blocksHandler[toCamelCase(block.name)];
     const handler =
       !blockHandler && tasks[block.name].category === 'interaction'

+ 8 - 7
src/components/newtab/app/AppSidebar.vue

@@ -37,6 +37,12 @@
           <div class="p-2 rounded-lg transition-colors inline-block">
             <v-remixicon :name="tab.icon" />
           </div>
+          <span
+            v-if="tab.id === 'log' && runningWorkflowsLen > 0"
+            class="absolute h-4 w-4 text-xs dark:text-black text-white rounded-full bg-accent -top-1 right-2"
+          >
+            {{ runningWorkflowsLen }}
+          </span>
         </a>
       </router-link>
     </div>
@@ -80,7 +86,7 @@
   </aside>
 </template>
 <script setup>
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 import { useStore } from 'vuex';
 import { useI18n } from 'vue-i18n';
 import { useRouter } from 'vue-router';
@@ -97,12 +103,6 @@ const router = useRouter();
 
 const extensionVersion = browser.runtime.getManifest().version;
 const tabs = [
-  {
-    id: 'dashboard',
-    icon: 'riHome5Line',
-    path: '/',
-    shortcut: getShortcut('page:dashboard', '/'),
-  },
   {
     id: 'workflow',
     icon: 'riFlowChart',
@@ -130,6 +130,7 @@ const tabs = [
 ];
 const hoverIndicator = ref(null);
 const showHoverIndicator = ref(false);
+const runningWorkflowsLen = computed(() => store.state.workflowState.length);
 
 useShortcut(
   tabs.map(({ shortcut }) => shortcut),

+ 5 - 6
src/components/newtab/logs/LogsFilters.vue

@@ -68,12 +68,11 @@
         </ui-select>
       </div>
     </ui-popover>
-    <ui-button
-      v-tooltip:bottom="t('log.clearLogs.title')"
-      icon
-      @click="$emit('clear')"
-    >
-      <v-remixicon name="riDeleteBin7Line" />
+    <ui-button @click="$emit('clear')">
+      <v-remixicon name="riDeleteBin7Line" class="mr-2 -ml-1" />
+      <span>
+        {{ t('log.clearLogs.title') }}
+      </span>
     </ui-button>
   </div>
 </template>

+ 4 - 2
src/components/newtab/logs/LogsHistory.vue

@@ -9,10 +9,11 @@
     {{ t('log.goBack', { name: parentLog.name }) }}
   </router-link>
   <div
-    class="p-4 rounded-lg flex items-start font-mono bg-gray-900 dark:bg-gray-800 text-gray-100 dark scroll overflow-auto"
+    class="p-4 rounded-lg bg-gray-900 dark:bg-gray-800 text-gray-100 dark scroll overflow-auto"
     style="max-height: 600px"
   >
-    <div class="text-sm flex-1 space-y-1 overflow-auto">
+    <slot name="prepend" />
+    <div class="text-sm font-mono space-y-1 w-full overflow-auto">
       <ui-expand
         v-for="(item, index) in history"
         :key="item.id || index"
@@ -96,6 +97,7 @@
           >{{ ctxData[state.itemId] || 'EMPTY' }}</pre
         >
       </ui-expand>
+      <slot name="append-items" />
     </div>
   </div>
   <div

+ 126 - 38
src/components/newtab/shared/SharedLogsTable.vue

@@ -1,47 +1,108 @@
 <template>
-  <table>
-    <tbody class="divide-y dark:divide-gray-800">
-      <tr v-for="log in logs" :key="log.id" class="hoverable">
-        <slot name="item-prepend" :log="log" />
-        <td class="text-overflow" style="min-width: 140px; max-width: 330px">
-          <router-link
-            :to="`/logs/${log.id}`"
-            class="inline-block text-overflow w-full align-middle min-h"
-            style="min-height: 28px"
+  <div class="logs-table">
+    <transition-expand>
+      <div v-if="state.selected.length > 0" class="border-x border-t px-4 py-2">
+        <ui-button @click="stopSelectedWorkflow"> Stop selected </ui-button>
+      </div>
+    </transition-expand>
+    <table class="w-full">
+      <tbody class="divide-y dark:divide-gray-800">
+        <tr v-for="item in running" :key="item.id" class="p-2 border">
+          <td v-if="!hideSelect" class="w-8">
+            <ui-checkbox
+              :model-value="state.selected.includes(item.id)"
+              class="align-text-bottom"
+              @change="toggleSelectedLog($event, item.id)"
+            />
+          </td>
+          <td class="w-4/12">
+            <router-link
+              :to="`/logs/${item.id}/running`"
+              class="inline-block text-overflow w-full align-middle min-h"
+              style="min-height: 28px"
+            >
+              {{ item.state.name }}
+            </router-link>
+          </td>
+          <td
+            class="log-time w-2/12 dark:text-gray-200"
+            :title="t('log.duration')"
           >
-            {{ log.name }}
-          </router-link>
-        </td>
-        <td class="log-time dark:text-gray-200">
-          <v-remixicon
-            :title="t('log.startedDate')"
-            name="riCalendarLine"
-            class="mr-2 inline-block align-middle"
-          />
-          <span :title="formatDate(log.startedAt, 'DD MMM YYYY, hh:mm A')">
-            {{ formatDate(log.startedAt, 'relative') }}
-          </span>
-        </td>
-        <td class="log-time dark:text-gray-200" :title="t('log.duration')">
-          <v-remixicon name="riTimerLine"></v-remixicon>
-          <span>{{ countDuration(log.startedAt, log.endedAt) }}</span>
-        </td>
-        <td class="text-right">
-          <span
-            :class="statusColors[log.status]"
-            :title="log.status === 'error' ? getErrorMessage(log) : null"
-            class="inline-block py-1 w-16 text-center text-sm rounded-md dark:text-black"
+            <v-remixicon name="riTimerLine"></v-remixicon>
+            <span>{{
+              countDuration(item.state.startedTimestamp, Date.now())
+            }}</span>
+          </td>
+          <td title="Executing block">
+            <ui-spinner color="text-accent" size="20" />
+            <span class="align-middle inline-block ml-3">
+              {{ t(`workflow.blocks.${item.state.currentBlock[0].name}.name`) }}
+            </span>
+          </td>
+          <td class="text-right">
+            <span
+              class="inline-block py-1 w-16 text-center text-sm rounded-md dark:text-black bg-blue-300"
+            >
+              {{ t('common.running') }}
+            </span>
+          </td>
+          <td class="text-right">
+            <ui-button small class="text-sm" @click="stopWorkflow(item.id)">
+              {{ t('common.stop') }}
+            </ui-button>
+          </td>
+        </tr>
+        <tr v-for="log in logs" :key="log.id" class="hoverable">
+          <slot name="item-prepend" :log="log" />
+          <td
+            class="text-overflow w-4/12"
+            style="min-width: 140px; max-width: 330px"
           >
-            {{ t(`logStatus.${log.status}`) }}
-          </span>
-        </td>
-        <slot name="item-append" :log="log" />
-      </tr>
-    </tbody>
-  </table>
+            <router-link
+              :to="`/logs/${log.id}`"
+              class="inline-block text-overflow w-full align-middle min-h"
+              style="min-height: 28px"
+            >
+              {{ log.name }}
+            </router-link>
+          </td>
+          <td class="log-time w-3/12 dark:text-gray-200">
+            <v-remixicon
+              :title="t('log.startedDate')"
+              name="riCalendarLine"
+              class="mr-2 inline-block align-middle"
+            />
+            <span :title="formatDate(log.startedAt, 'DD MMM YYYY, hh:mm A')">
+              {{ formatDate(log.startedAt, 'relative') }}
+            </span>
+          </td>
+          <td
+            class="log-time w-2/12 dark:text-gray-200"
+            :title="t('log.duration')"
+          >
+            <v-remixicon name="riTimerLine"></v-remixicon>
+            <span>{{ countDuration(log.startedAt, log.endedAt) }}</span>
+          </td>
+          <td class="text-right">
+            <span
+              :class="statusColors[log.status]"
+              :title="log.status === 'error' ? getErrorMessage(log) : null"
+              class="inline-block py-1 w-16 text-center text-sm rounded-md dark:text-black"
+            >
+              {{ t(`logStatus.${log.status}`) }}
+            </span>
+          </td>
+          <slot name="item-append" :log="log" />
+        </tr>
+        <slot name="table:append" />
+      </tbody>
+    </table>
+  </div>
 </template>
 <script setup>
+import { reactive } from 'vue';
 import { useI18n } from 'vue-i18n';
+import { sendMessage } from '@/utils/message';
 import { countDuration } from '@/utils/helper';
 import dayjs from '@/lib/dayjs';
 
@@ -50,6 +111,11 @@ defineProps({
     type: Array,
     default: () => [],
   },
+  running: {
+    type: Array,
+    default: () => [],
+  },
+  hideSelect: Boolean,
 });
 
 const { t, te } = useI18n();
@@ -59,7 +125,23 @@ const statusColors = {
   success: 'bg-green-200 dark:bg-green-300',
   stopped: 'bg-yellow-200 dark:bg-yellow-300',
 };
+const state = reactive({
+  selected: [],
+});
 
+function stopWorkflow(stateId) {
+  sendMessage('workflow:stop', stateId, 'background');
+}
+function toggleSelectedLog(selected, id) {
+  if (selected) {
+    state.selected.push(id);
+    return;
+  }
+
+  const index = state.selected.indexOf(id);
+
+  if (index !== -1) state.selected.splice(index, 1);
+}
 function formatDate(date, format) {
   if (format === 'relative') return dayjs(date).fromNow();
 
@@ -74,6 +156,12 @@ function getErrorMessage({ message }) {
 
   return '';
 }
+function stopSelectedWorkflow() {
+  state.selected.forEach((id) => {
+    stopWorkflow(id);
+  });
+  state.selected = [];
+}
 </script>
 <style scoped>
 .log-time svg {

+ 4 - 0
src/locales/en/newtab.json

@@ -27,6 +27,10 @@
     "clickHere": "Click here",
     "text": "You need to be signed in before you can do that"
   },
+  "running": {
+    "start": "Started on {date}",
+    "message": "This only display the last 5 logs"
+  },
   "settings": {
     "theme": "Theme",
     "shortcuts": {

+ 1 - 4
src/newtab/App.vue

@@ -83,7 +83,6 @@
 import { ref, shallowReactive, computed } from 'vue';
 import { useStore } from 'vuex';
 import { useI18n } from 'vue-i18n';
-import { useRoute } from 'vue-router';
 import { compare } from 'compare-versions';
 import browser from 'webextension-polyfill';
 import dbLogs from '@/db/logs';
@@ -99,7 +98,6 @@ import dataMigration from '@/utils/dataMigration';
 const { t } = useI18n();
 const store = useStore();
 const theme = useTheme();
-const route = useRoute();
 
 theme.init();
 
@@ -306,9 +304,8 @@ window.addEventListener('beforeunload', () => {
   browser.storage.onChanged.removeListener(handleStorageChanged);
 });
 
-const includeRoutes = ['home', 'workflows-details'];
 window.addEventListener('storage', ({ key, newValue }) => {
-  if (key !== 'workflowState' || !includeRoutes.includes(route.name)) return;
+  if (key !== 'workflowState') return;
 
   const states = parseJSON(newValue, {});
   store.commit('updateState', {

+ 34 - 56
src/newtab/pages/Home.vue

@@ -1,59 +1,40 @@
 <template>
   <div class="container pt-8 pb-4">
     <h1 class="text-2xl font-semibold mb-8">{{ t('common.dashboard') }}</h1>
-    <div class="flex items-start">
-      <div class="w-8/12 mr-8">
-        <div class="grid gap-4 mb-8 2xl:grid-cols-4 grid-cols-3">
-          <p
-            v-if="workflows.length === 0"
-            class="text-center text-gray-600 dark:text-gray-200"
-          >
-            {{ t('message.noData') }}
-          </p>
-          <shared-card
-            v-for="workflow in workflows"
-            :key="workflow.id"
-            :data="workflow"
-            :show-details="false"
-            style="max-width: 250px"
-            @execute="executeWorkflow"
-            @click="$router.push(`/workflows/${$event.id}`)"
-          />
-        </div>
-        <div>
-          <div class="mb-2 flex items-center justify-between">
-            <p class="font-semibold inline-block">{{ t('common.log', 2) }}</p>
-            <router-link
-              to="/logs"
-              class="text-gray-600 dark:text-gray-200 text-sm"
-            >
-              {{ t('home.viewAll') }}
-            </router-link>
-          </div>
-          <p
-            v-if="logs?.length === 0"
-            class="text-center text-gray-600 dark:text-gray-200"
-          >
-            {{ t('message.noData') }}
-          </p>
-          <shared-logs-table :logs="logs || []" class="w-full" />
-        </div>
-      </div>
-      <div class="w-4/12 space-y-4">
-        <p
-          v-if="workflowState.length === 0"
-          class="text-center text-gray-600 dark:text-gray-200"
-        >
-          {{ t('message.noData') }}
-        </p>
-        <shared-workflow-state
-          v-for="item in workflowState"
-          v-bind="{ data: item }"
-          :key="item.id"
-          class="w-full"
-        />
-      </div>
+    <div class="grid gap-4 mb-8 2xl:grid-cols-5 grid-cols-4">
+      <p
+        v-if="workflows.length === 0"
+        class="text-center text-gray-600 dark:text-gray-200"
+      >
+        {{ t('message.noData') }}
+      </p>
+      <shared-card
+        v-for="workflow in workflows"
+        :key="workflow.id"
+        :data="workflow"
+        :show-details="false"
+        style="max-width: 250px"
+        @execute="executeWorkflow"
+        @click="$router.push(`/workflows/${$event.id}`)"
+      />
     </div>
+    <div class="mb-2 flex items-center justify-between">
+      <p class="font-semibold inline-block">{{ t('common.log', 2) }}</p>
+      <router-link to="/logs" class="text-gray-600 dark:text-gray-200 text-sm">
+        {{ t('home.viewAll') }}
+      </router-link>
+    </div>
+    <p
+      v-if="logs?.length === 0"
+      class="text-center text-gray-600 dark:text-gray-200"
+    >
+      {{ t('message.noData') }}
+    </p>
+    <shared-logs-table
+      :logs="logs || []"
+      :running="workflowState"
+      class="w-full"
+    />
   </div>
 </template>
 <script setup>
@@ -66,7 +47,6 @@ import dbLogs from '@/db/logs';
 import Workflow from '@/models/workflow';
 import SharedCard from '@/components/newtab/shared/SharedCard.vue';
 import SharedLogsTable from '@/components/newtab/shared/SharedLogsTable.vue';
-import SharedWorkflowState from '@/components/newtab/shared/SharedWorkflowState.vue';
 
 const { t } = useI18n();
 const store = useStore();
@@ -77,9 +57,7 @@ const logs = useLiveQuery(() =>
 const workflows = computed(() =>
   Workflow.query().orderBy('createdAt', 'desc').limit(3).get()
 );
-const workflowState = computed(() =>
-  store.state.workflowState.filter(({ isInCollection }) => !isInCollection)
-);
+const workflowState = computed(() => store.state.workflowState);
 
 function executeWorkflow(workflow) {
   sendMessage('workflow:execute', workflow, 'background');

+ 5 - 1
src/newtab/pages/Logs.vue

@@ -9,7 +9,11 @@
       @updateFilters="filtersBuilder[$event.key] = $event.value"
     />
     <div v-if="logs" style="min-height: 320px">
-      <shared-logs-table :logs="logs" class="w-full">
+      <shared-logs-table
+        :logs="logs"
+        :running="$store.state.workflowState"
+        class="w-full"
+      >
         <template #item-prepend="{ log }">
           <td class="w-8">
             <ui-checkbox

+ 116 - 0
src/newtab/pages/logs/Running.vue

@@ -0,0 +1,116 @@
+<template>
+  <div v-if="running" class="container py-8">
+    <div class="flex items-center">
+      <div class="flex-grow overflow-hidden">
+        <h1 class="text-2xl max-w-md text-overflow font-semibold text-overflow">
+          {{ running.state.name }}
+        </h1>
+        <p>
+          {{
+            t('running.start', {
+              date: dayjs(running.state.startedTimestamp).format(
+                'DD MMM, hh:mm A'
+              ),
+            })
+          }}
+        </p>
+      </div>
+      <ui-button @click="stopWorkflow">
+        {{ t('common.stop') }}
+      </ui-button>
+    </div>
+    <div class="mt-8">
+      <logs-history
+        :current-log="{
+          history: running.state.logs,
+          workflowId: running.workflowId,
+        }"
+      >
+        <template #prepend>
+          <div class="mb-4">
+            <h3 class="leading-tight">
+              {{ t('common.log', 2) }}
+            </h3>
+            <p class="leading-tight text-gray-600 dark:text-gray-300">
+              {{ t('running.message') }}
+            </p>
+          </div>
+        </template>
+        <template #append-items>
+          <div
+            v-for="block in running.state.currentBlock"
+            :key="block.id"
+            class="px-2 py-1 rounded-md w-full group hoverable flex items-center"
+          >
+            <span
+              :key="key"
+              :title="`Duration: ${Math.round(
+                (Date.now() - block.startedAt) / 1000
+              )}s`"
+              class="w-14 flex-shrink-0 text-overflow text-gray-400 ml-6"
+            >
+              {{ countDuration(block.startedAt, Date.now()) }}
+            </span>
+            <ui-spinner size="16" class="mr-2" color="text-accent" />
+            <p class="flex-1">
+              {{ t(`workflow.blocks.${block.name}.name`) }}
+            </p>
+            <router-link
+              :to="`/workflows/${running.workflowId}?block=${block.id}`"
+              title="Go to block"
+              class="invisible group-hover:visible"
+            >
+              <v-remixicon
+                name="riExternalLinkLine"
+                size="20"
+                title="Go to block"
+                class="text-gray-300 cursor-pointer ml-2 invisible group-hover:visible"
+              />
+            </router-link>
+          </div>
+        </template>
+      </logs-history>
+    </div>
+  </div>
+</template>
+<script setup>
+import { computed, watch, shallowRef, onBeforeUnmount } from 'vue';
+import { useStore } from 'vuex';
+import { useRoute, useRouter } from 'vue-router';
+import { useI18n } from 'vue-i18n';
+import { countDuration } from '@/utils/helper';
+import { sendMessage } from '@/utils/message';
+import dayjs from '@/lib/dayjs';
+import LogsHistory from '@/components/newtab/logs/LogsHistory.vue';
+
+const { t } = useI18n();
+const store = useStore();
+const route = useRoute();
+const router = useRouter();
+
+const key = shallowRef(0);
+const interval = setInterval(() => {
+  key.value += 1;
+}, 1000);
+
+const running = computed(() =>
+  store.state.workflowState.find(({ id }) => id === route.params.id)
+);
+
+function stopWorkflow() {
+  sendMessage('workflow:stop', running.value.id, 'background');
+}
+
+watch(
+  running,
+  () => {
+    if (!running.value && route.params.id) {
+      router.replace('/logs');
+    }
+  },
+  { immediate: true }
+);
+onBeforeUnmount(() => {
+  clearInterval(interval);
+});
+</script>

+ 8 - 21
src/newtab/pages/workflows/[id].vue

@@ -41,9 +41,8 @@
             />
           </button>
           <ui-tab value="editor">{{ t('common.editor') }}</ui-tab>
-          <ui-tab value="logs">{{ t('common.log', 2) }}</ui-tab>
-          <ui-tab value="running" class="flex items-center">
-            {{ t('common.running') }}
+          <ui-tab value="logs" class="flex items-center">
+            {{ t('common.log', 2) }}
             <span
               v-if="workflowState.length > 0"
               class="ml-2 p-1 text-center inline-block text-xs rounded-full bg-accent text-white dark:text-black"
@@ -122,7 +121,12 @@
               />
               <p class="text-xl font-semibold">{{ t('message.noData') }}</p>
             </div>
-            <shared-logs-table :logs="logs" class="w-full">
+            <shared-logs-table
+              :logs="logs"
+              :running="workflowState"
+              hide-select
+              class="w-full"
+            >
               <template #item-append="{ log: itemLog }">
                 <td class="text-right">
                   <v-remixicon
@@ -134,22 +138,6 @@
               </template>
             </shared-logs-table>
           </template>
-          <template v-else-if="activeTab === 'running'">
-            <div v-if="workflowState.length === 0" class="text-center">
-              <img
-                src="@/assets/svg/files-and-folder.svg"
-                class="mx-auto max-w-sm"
-              />
-              <p class="text-xl font-semibold">{{ t('message.noData') }}</p>
-            </div>
-            <div class="grid grid-cols-2 gap-4">
-              <shared-workflow-state
-                v-for="item in workflowState"
-                :key="item.id"
-                :data="item"
-              />
-            </div>
-          </template>
         </div>
       </keep-alive>
     </div>
@@ -253,7 +241,6 @@ import WorkflowGlobalData from '@/components/newtab/workflow/WorkflowGlobalData.
 import WorkflowDetailsCard from '@/components/newtab/workflow/WorkflowDetailsCard.vue';
 import WorkflowSharedActions from '@/components/newtab/workflow/WorkflowSharedActions.vue';
 import SharedLogsTable from '@/components/newtab/shared/SharedLogsTable.vue';
-import SharedWorkflowState from '@/components/newtab/shared/SharedWorkflowState.vue';
 
 const { t } = useI18n();
 const store = useStore();

+ 7 - 2
src/newtab/router.js

@@ -1,5 +1,4 @@
 import { createRouter, createWebHashHistory } from 'vue-router';
-import Home from './pages/Home.vue';
 import Welcome from './pages/Welcome.vue';
 import Workflows from './pages/Workflows.vue';
 import WorkflowHost from './pages/workflows/Host.vue';
@@ -8,6 +7,7 @@ import Collections from './pages/Collections.vue';
 import CollectionsDetails from './pages/collections/[id].vue';
 import Logs from './pages/Logs.vue';
 import LogsDetails from './pages/logs/[id].vue';
+import LogsRunning from './pages/logs/Running.vue';
 import Settings from './pages/Settings.vue';
 import SettingsIndex from './pages/settings/SettingsIndex.vue';
 import SettingsAbout from './pages/settings/SettingsAbout.vue';
@@ -19,7 +19,7 @@ const routes = [
   {
     name: 'home',
     path: '/',
-    component: Home,
+    component: Workflows,
   },
   {
     name: 'welcome',
@@ -61,6 +61,11 @@ const routes = [
     path: '/logs/:id',
     component: LogsDetails,
   },
+  {
+    name: 'logs-running',
+    path: '/logs/:id/running',
+    component: LogsRunning,
+  },
   {
     path: '/settings',
     component: Settings,

+ 42 - 17
src/store/index.js

@@ -6,14 +6,44 @@ import defu from 'defu';
 import * as models from '@/models';
 import { firstWorkflows } from '@/utils/shared';
 import { fetchApi } from '@/utils/api';
-import { findTriggerBlock } from '@/utils/helper';
+import { findTriggerBlock, parseJSON } from '@/utils/helper';
 import { registerWorkflowTrigger } from '@/utils/workflowTrigger';
 
 const store = createStore({
   plugins: [vuexORM(models)],
   state: () => ({
     user: null,
-    workflowState: [],
+    workflowState: [
+      {
+        id: '7F9HCTQXKMSDGlm_q_dVW',
+        state: {
+          activeTabUrl: '',
+          childWorkflowId: null,
+          tabIds: [null],
+          currentBlock: [
+            {
+              id: '1fb2464c-b94d-48f0-b40a-2903f2592428',
+              name: 'delay',
+              startedAt: 1655001148198,
+            },
+          ],
+          name: 'Child',
+          logs: [
+            {
+              type: 'success',
+              name: 'trigger',
+              blockId: '1991a5a0-a499-4c70-9040-03b37123b5df',
+              workerId: 'worker-1',
+              description: '',
+              duration: 1,
+              id: 1,
+            },
+          ],
+          startedTimestamp: 1655001148195,
+        },
+        workflowId: 'lPKjzF5cUfzckN3KgCdmX',
+      },
+    ],
     backupIds: [],
     contributors: null,
     hostWorkflows: {},
@@ -98,22 +128,17 @@ const store = createStore({
         throw error;
       }
     },
-    async retrieveWorkflowState({ commit }) {
-      try {
-        const { workflowState } = await browser.storage.local.get(
-          'workflowState'
-        );
+    retrieveWorkflowState({ commit }) {
+      const storedStates = localStorage.getItem('workflowState') || '{}';
+      const states = parseJSON(storedStates, {});
 
-        commit('updateState', {
-          key: 'workflowState',
-          value: Object.values(workflowState || {}).filter(
-            ({ isDestroyed, parentState }) =>
-              !isDestroyed && !parentState?.isCollection
-          ),
-        });
-      } catch (error) {
-        console.error(error);
-      }
+      commit('updateState', {
+        key: 'workflowState',
+        value: Object.values(states).filter(
+          ({ isDestroyed, parentState }) =>
+            !isDestroyed && !parentState?.isCollection
+        ),
+      });
     },
     saveToStorage({ getters }, key) {
       return new Promise((resolve, reject) => {