@nvent-addon/app 0.4.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.d.mts +19 -1
- package/dist/module.mjs +21 -4
- package/dist/runtime/app/components/{nhealth/component-router.d.vue.ts → ComponentRouter.d.vue.ts} +1 -5
- package/dist/runtime/app/components/{nhealth/component-router.vue.d.ts → ComponentRouter.vue.d.ts} +1 -5
- package/dist/runtime/app/components/{nhealth/component-shell.d.vue.ts → ComponentShell.d.vue.ts} +4 -9
- package/dist/runtime/app/components/ComponentShell.vue +87 -0
- package/dist/runtime/app/components/{nhealth/component-shell.vue.d.ts → ComponentShell.vue.d.ts} +4 -9
- package/dist/runtime/app/components/ConfirmDialog.d.vue.ts +1 -6
- package/dist/runtime/app/components/ConfirmDialog.vue.d.ts +1 -6
- package/dist/runtime/app/components/ListItem.d.vue.ts +3 -6
- package/dist/runtime/app/components/ListItem.vue.d.ts +3 -6
- package/dist/runtime/app/components/LiveIndicator.d.vue.ts +7 -0
- package/dist/runtime/app/components/LiveIndicator.vue +30 -0
- package/dist/runtime/app/components/LiveIndicator.vue.d.ts +7 -0
- package/dist/runtime/app/components/{QueueConfigDetails.d.vue.ts → QueueConfiguration.d.vue.ts} +1 -10
- package/dist/runtime/app/components/QueueConfiguration.vue +387 -0
- package/dist/runtime/app/components/{QueueConfigDetails.vue.d.ts → QueueConfiguration.vue.d.ts} +1 -10
- package/dist/runtime/app/components/StatCard.d.vue.ts +9 -0
- package/dist/runtime/app/components/StatCard.vue +57 -0
- package/dist/runtime/app/components/StatCard.vue.d.ts +9 -0
- package/dist/runtime/app/components/TimelineList.vue +67 -0
- package/dist/runtime/app/components/flow/AwaitNode.d.vue.ts +18 -0
- package/dist/runtime/app/components/flow/AwaitNode.vue +91 -0
- package/dist/runtime/app/components/flow/AwaitNode.vue.d.ts +18 -0
- package/dist/runtime/app/components/{FlowDiagram.d.vue.ts → flow/Diagram.d.vue.ts} +12 -1
- package/dist/runtime/app/components/{FlowDiagram.vue → flow/Diagram.vue} +92 -11
- package/dist/runtime/app/components/{FlowDiagram.vue.d.ts → flow/Diagram.vue.d.ts} +12 -1
- package/dist/runtime/app/components/{FlowRunOverview.d.vue.ts → flow/RunOverview.d.vue.ts} +3 -0
- package/dist/runtime/app/components/{FlowRunOverview.vue → flow/RunOverview.vue} +94 -8
- package/dist/runtime/app/components/{FlowRunOverview.vue.d.ts → flow/RunOverview.vue.d.ts} +3 -0
- package/dist/runtime/app/components/{FlowRunStatusBadge.d.vue.ts → flow/RunStatusBadge.d.vue.ts} +2 -8
- package/dist/runtime/app/components/{FlowRunStatusBadge.vue → flow/RunStatusBadge.vue} +8 -1
- package/dist/runtime/app/components/{FlowRunStatusBadge.vue.d.ts → flow/RunStatusBadge.vue.d.ts} +2 -8
- package/dist/runtime/app/components/{FlowRunTimeline.vue → flow/RunTimeline.vue} +1 -1
- package/dist/runtime/app/components/{FlowStepSelector.d.vue.ts → flow/StepSelector.d.vue.ts} +1 -0
- package/dist/runtime/app/components/flow/StepSelector.vue +553 -0
- package/dist/runtime/app/components/{FlowStepSelector.vue.d.ts → flow/StepSelector.vue.d.ts} +1 -0
- package/dist/runtime/app/components/trigger/BasicInfoCard.d.vue.ts +33 -0
- package/dist/runtime/app/components/trigger/BasicInfoCard.vue +168 -0
- package/dist/runtime/app/components/trigger/BasicInfoCard.vue.d.ts +33 -0
- package/dist/runtime/app/components/{FlowSchedulesList.d.vue.ts → trigger/DangerZone.d.vue.ts} +4 -6
- package/dist/runtime/app/components/trigger/DangerZone.vue +46 -0
- package/dist/runtime/app/components/{FlowSchedulesList.vue.d.ts → trigger/DangerZone.vue.d.ts} +4 -6
- package/dist/runtime/app/components/trigger/EditHeader.d.vue.ts +15 -0
- package/dist/runtime/app/components/trigger/EditHeader.vue +55 -0
- package/dist/runtime/app/components/trigger/EditHeader.vue.d.ts +15 -0
- package/dist/runtime/app/components/trigger/EventConfig.d.vue.ts +24 -0
- package/dist/runtime/app/components/trigger/EventConfig.vue +68 -0
- package/dist/runtime/app/components/trigger/EventConfig.vue.d.ts +24 -0
- package/dist/runtime/app/components/trigger/FlowSubscriptions.d.vue.ts +14 -0
- package/dist/runtime/app/components/trigger/FlowSubscriptions.vue +128 -0
- package/dist/runtime/app/components/trigger/FlowSubscriptions.vue.d.ts +14 -0
- package/dist/runtime/app/components/trigger/ScheduleConfig.d.vue.ts +27 -0
- package/dist/runtime/app/components/trigger/ScheduleConfig.vue +375 -0
- package/dist/runtime/app/components/trigger/ScheduleConfig.vue.d.ts +27 -0
- package/dist/runtime/app/components/{FlowScheduleDialog.d.vue.ts → trigger/StatusConfig.d.vue.ts} +6 -6
- package/dist/runtime/app/components/trigger/StatusConfig.vue +78 -0
- package/dist/runtime/app/components/{FlowScheduleDialog.vue.d.ts → trigger/StatusConfig.vue.d.ts} +6 -6
- package/dist/runtime/app/components/trigger/WebhookConfig.d.vue.ts +30 -0
- package/dist/runtime/app/components/trigger/WebhookConfig.vue +97 -0
- package/dist/runtime/app/components/trigger/WebhookConfig.vue.d.ts +30 -0
- package/dist/runtime/app/composables/useAnalyzedFlows.d.ts +5 -0
- package/dist/runtime/app/composables/useAnalyzedFlows.js +15 -1
- package/dist/runtime/app/composables/useComponentRouter.d.ts +8 -0
- package/dist/runtime/app/composables/useComponentRouter.js +10 -2
- package/dist/runtime/app/composables/useFlowRunsInfinite.d.ts +1 -1
- package/dist/runtime/app/composables/useFlowState.js +65 -0
- package/dist/runtime/app/composables/useFlowWebSocket.d.ts +11 -2
- package/dist/runtime/app/composables/useFlowWebSocket.js +181 -65
- package/dist/runtime/app/composables/useQueueJobs.d.ts +12 -1
- package/dist/runtime/app/composables/useQueueJobs.js +13 -7
- package/dist/runtime/app/composables/useTrigger.d.ts +137 -0
- package/dist/runtime/app/composables/useTrigger.js +116 -0
- package/dist/runtime/app/composables/useTriggerWebSocket.d.ts +35 -0
- package/dist/runtime/app/composables/useTriggerWebSocket.js +333 -0
- package/dist/runtime/app/pages/dashboard.d.vue.ts +3 -0
- package/dist/runtime/app/pages/dashboard.vue +738 -0
- package/dist/runtime/app/pages/dashboard.vue.d.ts +3 -0
- package/dist/runtime/app/pages/flows/[name].d.vue.ts +3 -0
- package/dist/runtime/app/pages/flows/[name].vue +680 -0
- package/dist/runtime/app/pages/flows/[name].vue.d.ts +3 -0
- package/dist/runtime/app/pages/flows/index.vue +321 -620
- package/dist/runtime/app/pages/index.vue +39 -9
- package/dist/runtime/app/pages/queues/index.vue +202 -194
- package/dist/runtime/app/pages/queues/jobs.vue +534 -207
- package/dist/runtime/app/pages/settings/scheduler.d.vue.ts +3 -0
- package/dist/runtime/app/pages/settings/scheduler.vue +310 -0
- package/dist/runtime/app/pages/settings/scheduler.vue.d.ts +3 -0
- package/dist/runtime/app/pages/triggers/[name]/edit.d.vue.ts +3 -0
- package/dist/runtime/app/pages/triggers/[name]/edit.vue +429 -0
- package/dist/runtime/app/pages/triggers/[name]/edit.vue.d.ts +3 -0
- package/dist/runtime/app/pages/triggers/[name].d.vue.ts +3 -0
- package/dist/runtime/app/pages/triggers/[name].vue +898 -0
- package/dist/runtime/app/pages/triggers/[name].vue.d.ts +3 -0
- package/dist/runtime/app/pages/triggers/index.d.vue.ts +3 -0
- package/dist/runtime/app/pages/triggers/index.vue +528 -0
- package/dist/runtime/app/pages/triggers/index.vue.d.ts +3 -0
- package/dist/runtime/app/pages/triggers/new.d.vue.ts +3 -0
- package/dist/runtime/app/pages/triggers/new.vue +610 -0
- package/dist/runtime/app/pages/triggers/new.vue.d.ts +3 -0
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.d.ts +10 -0
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.js +49 -0
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.d.ts +2 -0
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.js +21 -0
- package/dist/runtime/server/api/_flows/[name]/runs.get.d.ts +17 -0
- package/dist/runtime/server/api/_flows/[name]/runs.get.js +64 -0
- package/dist/runtime/server/api/_flows/[name]/start.post.d.ts +2 -0
- package/dist/runtime/server/api/_flows/[name]/start.post.js +9 -0
- package/dist/runtime/server/api/_flows/index.get.d.ts +7 -0
- package/dist/runtime/server/api/_flows/index.get.js +5 -0
- package/dist/runtime/server/api/_flows/recent-runs.get.d.ts +15 -0
- package/dist/runtime/server/api/_flows/recent-runs.get.js +67 -0
- package/dist/runtime/server/api/_flows/ws.d.ts +80 -0
- package/dist/runtime/server/api/_flows/ws.js +309 -0
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.d.ts +2 -0
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.js +14 -0
- package/dist/runtime/server/api/_queues/[name]/job/index.get.d.ts +2 -0
- package/dist/runtime/server/api/_queues/[name]/job/index.get.js +39 -0
- package/dist/runtime/server/api/_queues/index.get.d.ts +2 -0
- package/dist/runtime/server/api/_queues/index.get.js +106 -0
- package/dist/runtime/server/api/_queues/ws.d.ts +48 -0
- package/dist/runtime/server/api/_queues/ws.js +215 -0
- package/dist/runtime/server/api/_scheduler/jobs.get.d.ts +19 -0
- package/dist/runtime/server/api/_scheduler/jobs.get.js +36 -0
- package/dist/runtime/server/api/_triggers/[name]/events.get.d.ts +6 -0
- package/dist/runtime/server/api/_triggers/[name]/events.get.js +43 -0
- package/dist/runtime/server/api/_triggers/[name]/index.get.d.ts +6 -0
- package/dist/runtime/server/api/_triggers/[name]/index.get.js +76 -0
- package/dist/runtime/server/api/_triggers/[name].delete.d.ts +7 -0
- package/dist/runtime/server/api/_triggers/[name].delete.js +37 -0
- package/dist/runtime/server/api/_triggers/[name].patch.d.ts +7 -0
- package/dist/runtime/server/api/_triggers/[name].patch.js +117 -0
- package/dist/runtime/server/api/_triggers/index.get.d.ts +6 -0
- package/dist/runtime/server/api/_triggers/index.get.js +44 -0
- package/dist/runtime/server/api/_triggers/index.post.d.ts +7 -0
- package/dist/runtime/server/api/_triggers/index.post.js +124 -0
- package/dist/runtime/server/api/_triggers/stats.get.d.ts +6 -0
- package/dist/runtime/server/api/_triggers/stats.get.js +41 -0
- package/dist/runtime/server/api/_triggers/ws.d.ts +74 -0
- package/dist/runtime/server/api/_triggers/ws.js +315 -0
- package/dist/runtime/server/tsconfig.json +7 -0
- package/package.json +8 -8
- package/dist/runtime/app/components/FlowScheduleDialog.vue +0 -226
- package/dist/runtime/app/components/FlowSchedulesList.vue +0 -99
- package/dist/runtime/app/components/FlowStepSelector.vue +0 -238
- package/dist/runtime/app/components/QueueConfigDetails.vue +0 -412
- package/dist/runtime/app/components/nhealth/component-shell.vue +0 -89
- /package/dist/runtime/app/components/{nhealth/component-router.vue → ComponentRouter.vue} +0 -0
- /package/dist/runtime/app/components/{FlowNodeCard.d.vue.ts → flow/NodeCard.d.vue.ts} +0 -0
- /package/dist/runtime/app/components/{FlowNodeCard.vue → flow/NodeCard.vue} +0 -0
- /package/dist/runtime/app/components/{FlowNodeCard.vue.d.ts → flow/NodeCard.vue.d.ts} +0 -0
- /package/dist/runtime/app/components/{FlowRunTimeline.d.vue.ts → flow/RunTimeline.d.vue.ts} +0 -0
- /package/dist/runtime/app/components/{FlowRunTimeline.vue.d.ts → flow/RunTimeline.vue.d.ts} +0 -0
|
@@ -8,676 +8,377 @@
|
|
|
8
8
|
Flows
|
|
9
9
|
</h1>
|
|
10
10
|
</div>
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
placeholder="Select a flow..."
|
|
16
|
-
class="w-64"
|
|
17
|
-
>
|
|
18
|
-
<template #leading>
|
|
19
|
-
<UIcon
|
|
20
|
-
v-if="selectedFlow"
|
|
21
|
-
name="i-lucide-git-branch"
|
|
22
|
-
class="w-4 h-4 text-gray-500"
|
|
23
|
-
/>
|
|
24
|
-
</template>
|
|
25
|
-
</USelectMenu>
|
|
26
|
-
</div>
|
|
11
|
+
<LiveIndicator
|
|
12
|
+
:is-connected="flowWs.connected.value"
|
|
13
|
+
:is-reconnecting="flowWs.reconnecting.value"
|
|
14
|
+
/>
|
|
27
15
|
</div>
|
|
28
16
|
</div>
|
|
29
17
|
|
|
30
18
|
<!-- Main Content -->
|
|
31
|
-
<div class="flex-1 min-h-0 overflow-
|
|
32
|
-
<div class="
|
|
33
|
-
<!--
|
|
34
|
-
<div class="
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
<
|
|
76
|
-
v-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
19
|
+
<div class="flex-1 min-h-0 overflow-y-auto">
|
|
20
|
+
<div class="max-w-7xl mx-auto p-6">
|
|
21
|
+
<!-- Stats Overview -->
|
|
22
|
+
<div class="grid grid-cols-2 md:grid-cols-6 gap-4 mb-6">
|
|
23
|
+
<StatCard
|
|
24
|
+
icon="i-lucide-git-branch"
|
|
25
|
+
:count="flows?.length || 0"
|
|
26
|
+
label="Total Flows"
|
|
27
|
+
variant="gray"
|
|
28
|
+
/>
|
|
29
|
+
<StatCard
|
|
30
|
+
icon="i-lucide-play"
|
|
31
|
+
:count="totalRuns"
|
|
32
|
+
label="Total Runs"
|
|
33
|
+
variant="blue"
|
|
34
|
+
/>
|
|
35
|
+
<StatCard
|
|
36
|
+
icon="i-lucide-check-circle"
|
|
37
|
+
:count="totalSuccess"
|
|
38
|
+
label="Success"
|
|
39
|
+
variant="emerald"
|
|
40
|
+
/>
|
|
41
|
+
<StatCard
|
|
42
|
+
icon="i-lucide-x-circle"
|
|
43
|
+
:count="totalFailure"
|
|
44
|
+
label="Failures"
|
|
45
|
+
variant="red"
|
|
46
|
+
/>
|
|
47
|
+
<StatCard
|
|
48
|
+
icon="i-lucide-loader"
|
|
49
|
+
:count="totalRunning"
|
|
50
|
+
label="Running"
|
|
51
|
+
variant="purple"
|
|
52
|
+
/>
|
|
53
|
+
<StatCard
|
|
54
|
+
icon="i-lucide-pause"
|
|
55
|
+
:count="totalAwaiting"
|
|
56
|
+
label="Awaiting"
|
|
57
|
+
variant="amber"
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<!-- Filters -->
|
|
62
|
+
<div class="mb-4">
|
|
63
|
+
<UInput
|
|
64
|
+
v-model="searchQuery"
|
|
65
|
+
icon="i-lucide-search"
|
|
66
|
+
placeholder="Search flows..."
|
|
67
|
+
size="sm"
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<!-- Flows List -->
|
|
72
|
+
<div
|
|
73
|
+
v-if="!filteredFlows || filteredFlows.length === 0"
|
|
74
|
+
class="bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 p-8 text-center text-gray-500"
|
|
75
|
+
>
|
|
76
|
+
<div v-if="searchQuery">
|
|
77
|
+
<UIcon
|
|
78
|
+
name="i-lucide-search-x"
|
|
79
|
+
class="w-12 h-12 animate-spin mx-auto mb-3 opacity-50"
|
|
80
|
+
/>
|
|
81
|
+
<p>No flows match your search</p>
|
|
82
|
+
<UButton
|
|
83
|
+
size="xs"
|
|
84
|
+
color="neutral"
|
|
85
|
+
variant="ghost"
|
|
86
|
+
class="mt-2"
|
|
87
|
+
@click="searchQuery = ''"
|
|
88
|
+
>
|
|
89
|
+
Clear Search
|
|
90
|
+
</UButton>
|
|
82
91
|
</div>
|
|
83
|
-
<div
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
<div class="text-center">
|
|
90
|
-
<div v-if="loadingRuns">
|
|
91
|
-
Loading runs...
|
|
92
|
-
</div>
|
|
93
|
-
<div v-else>
|
|
94
|
-
No runs yet
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
<template #fallback>
|
|
98
|
-
<div class="text-center">
|
|
99
|
-
No runs yet
|
|
100
|
-
</div>
|
|
101
|
-
</template>
|
|
102
|
-
</ClientOnly>
|
|
103
|
-
</div>
|
|
92
|
+
<div v-else>
|
|
93
|
+
<UIcon
|
|
94
|
+
name="i-lucide-git-branch"
|
|
95
|
+
class="w-12 h-12 mx-auto mb-3 opacity-50"
|
|
96
|
+
/>
|
|
97
|
+
<p>No flows found</p>
|
|
104
98
|
</div>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
>
|
|
99
|
+
</div>
|
|
100
|
+
<div
|
|
101
|
+
v-else
|
|
102
|
+
class="bg-white dark:bg-gray-900 rounded-lg border border-gray-200 dark:border-gray-800 overflow-hidden"
|
|
103
|
+
>
|
|
104
|
+
<div class="divide-y divide-gray-100 dark:divide-gray-800">
|
|
111
105
|
<div
|
|
112
|
-
v-for="
|
|
113
|
-
:key="
|
|
114
|
-
class="
|
|
106
|
+
v-for="flow in filteredFlows"
|
|
107
|
+
:key="flow.id"
|
|
108
|
+
class="px-6 py-4 hover:bg-gray-50 dark:hover:bg-gray-900/50 transition-colors cursor-pointer"
|
|
109
|
+
@click="openFlow(flow.id)"
|
|
115
110
|
>
|
|
116
|
-
<div
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
111
|
+
<div class="flex items-start justify-between gap-4">
|
|
112
|
+
<!-- Left: Flow Info -->
|
|
113
|
+
<div class="flex-1 min-w-0">
|
|
114
|
+
<div class="flex items-center gap-2 mb-2">
|
|
115
|
+
<UIcon
|
|
116
|
+
name="i-lucide-git-branch"
|
|
117
|
+
class="w-4 h-4 shrink-0 text-blue-500"
|
|
118
|
+
/>
|
|
119
|
+
<h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 truncate">
|
|
120
|
+
{{ flow.id }}
|
|
121
|
+
</h3>
|
|
122
|
+
<UBadge
|
|
123
|
+
v-if="flow.hasAwait"
|
|
124
|
+
label="await"
|
|
125
|
+
color="purple"
|
|
126
|
+
variant="subtle"
|
|
127
|
+
size="xs"
|
|
128
|
+
>
|
|
129
|
+
<template #leading>
|
|
130
|
+
<UIcon
|
|
131
|
+
name="i-lucide-pause"
|
|
132
|
+
class="w-3 h-3"
|
|
133
|
+
/>
|
|
134
|
+
</template>
|
|
135
|
+
</UBadge>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<div class="flex items-center gap-4 text-xs text-gray-500 dark:text-gray-400">
|
|
139
|
+
<div class="flex items-center gap-1">
|
|
140
|
+
<UIcon
|
|
141
|
+
name="i-lucide-layers"
|
|
142
|
+
class="w-3 h-3"
|
|
143
|
+
/>
|
|
144
|
+
<span>{{ getStepCount(flow) }} step{{ getStepCount(flow) === 1 ? "" : "s" }}</span>
|
|
125
145
|
</div>
|
|
126
|
-
<div class="flex items-center gap-
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
>
|
|
146
|
+
<div class="flex items-center gap-1">
|
|
147
|
+
<UIcon
|
|
148
|
+
name="i-lucide-bar-chart-3"
|
|
149
|
+
class="w-3 h-3"
|
|
150
|
+
/>
|
|
151
|
+
<span>{{ getLevelCount(flow) }} level{{ getLevelCount(flow) === 1 ? "" : "s" }}</span>
|
|
152
|
+
</div>
|
|
153
|
+
<div
|
|
154
|
+
v-if="flow.stats && flow.stats.total > 0"
|
|
155
|
+
class="flex items-center gap-2"
|
|
156
|
+
>
|
|
157
|
+
<div class="flex items-center gap-1">
|
|
135
158
|
<UIcon
|
|
136
|
-
name="i-lucide-
|
|
159
|
+
name="i-lucide-play"
|
|
137
160
|
class="w-3 h-3"
|
|
138
161
|
/>
|
|
139
|
-
<span>{{
|
|
162
|
+
<span>{{ flow.stats.total }}</span>
|
|
140
163
|
</div>
|
|
141
|
-
<!-- Duration (if completed) -->
|
|
142
164
|
<div
|
|
143
|
-
v-if="
|
|
144
|
-
class="
|
|
165
|
+
v-if="flow.stats.running > 0"
|
|
166
|
+
class="flex items-center gap-1 text-purple-600 dark:text-purple-400"
|
|
145
167
|
>
|
|
146
168
|
<UIcon
|
|
147
|
-
name="i-lucide-
|
|
169
|
+
name="i-lucide-loader"
|
|
148
170
|
class="w-3 h-3"
|
|
149
171
|
/>
|
|
150
|
-
<span>{{
|
|
172
|
+
<span>{{ flow.stats.running }}</span>
|
|
151
173
|
</div>
|
|
152
174
|
</div>
|
|
153
|
-
</div>
|
|
154
|
-
<!-- Status badge -->
|
|
155
|
-
<FlowRunStatusBadge
|
|
156
|
-
:is-running="r.status === 'running'"
|
|
157
|
-
:is-completed="r.status === 'completed'"
|
|
158
|
-
:is-failed="r.status === 'failed'"
|
|
159
|
-
:is-canceled="r.status === 'canceled'"
|
|
160
|
-
:is-stalled="r.status === 'stalled'"
|
|
161
|
-
/>
|
|
162
|
-
</div>
|
|
163
|
-
</div>
|
|
164
|
-
</div>
|
|
165
|
-
|
|
166
|
-
<!-- Loading indicator for infinite scroll -->
|
|
167
|
-
<div
|
|
168
|
-
v-if="loadingRuns"
|
|
169
|
-
class="px-4 py-3 text-center text-xs text-gray-400"
|
|
170
|
-
>
|
|
171
|
-
<UIcon
|
|
172
|
-
name="i-lucide-loader-2"
|
|
173
|
-
class="w-4 h-4 animate-spin inline-block"
|
|
174
|
-
/>
|
|
175
|
-
<span class="ml-2">Loading more runs...</span>
|
|
176
|
-
</div>
|
|
177
|
-
|
|
178
|
-
<!-- End of list indicator -->
|
|
179
|
-
<div
|
|
180
|
-
v-else-if="!hasMoreRuns && runs.length > 0"
|
|
181
|
-
class="px-4 py-3 text-center text-xs text-gray-400"
|
|
182
|
-
>
|
|
183
|
-
All runs loaded
|
|
184
|
-
</div>
|
|
185
|
-
</div>
|
|
186
|
-
|
|
187
|
-
<!-- Schedules Section -->
|
|
188
|
-
<div
|
|
189
|
-
v-if="selectedFlow"
|
|
190
|
-
class="border-t border-gray-200 dark:border-gray-800 shrink-0"
|
|
191
|
-
>
|
|
192
|
-
<UAccordion
|
|
193
|
-
:items="scheduleAccordionItems"
|
|
194
|
-
:ui="{
|
|
195
|
-
root: 'w-full',
|
|
196
|
-
trigger: 'px-4 py-2 bg-gray-50 dark:bg-gray-900/50',
|
|
197
|
-
content: 'max-h-48 overflow-y-auto'
|
|
198
|
-
}"
|
|
199
|
-
>
|
|
200
|
-
<template #item>
|
|
201
|
-
<FlowSchedulesList
|
|
202
|
-
v-if="selectedFlow"
|
|
203
|
-
ref="schedulesListRef"
|
|
204
|
-
:flow-name="selectedFlow"
|
|
205
|
-
class="px-4 py-3"
|
|
206
|
-
@updated="handleSchedulesUpdated"
|
|
207
|
-
/>
|
|
208
|
-
</template>
|
|
209
|
-
</UAccordion>
|
|
210
|
-
</div>
|
|
211
|
-
</div>
|
|
212
|
-
|
|
213
|
-
<!-- Main Content Area with Tabs -->
|
|
214
|
-
<div class="flex-1 min-w-0 bg-white dark:bg-gray-950 flex flex-col min-h-0 overflow-hidden">
|
|
215
|
-
<div class="px-4 py-2.5 border-b border-gray-200 dark:border-gray-800 shrink-0">
|
|
216
|
-
<div class="flex items-center justify-between">
|
|
217
|
-
<UTabs
|
|
218
|
-
v-model="mainTab"
|
|
219
|
-
:items="mainTabs"
|
|
220
|
-
size="xs"
|
|
221
|
-
:ui="{
|
|
222
|
-
root: 'gap-0',
|
|
223
|
-
trigger: 'px-2 py-0.5'
|
|
224
|
-
}"
|
|
225
|
-
/>
|
|
226
|
-
<div class="flex items-center gap-2">
|
|
227
|
-
<span
|
|
228
|
-
v-if="selectedRunId"
|
|
229
|
-
class="text-xs text-gray-500 flex items-center gap-2"
|
|
230
|
-
>
|
|
231
|
-
<span>Run: {{ selectedRunId.substring(0, 8) }}...</span>
|
|
232
|
-
<div
|
|
233
|
-
v-if="isReconnecting || isConnected && flowState.isRunning.value"
|
|
234
|
-
class="flex items-center gap-1.5"
|
|
235
|
-
>
|
|
236
175
|
<div
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
176
|
+
v-if="flow.timeout"
|
|
177
|
+
class="flex items-center gap-1"
|
|
178
|
+
>
|
|
179
|
+
<UIcon
|
|
180
|
+
name="i-lucide-clock"
|
|
181
|
+
class="w-3 h-3"
|
|
182
|
+
/>
|
|
183
|
+
<span>{{ formatTimeout(flow.timeout) }} timeout</span>
|
|
184
|
+
</div>
|
|
185
|
+
<!-- Runtime Badges -->
|
|
186
|
+
<div
|
|
187
|
+
v-if="getRuntimes(flow).length > 0"
|
|
188
|
+
class="flex items-center gap-1"
|
|
189
|
+
>
|
|
190
|
+
<UIcon
|
|
191
|
+
name="i-lucide-cpu"
|
|
192
|
+
class="w-3 h-3"
|
|
193
|
+
/>
|
|
194
|
+
<span>{{ getRuntimes(flow).join(", ") }}</span>
|
|
195
|
+
</div>
|
|
241
196
|
</div>
|
|
242
|
-
</span>
|
|
243
|
-
<span
|
|
244
|
-
v-else-if="selectedFlow"
|
|
245
|
-
class="text-xs font-mono text-gray-500"
|
|
246
|
-
>
|
|
247
|
-
{{ selectedFlow }}
|
|
248
|
-
</span>
|
|
249
|
-
</div>
|
|
250
|
-
</div>
|
|
251
|
-
</div>
|
|
252
|
-
<div class="flex-1 min-h-0">
|
|
253
|
-
<!-- Diagram Tab -->
|
|
254
|
-
<div
|
|
255
|
-
v-if="mainTab === 'diagram'"
|
|
256
|
-
class="h-full"
|
|
257
|
-
>
|
|
258
|
-
<div
|
|
259
|
-
v-if="!selectedFlow"
|
|
260
|
-
class="h-full flex items-center justify-center text-sm text-gray-400"
|
|
261
|
-
>
|
|
262
|
-
Select a flow to view diagram
|
|
263
|
-
</div>
|
|
264
|
-
<FlowDiagram
|
|
265
|
-
v-else
|
|
266
|
-
:flow="selectedFlowMeta"
|
|
267
|
-
:show-controls="true"
|
|
268
|
-
:show-background="true"
|
|
269
|
-
:step-states="diagramStepStates"
|
|
270
|
-
:flow-status="runSnapshot.status"
|
|
271
|
-
height-class="h-full"
|
|
272
|
-
@node-action="handleNodeAction"
|
|
273
|
-
/>
|
|
274
|
-
</div>
|
|
275
|
-
|
|
276
|
-
<!-- Timeline Tab -->
|
|
277
|
-
<div
|
|
278
|
-
v-else-if="mainTab === 'timeline'"
|
|
279
|
-
class="h-full flex gap-px bg-gray-200 dark:bg-gray-800"
|
|
280
|
-
>
|
|
281
|
-
<!-- Left: Overview -->
|
|
282
|
-
<div class="flex-1 min-w-0 max-w-[50%] bg-white dark:bg-gray-950 flex flex-col min-h-0 overflow-hidden">
|
|
283
|
-
<div class="flex-1 overflow-y-auto min-h-0">
|
|
284
|
-
<FlowRunOverview
|
|
285
|
-
:run-status="runSnapshot.status"
|
|
286
|
-
:started-at="runSnapshot.startedAt"
|
|
287
|
-
:completed-at="runSnapshot.completedAt"
|
|
288
|
-
:steps="flowState.stepList.value"
|
|
289
|
-
:flow-name="selectedFlow"
|
|
290
|
-
:run-id="selectedRunId"
|
|
291
|
-
@select-step="handleSelectStep"
|
|
292
|
-
@cancel-flow="handleCancelFlow"
|
|
293
|
-
/>
|
|
294
197
|
</div>
|
|
295
|
-
</div>
|
|
296
198
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
199
|
+
<!-- Right: Action -->
|
|
200
|
+
<UButton
|
|
201
|
+
icon="i-lucide-arrow-right"
|
|
202
|
+
size="xs"
|
|
203
|
+
color="neutral"
|
|
204
|
+
variant="ghost"
|
|
205
|
+
square
|
|
304
206
|
/>
|
|
305
207
|
</div>
|
|
306
208
|
</div>
|
|
307
209
|
</div>
|
|
308
210
|
</div>
|
|
309
|
-
</div>
|
|
310
|
-
</div>
|
|
311
211
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
Start Flow Run
|
|
319
|
-
</h3>
|
|
320
|
-
<p class="text-sm text-gray-500 mt-1">
|
|
321
|
-
{{ selectedFlow }}
|
|
322
|
-
</p>
|
|
323
|
-
</div>
|
|
324
|
-
</div>
|
|
325
|
-
</template>
|
|
326
|
-
<template #body>
|
|
327
|
-
<div class="space-y-4">
|
|
328
|
-
<div>
|
|
329
|
-
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
|
330
|
-
Input Data (JSON)
|
|
331
|
-
</label>
|
|
332
|
-
<UTextarea
|
|
333
|
-
v-model="flowInputJson"
|
|
334
|
-
:rows="12"
|
|
335
|
-
placeholder="{\n "key": "value"\n}"
|
|
336
|
-
class="w-full font-mono text-sm"
|
|
337
|
-
/>
|
|
338
|
-
<p
|
|
339
|
-
v-if="jsonError"
|
|
340
|
-
class="text-xs text-red-500 mt-2"
|
|
341
|
-
>
|
|
342
|
-
{{ jsonError }}
|
|
343
|
-
</p>
|
|
344
|
-
</div>
|
|
212
|
+
<!-- Footer Info -->
|
|
213
|
+
<div
|
|
214
|
+
v-if="filteredFlows && filteredFlows.length > 0"
|
|
215
|
+
class="mt-4 text-center text-sm text-gray-500 dark:text-gray-400"
|
|
216
|
+
>
|
|
217
|
+
Showing {{ filteredFlows.length }} flow{{ filteredFlows.length === 1 ? "" : "s" }}
|
|
345
218
|
</div>
|
|
346
|
-
</
|
|
347
|
-
|
|
348
|
-
<div class="flex justify-end gap-2">
|
|
349
|
-
<UButton
|
|
350
|
-
color="neutral"
|
|
351
|
-
variant="ghost"
|
|
352
|
-
@click="startFlowModalOpen = false"
|
|
353
|
-
>
|
|
354
|
-
Cancel
|
|
355
|
-
</UButton>
|
|
356
|
-
<UButton
|
|
357
|
-
color="primary"
|
|
358
|
-
:loading="startingFlow"
|
|
359
|
-
:disabled="!!jsonError"
|
|
360
|
-
@click="startFlowRun"
|
|
361
|
-
>
|
|
362
|
-
Start Flow
|
|
363
|
-
</UButton>
|
|
364
|
-
</div>
|
|
365
|
-
</template>
|
|
366
|
-
</UModal>
|
|
367
|
-
|
|
368
|
-
<!-- Schedule Flow Modal -->
|
|
369
|
-
<FlowScheduleDialog
|
|
370
|
-
v-if="selectedFlow"
|
|
371
|
-
v-model="scheduleModalOpen"
|
|
372
|
-
:flow-name="selectedFlow"
|
|
373
|
-
@scheduled="handleFlowScheduled"
|
|
374
|
-
/>
|
|
375
|
-
|
|
376
|
-
<!-- Confirm Dialog -->
|
|
377
|
-
<ConfirmDialog
|
|
378
|
-
v-model:open="confirmDialogOpen"
|
|
379
|
-
:title="confirmDialogConfig.title"
|
|
380
|
-
:description="confirmDialogConfig.description"
|
|
381
|
-
:items="confirmDialogConfig.items"
|
|
382
|
-
:warning="confirmDialogConfig.warning"
|
|
383
|
-
:loading="clearingHistory"
|
|
384
|
-
confirm-label="Clear History"
|
|
385
|
-
confirm-color="error"
|
|
386
|
-
icon="i-lucide-trash-2"
|
|
387
|
-
icon-color="error"
|
|
388
|
-
@confirm="confirmDialogConfig.onConfirm"
|
|
389
|
-
/>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
390
221
|
</div>
|
|
391
222
|
</template>
|
|
392
223
|
|
|
393
224
|
<script setup>
|
|
394
|
-
import { ref, computed,
|
|
395
|
-
import
|
|
396
|
-
import
|
|
397
|
-
import
|
|
398
|
-
import
|
|
399
|
-
import
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
label: "Timeline",
|
|
423
|
-
value: "timeline",
|
|
424
|
-
icon: "i-lucide-activity",
|
|
425
|
-
disabled: !selectedRunId.value
|
|
225
|
+
import { ref, computed, onMounted, onBeforeUnmount } from "#imports";
|
|
226
|
+
import { UButton, UIcon, UBadge, UInput } from "#components";
|
|
227
|
+
import { useComponentRouter } from "../../composables/useComponentRouter";
|
|
228
|
+
import { useFlowWebSocket } from "../../composables/useFlowWebSocket";
|
|
229
|
+
import StatCard from "../../components/StatCard.vue";
|
|
230
|
+
import LiveIndicator from "../../components/LiveIndicator.vue";
|
|
231
|
+
const router = useComponentRouter();
|
|
232
|
+
const flows = ref([]);
|
|
233
|
+
const loading = ref(true);
|
|
234
|
+
const flowWs = useFlowWebSocket();
|
|
235
|
+
async function fetchAnalyzedFlows() {
|
|
236
|
+
try {
|
|
237
|
+
const data = await $fetch("/api/_flows");
|
|
238
|
+
const analyzedFlows = data;
|
|
239
|
+
flows.value = analyzedFlows.map((flow) => ({
|
|
240
|
+
id: flow.id,
|
|
241
|
+
entry: flow.entry,
|
|
242
|
+
steps: flow.analyzed?.steps || flow.steps || {},
|
|
243
|
+
levels: flow.analyzed?.levels || [],
|
|
244
|
+
maxLevel: flow.analyzed?.maxLevel || 0,
|
|
245
|
+
stallTimeout: flow.analyzed?.stallTimeout,
|
|
246
|
+
awaitPatterns: flow.analyzed?.awaitPatterns,
|
|
247
|
+
hasAwait: flow.analyzed?.awaitPatterns?.steps?.length > 0 || false,
|
|
248
|
+
timeout: flow.timeout,
|
|
249
|
+
stats: { total: 0, success: 0, failure: 0, cancel: 0, running: 0, awaiting: 0 }
|
|
250
|
+
}));
|
|
251
|
+
} catch (err) {
|
|
252
|
+
console.error("Error fetching analyzed flows:", err);
|
|
426
253
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
254
|
+
}
|
|
255
|
+
function updateFlowStats(data) {
|
|
256
|
+
const flowId = data?.id;
|
|
257
|
+
if (!flowId || !flows.value)
|
|
258
|
+
return;
|
|
259
|
+
const flowIndex = flows.value.findIndex((f) => f.id === flowId);
|
|
260
|
+
if (flowIndex === -1) {
|
|
261
|
+
console.warn("[Flow Stats] Flow not found in list:", flowId);
|
|
262
|
+
return;
|
|
433
263
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
total: totalRuns,
|
|
439
|
-
loading: loadingRuns,
|
|
440
|
-
hasMore: hasMoreRuns,
|
|
441
|
-
loadMore: loadMoreRuns,
|
|
442
|
-
refresh: refreshRuns,
|
|
443
|
-
checkForNewRuns
|
|
444
|
-
} = useFlowRunsInfinite(selectedFlow);
|
|
445
|
-
const { flowState, isConnected, isReconnecting } = useFlowRunTimeline(selectedFlow, selectedRunId);
|
|
446
|
-
const shouldPoll = computed(() => !!selectedFlow.value);
|
|
447
|
-
useFlowRunsPolling(checkForNewRuns, shouldPoll);
|
|
448
|
-
const startFlowModalOpen = ref(false);
|
|
449
|
-
const scheduleModalOpen = ref(false);
|
|
450
|
-
const flowInputJson = ref("{}");
|
|
451
|
-
const jsonError = ref("");
|
|
452
|
-
const startingFlow = ref(false);
|
|
453
|
-
const schedulesListRef = ref();
|
|
454
|
-
const clearingHistory = ref(false);
|
|
455
|
-
const scheduleAccordionItems = [
|
|
456
|
-
{
|
|
457
|
-
label: "Schedules",
|
|
458
|
-
slot: "item",
|
|
459
|
-
defaultOpen: false
|
|
264
|
+
const metadata = data?.metadata;
|
|
265
|
+
if (!metadata) {
|
|
266
|
+
console.warn("[Flow Stats] No metadata in update:", data);
|
|
267
|
+
return;
|
|
460
268
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
269
|
+
const stats = metadata.stats || {
|
|
270
|
+
total: metadata["stats.total"] || 0,
|
|
271
|
+
success: metadata["stats.success"] || 0,
|
|
272
|
+
failure: metadata["stats.failure"] || 0,
|
|
273
|
+
cancel: metadata["stats.cancel"] || 0,
|
|
274
|
+
running: metadata["stats.running"] || 0,
|
|
275
|
+
awaiting: metadata["stats.awaiting"] || 0
|
|
276
|
+
};
|
|
277
|
+
flows.value[flowIndex] = {
|
|
278
|
+
...flows.value[flowIndex],
|
|
279
|
+
stats: {
|
|
280
|
+
total: stats.total || 0,
|
|
281
|
+
success: stats.success || 0,
|
|
282
|
+
failure: stats.failure || 0,
|
|
283
|
+
cancel: stats.cancel || 0,
|
|
284
|
+
running: stats.running || 0,
|
|
285
|
+
awaiting: stats.awaiting || 0
|
|
286
|
+
},
|
|
287
|
+
lastRunAt: metadata.lastRunAt,
|
|
288
|
+
lastCompletedAt: metadata.lastCompletedAt
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
onMounted(async () => {
|
|
292
|
+
await fetchAnalyzedFlows();
|
|
293
|
+
if (import.meta.client) {
|
|
294
|
+
flowWs.subscribeStats(
|
|
295
|
+
{
|
|
296
|
+
onInitial: (data) => {
|
|
297
|
+
updateFlowStats(data);
|
|
298
|
+
},
|
|
299
|
+
onUpdate: (data) => {
|
|
300
|
+
updateFlowStats(data);
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
autoReconnect: true,
|
|
305
|
+
onOpen: () => {
|
|
306
|
+
loading.value = false;
|
|
307
|
+
},
|
|
308
|
+
onError: (err) => {
|
|
309
|
+
console.error("[Flow Stats] Error:", err);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
);
|
|
469
313
|
}
|
|
470
314
|
});
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
icon: "i-lucide-clock",
|
|
475
|
-
onSelect: () => openScheduleModal()
|
|
476
|
-
},
|
|
477
|
-
{
|
|
478
|
-
label: "Clear History",
|
|
479
|
-
icon: "i-lucide-trash-2",
|
|
480
|
-
disabled: clearingHistory.value,
|
|
481
|
-
onSelect: () => confirmClearHistory()
|
|
482
|
-
}
|
|
483
|
-
]]);
|
|
484
|
-
watch(flowInputJson, (value) => {
|
|
485
|
-
try {
|
|
486
|
-
JSON.parse(value);
|
|
487
|
-
jsonError.value = "";
|
|
488
|
-
} catch (err) {
|
|
489
|
-
jsonError.value = err instanceof Error ? err.message : "Invalid JSON";
|
|
490
|
-
}
|
|
315
|
+
onBeforeUnmount(() => {
|
|
316
|
+
flowWs.unsubscribeStats();
|
|
317
|
+
flowWs.stop();
|
|
491
318
|
});
|
|
492
|
-
const
|
|
493
|
-
const
|
|
494
|
-
if (!
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
}
|
|
502
|
-
};
|
|
503
|
-
const formatTime = (timestamp) => {
|
|
504
|
-
const date = new Date(timestamp);
|
|
505
|
-
const now = /* @__PURE__ */ new Date();
|
|
506
|
-
const diff = now.getTime() - date.getTime();
|
|
507
|
-
const seconds = Math.floor(diff / 1e3);
|
|
508
|
-
const minutes = Math.floor(seconds / 60);
|
|
509
|
-
const hours = Math.floor(minutes / 60);
|
|
510
|
-
const days = Math.floor(hours / 24);
|
|
511
|
-
if (days > 0) return `${days}d ago`;
|
|
512
|
-
if (hours > 0) return `${hours}h ago`;
|
|
513
|
-
if (minutes > 0) return `${minutes}m ago`;
|
|
514
|
-
if (seconds > 10) return `${seconds}s ago`;
|
|
515
|
-
return "just now";
|
|
516
|
-
};
|
|
517
|
-
const formatDuration = (start, end) => {
|
|
518
|
-
const startTime = new Date(start).getTime();
|
|
519
|
-
const endTime = new Date(end).getTime();
|
|
520
|
-
const diff = endTime - startTime;
|
|
521
|
-
const seconds = Math.floor(diff / 1e3);
|
|
522
|
-
const minutes = Math.floor(seconds / 60);
|
|
523
|
-
const hours = Math.floor(minutes / 60);
|
|
524
|
-
if (hours > 0) {
|
|
525
|
-
const remainingMinutes = minutes % 60;
|
|
526
|
-
return `${hours}h ${remainingMinutes}m`;
|
|
527
|
-
}
|
|
528
|
-
if (minutes > 0) {
|
|
529
|
-
const remainingSeconds = seconds % 60;
|
|
530
|
-
return `${minutes}m ${remainingSeconds}s`;
|
|
319
|
+
const searchQuery = ref("");
|
|
320
|
+
const filteredFlows = computed(() => {
|
|
321
|
+
if (!flows.value)
|
|
322
|
+
return [];
|
|
323
|
+
if (searchQuery.value) {
|
|
324
|
+
const query = searchQuery.value.toLowerCase();
|
|
325
|
+
return flows.value.filter(
|
|
326
|
+
(flow) => flow.id.toLowerCase().includes(query)
|
|
327
|
+
);
|
|
531
328
|
}
|
|
532
|
-
return
|
|
533
|
-
};
|
|
534
|
-
const runSnapshot = computed(() => {
|
|
535
|
-
const state = flowState.state.value;
|
|
536
|
-
return {
|
|
537
|
-
status: state.status,
|
|
538
|
-
startedAt: state.startedAt,
|
|
539
|
-
completedAt: state.completedAt,
|
|
540
|
-
logsCount: state.logs.length,
|
|
541
|
-
lastLogLevel: state.logs.length > 0 ? state.logs[state.logs.length - 1]?.level : void 0
|
|
542
|
-
};
|
|
329
|
+
return flows.value;
|
|
543
330
|
});
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
return events.filter((e) => e.stepName === selectedStepKey.value);
|
|
331
|
+
const totalRuns = computed(() => {
|
|
332
|
+
if (!flows.value)
|
|
333
|
+
return 0;
|
|
334
|
+
return flows.value.reduce((acc, flow) => acc + (flow.stats?.total || 0), 0);
|
|
549
335
|
});
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
return
|
|
336
|
+
const totalSuccess = computed(() => {
|
|
337
|
+
if (!flows.value)
|
|
338
|
+
return 0;
|
|
339
|
+
return flows.value.reduce((acc, flow) => acc + (flow.stats?.success || 0), 0);
|
|
554
340
|
});
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
return
|
|
341
|
+
const totalFailure = computed(() => {
|
|
342
|
+
if (!flows.value)
|
|
343
|
+
return 0;
|
|
344
|
+
return flows.value.reduce((acc, flow) => acc + (flow.stats?.failure || 0), 0);
|
|
559
345
|
});
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
if (!selectedFlow.value || !selectedRunId.value) return;
|
|
565
|
-
try {
|
|
566
|
-
await $fetch(`/api/_flows/${selectedFlow.value}/runs/${selectedRunId.value}/cancel`, {
|
|
567
|
-
method: "POST"
|
|
568
|
-
});
|
|
569
|
-
console.log("Flow canceled successfully");
|
|
570
|
-
} catch (error) {
|
|
571
|
-
console.error("Failed to cancel flow:", error);
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
const diagramStepStates = computed(() => {
|
|
575
|
-
if (!selectedRunId.value) return void 0;
|
|
576
|
-
return flowState.state.value.steps;
|
|
346
|
+
const totalRunning = computed(() => {
|
|
347
|
+
if (!flows.value)
|
|
348
|
+
return 0;
|
|
349
|
+
return flows.value.reduce((acc, flow) => acc + (flow.stats?.running || 0), 0);
|
|
577
350
|
});
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
a.href = url;
|
|
587
|
-
a.download = `flow-${selectedFlow.value}-${selectedRunId.value}-events-${(/* @__PURE__ */ new Date()).toISOString()}.json`;
|
|
588
|
-
document.body.appendChild(a);
|
|
589
|
-
a.click();
|
|
590
|
-
a.remove();
|
|
591
|
-
URL.revokeObjectURL(url);
|
|
592
|
-
};
|
|
593
|
-
const handleNodeAction = async (payload) => {
|
|
594
|
-
const _stepName = payload.id.split(":")[1];
|
|
595
|
-
if (!selectedRunId.value) {
|
|
596
|
-
console.log("[flows/index] No run selected, showing alert");
|
|
597
|
-
alert("Please select a flow run first to view logs or details.");
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
mainTab.value = "timeline";
|
|
601
|
-
};
|
|
602
|
-
const openStartFlowModal = () => {
|
|
603
|
-
flowInputJson.value = "{}";
|
|
604
|
-
jsonError.value = "";
|
|
605
|
-
startFlowModalOpen.value = true;
|
|
606
|
-
};
|
|
607
|
-
const openScheduleModal = () => {
|
|
608
|
-
scheduleModalOpen.value = true;
|
|
609
|
-
};
|
|
610
|
-
const handleFlowScheduled = () => {
|
|
611
|
-
schedulesListRef.value?.loadSchedules();
|
|
612
|
-
};
|
|
613
|
-
const handleSchedulesUpdated = () => {
|
|
614
|
-
schedulesListRef.value?.loadSchedules();
|
|
351
|
+
const totalAwaiting = computed(() => {
|
|
352
|
+
if (!flows.value)
|
|
353
|
+
return 0;
|
|
354
|
+
return flows.value.reduce((acc, flow) => acc + (flow.stats?.awaiting || 0), 0);
|
|
355
|
+
});
|
|
356
|
+
const getStepCount = (flow) => {
|
|
357
|
+
if (!flow.steps) return 0;
|
|
358
|
+
return Object.keys(flow.steps).length;
|
|
615
359
|
};
|
|
616
|
-
const
|
|
617
|
-
if (!
|
|
618
|
-
|
|
619
|
-
startingFlow.value = true;
|
|
620
|
-
const input = JSON.parse(flowInputJson.value);
|
|
621
|
-
const result = await $fetch(`/api/_flows/${encodeURIComponent(selectedFlow.value)}/start`, {
|
|
622
|
-
method: "POST",
|
|
623
|
-
body: input
|
|
624
|
-
});
|
|
625
|
-
startFlowModalOpen.value = false;
|
|
626
|
-
flowInputJson.value = "{}";
|
|
627
|
-
if (result?.flowId) {
|
|
628
|
-
selectedRunId.value = result.flowId;
|
|
629
|
-
mainTab.value = "timeline";
|
|
630
|
-
}
|
|
631
|
-
await refreshRuns();
|
|
632
|
-
} catch (err) {
|
|
633
|
-
console.error("Failed to start flow:", err);
|
|
634
|
-
jsonError.value = err instanceof Error ? err.message : "Failed to start flow";
|
|
635
|
-
} finally {
|
|
636
|
-
startingFlow.value = false;
|
|
637
|
-
}
|
|
360
|
+
const getLevelCount = (flow) => {
|
|
361
|
+
if (!flow.levels) return 0;
|
|
362
|
+
return Array.isArray(flow.levels) ? flow.levels.length : 0;
|
|
638
363
|
};
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
"All flow run events",
|
|
646
|
-
"All flow run logs",
|
|
647
|
-
"The runs index"
|
|
648
|
-
],
|
|
649
|
-
warning: "This action cannot be undone.",
|
|
650
|
-
onConfirm: () => {
|
|
651
|
-
clearFlowHistory();
|
|
364
|
+
const getRuntimes = (flow) => {
|
|
365
|
+
const runtimes = /* @__PURE__ */ new Set();
|
|
366
|
+
if (!flow.steps) return [];
|
|
367
|
+
for (const step of Object.values(flow.steps)) {
|
|
368
|
+
if (step.runtime) {
|
|
369
|
+
runtimes.add(step.runtime);
|
|
652
370
|
}
|
|
653
|
-
};
|
|
654
|
-
confirmDialogOpen.value = true;
|
|
655
|
-
};
|
|
656
|
-
const clearFlowHistory = async () => {
|
|
657
|
-
if (!selectedFlow.value) return;
|
|
658
|
-
try {
|
|
659
|
-
clearingHistory.value = true;
|
|
660
|
-
await $fetch(`/api/_flows/${encodeURIComponent(selectedFlow.value)}/clear-history`, {
|
|
661
|
-
method: "DELETE"
|
|
662
|
-
});
|
|
663
|
-
confirmDialogOpen.value = false;
|
|
664
|
-
selectedRunId.value = "";
|
|
665
|
-
mainTab.value = "diagram";
|
|
666
|
-
await refreshRuns();
|
|
667
|
-
console.log(`Successfully cleared history for "${selectedFlow.value}"`);
|
|
668
|
-
} catch (err) {
|
|
669
|
-
console.error("Failed to clear history:", err);
|
|
670
|
-
confirmDialogConfig.value = {
|
|
671
|
-
title: "Error Clearing History",
|
|
672
|
-
description: `Failed to clear history: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
673
|
-
items: [],
|
|
674
|
-
warning: "",
|
|
675
|
-
onConfirm: () => {
|
|
676
|
-
confirmDialogOpen.value = false;
|
|
677
|
-
}
|
|
678
|
-
};
|
|
679
|
-
} finally {
|
|
680
|
-
clearingHistory.value = false;
|
|
681
371
|
}
|
|
372
|
+
return Array.from(runtimes);
|
|
682
373
|
};
|
|
374
|
+
function formatTimeout(ms) {
|
|
375
|
+
if (ms < 1e3)
|
|
376
|
+
return `${ms}ms`;
|
|
377
|
+
if (ms < 6e4)
|
|
378
|
+
return `${ms / 1e3}s`;
|
|
379
|
+
return `${ms / 6e4}m`;
|
|
380
|
+
}
|
|
381
|
+
function openFlow(flowId) {
|
|
382
|
+
router.push(`/flows/${flowId}`);
|
|
383
|
+
}
|
|
683
384
|
</script>
|