@nvent-addon/app 0.5.14 → 1.0.0-alpha.1
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.json +1 -1
- package/dist/module.mjs +3 -2
- package/dist/runtime/app/components/DashboardCard.d.vue.ts +1 -1
- package/dist/runtime/app/components/DashboardCard.vue.d.ts +1 -1
- package/dist/runtime/app/composables/useWorkers.d.ts +57 -0
- package/dist/runtime/app/composables/useWorkers.js +42 -0
- package/dist/runtime/app/pages/dashboard.vue +1 -654
- package/dist/runtime/app/pages/index.vue +25 -41
- package/dist/runtime/app/pages/workers.vue +458 -0
- package/dist/runtime/server/api/_workers/index.get.d.ts +8 -0
- package/dist/runtime/server/api/_workers/index.get.js +14 -0
- package/package.json +12 -11
- package/dist/runtime/app/components/ComponentRouter.d.vue.ts +0 -46
- package/dist/runtime/app/components/ComponentRouter.vue +0 -26
- package/dist/runtime/app/components/ComponentRouter.vue.d.ts +0 -46
- package/dist/runtime/app/components/ComponentShell.d.vue.ts +0 -23
- package/dist/runtime/app/components/ComponentShell.vue +0 -97
- package/dist/runtime/app/components/ComponentShell.vue.d.ts +0 -23
- package/dist/runtime/app/components/ConfirmDialog.d.vue.ts +0 -33
- package/dist/runtime/app/components/ConfirmDialog.vue +0 -120
- package/dist/runtime/app/components/ConfirmDialog.vue.d.ts +0 -33
- package/dist/runtime/app/composables/useComponentRouter.d.ts +0 -46
- package/dist/runtime/app/composables/useComponentRouter.js +0 -248
- package/dist/runtime/app/pages/flows/[name].vue +0 -750
- package/dist/runtime/app/pages/flows/index.d.vue.ts +0 -3
- package/dist/runtime/app/pages/flows/index.vue +0 -381
- package/dist/runtime/app/pages/flows/index.vue.d.ts +0 -3
- package/dist/runtime/app/pages/queues/index.d.vue.ts +0 -3
- package/dist/runtime/app/pages/queues/index.vue +0 -236
- package/dist/runtime/app/pages/queues/index.vue.d.ts +0 -3
- package/dist/runtime/app/pages/queues/job.d.vue.ts +0 -3
- package/dist/runtime/app/pages/queues/job.vue +0 -261
- package/dist/runtime/app/pages/queues/job.vue.d.ts +0 -3
- package/dist/runtime/app/pages/queues/jobs.d.vue.ts +0 -3
- package/dist/runtime/app/pages/queues/jobs.vue +0 -595
- package/dist/runtime/app/pages/queues/jobs.vue.d.ts +0 -3
- package/dist/runtime/app/pages/settings/scheduler.d.vue.ts +0 -3
- package/dist/runtime/app/pages/settings/scheduler.vue +0 -310
- package/dist/runtime/app/pages/settings/scheduler.vue.d.ts +0 -3
- package/dist/runtime/app/pages/triggers/[name]/edit.d.vue.ts +0 -3
- package/dist/runtime/app/pages/triggers/[name]/edit.vue +0 -429
- package/dist/runtime/app/pages/triggers/[name]/edit.vue.d.ts +0 -3
- package/dist/runtime/app/pages/triggers/[name].d.vue.ts +0 -3
- package/dist/runtime/app/pages/triggers/[name].vue +0 -870
- package/dist/runtime/app/pages/triggers/[name].vue.d.ts +0 -3
- package/dist/runtime/app/pages/triggers/index.d.vue.ts +0 -3
- package/dist/runtime/app/pages/triggers/index.vue +0 -525
- package/dist/runtime/app/pages/triggers/index.vue.d.ts +0 -3
- package/dist/runtime/app/pages/triggers/new.d.vue.ts +0 -3
- package/dist/runtime/app/pages/triggers/new.vue +0 -610
- package/dist/runtime/app/pages/triggers/new.vue.d.ts +0 -3
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.d.ts +0 -10
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.js +0 -49
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.js +0 -21
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/restart.post.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/restart.post.js +0 -21
- package/dist/runtime/server/api/_flows/[name]/runs.get.d.ts +0 -17
- package/dist/runtime/server/api/_flows/[name]/runs.get.js +0 -64
- package/dist/runtime/server/api/_flows/[name]/start.post.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/start.post.js +0 -9
- package/dist/runtime/server/api/_flows/index.get.d.ts +0 -7
- package/dist/runtime/server/api/_flows/index.get.js +0 -5
- package/dist/runtime/server/api/_flows/recent-runs.get.d.ts +0 -15
- package/dist/runtime/server/api/_flows/recent-runs.get.js +0 -67
- package/dist/runtime/server/api/_flows/ws.d.ts +0 -80
- package/dist/runtime/server/api/_flows/ws.js +0 -309
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.d.ts +0 -2
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.js +0 -14
- package/dist/runtime/server/api/_queues/[name]/job/index.get.d.ts +0 -2
- package/dist/runtime/server/api/_queues/[name]/job/index.get.js +0 -39
- package/dist/runtime/server/api/_queues/index.get.d.ts +0 -2
- package/dist/runtime/server/api/_queues/index.get.js +0 -106
- package/dist/runtime/server/api/_queues/ws.d.ts +0 -48
- package/dist/runtime/server/api/_queues/ws.js +0 -215
- package/dist/runtime/server/api/_scheduler/jobs.get.d.ts +0 -19
- package/dist/runtime/server/api/_scheduler/jobs.get.js +0 -36
- package/dist/runtime/server/api/_triggers/[name]/events.get.d.ts +0 -6
- package/dist/runtime/server/api/_triggers/[name]/events.get.js +0 -43
- package/dist/runtime/server/api/_triggers/[name]/index.get.d.ts +0 -6
- package/dist/runtime/server/api/_triggers/[name]/index.get.js +0 -76
- package/dist/runtime/server/api/_triggers/[name].delete.d.ts +0 -7
- package/dist/runtime/server/api/_triggers/[name].delete.js +0 -37
- package/dist/runtime/server/api/_triggers/[name].patch.d.ts +0 -7
- package/dist/runtime/server/api/_triggers/[name].patch.js +0 -117
- package/dist/runtime/server/api/_triggers/index.get.d.ts +0 -6
- package/dist/runtime/server/api/_triggers/index.get.js +0 -44
- package/dist/runtime/server/api/_triggers/index.post.d.ts +0 -7
- package/dist/runtime/server/api/_triggers/index.post.js +0 -124
- package/dist/runtime/server/api/_triggers/stats.get.d.ts +0 -6
- package/dist/runtime/server/api/_triggers/stats.get.js +0 -41
- package/dist/runtime/server/api/_triggers/ws.d.ts +0 -74
- package/dist/runtime/server/api/_triggers/ws.js +0 -315
- /package/dist/runtime/app/pages/{flows/[name].d.vue.ts → workers.d.vue.ts} +0 -0
- /package/dist/runtime/app/pages/{flows/[name].vue.d.ts → workers.vue.d.ts} +0 -0
|
@@ -1,750 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="h-full flex flex-col overflow-hidden">
|
|
3
|
-
<!-- Header -->
|
|
4
|
-
<div class="border-b border-gray-200 dark:border-gray-800 px-6 py-3 shrink-0">
|
|
5
|
-
<div class="flex items-center justify-between">
|
|
6
|
-
<div class="flex items-center gap-3">
|
|
7
|
-
<UButton
|
|
8
|
-
icon="i-lucide-arrow-left"
|
|
9
|
-
size="xs"
|
|
10
|
-
color="neutral"
|
|
11
|
-
variant="ghost"
|
|
12
|
-
square
|
|
13
|
-
@click="goBack"
|
|
14
|
-
/>
|
|
15
|
-
<div>
|
|
16
|
-
<h1 class="text-lg font-semibold flex items-center gap-2">
|
|
17
|
-
<UIcon
|
|
18
|
-
name="i-lucide-git-branch"
|
|
19
|
-
class="w-5 h-5 text-blue-500"
|
|
20
|
-
/>
|
|
21
|
-
<span>{{ selectedFlow }}</span>
|
|
22
|
-
</h1>
|
|
23
|
-
<div
|
|
24
|
-
v-if="selectedFlowMeta"
|
|
25
|
-
class="flex items-center gap-2 mt-1"
|
|
26
|
-
>
|
|
27
|
-
<UBadge
|
|
28
|
-
v-if="selectedFlowMeta.hasAwait"
|
|
29
|
-
label="await"
|
|
30
|
-
color="secondary"
|
|
31
|
-
variant="subtle"
|
|
32
|
-
size="xs"
|
|
33
|
-
/>
|
|
34
|
-
<UBadge
|
|
35
|
-
v-if="selectedFlowMeta.stepCount"
|
|
36
|
-
:label="`${selectedFlowMeta.stepCount} steps`"
|
|
37
|
-
color="primary"
|
|
38
|
-
variant="subtle"
|
|
39
|
-
size="xs"
|
|
40
|
-
/>
|
|
41
|
-
<UBadge
|
|
42
|
-
v-if="selectedFlowMeta.levelCount"
|
|
43
|
-
:label="`${selectedFlowMeta.levelCount} levels`"
|
|
44
|
-
color="neutral"
|
|
45
|
-
variant="subtle"
|
|
46
|
-
size="xs"
|
|
47
|
-
/>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
<div class="flex items-center gap-3">
|
|
52
|
-
<!-- Flow actions can go here -->
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
|
|
57
|
-
<!-- Main Content -->
|
|
58
|
-
<div class="flex-1 min-h-0 overflow-hidden">
|
|
59
|
-
<div class="h-full flex gap-px bg-gray-200 dark:bg-gray-800">
|
|
60
|
-
<!-- Runs List -->
|
|
61
|
-
<div class="w-1/3 min-w-0 flex-shrink-0 bg-white dark:bg-gray-950 flex flex-col min-h-0 overflow-hidden">
|
|
62
|
-
<div class="px-4 py-3 min-h-[49px] border-b border-gray-200 dark:border-gray-800 flex items-center justify-between shrink-0">
|
|
63
|
-
<h2 class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
64
|
-
Runs
|
|
65
|
-
</h2>
|
|
66
|
-
<div class="flex items-center gap-2">
|
|
67
|
-
<UButton
|
|
68
|
-
v-if="selectedFlow"
|
|
69
|
-
icon="i-lucide-play"
|
|
70
|
-
size="xs"
|
|
71
|
-
color="primary"
|
|
72
|
-
variant="soft"
|
|
73
|
-
@click="openStartFlowModal"
|
|
74
|
-
>
|
|
75
|
-
Start
|
|
76
|
-
</UButton>
|
|
77
|
-
<div
|
|
78
|
-
v-if="selectedFlow"
|
|
79
|
-
class="flex items-center gap-2 text-xs text-gray-500"
|
|
80
|
-
>
|
|
81
|
-
<UIcon
|
|
82
|
-
name="i-lucide-list"
|
|
83
|
-
class="w-3.5 h-3.5"
|
|
84
|
-
/>
|
|
85
|
-
<span>{{ totalRuns }} run{{ totalRuns === 1 ? "" : "s" }}</span>
|
|
86
|
-
</div>
|
|
87
|
-
<UDropdownMenu
|
|
88
|
-
v-if="selectedFlow"
|
|
89
|
-
:items="flowActionsItems"
|
|
90
|
-
:ui="{ content: 'min-w-48' }"
|
|
91
|
-
>
|
|
92
|
-
<UButton
|
|
93
|
-
icon="i-lucide-more-vertical"
|
|
94
|
-
size="xs"
|
|
95
|
-
color="neutral"
|
|
96
|
-
variant="ghost"
|
|
97
|
-
square
|
|
98
|
-
/>
|
|
99
|
-
</UDropdownMenu>
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
<div
|
|
103
|
-
v-if="!selectedFlow"
|
|
104
|
-
class="flex-1 overflow-y-auto min-h-0"
|
|
105
|
-
>
|
|
106
|
-
<div class="h-full flex items-center justify-center text-sm text-gray-400 px-4 text-center">
|
|
107
|
-
Select a flow to view runs
|
|
108
|
-
</div>
|
|
109
|
-
</div>
|
|
110
|
-
<div
|
|
111
|
-
v-else-if="!runs || runs.length === 0"
|
|
112
|
-
class="flex-1 overflow-y-auto min-h-0"
|
|
113
|
-
>
|
|
114
|
-
<div class="h-full flex items-center justify-center text-sm text-gray-400">
|
|
115
|
-
<ClientOnly>
|
|
116
|
-
<div class="text-center">
|
|
117
|
-
<div v-if="loadingRuns">
|
|
118
|
-
Loading runs...
|
|
119
|
-
</div>
|
|
120
|
-
<div v-else>
|
|
121
|
-
No runs yet
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
<template #fallback>
|
|
125
|
-
<div class="text-center">
|
|
126
|
-
No runs yet
|
|
127
|
-
</div>
|
|
128
|
-
</template>
|
|
129
|
-
</ClientOnly>
|
|
130
|
-
</div>
|
|
131
|
-
</div>
|
|
132
|
-
<div
|
|
133
|
-
v-else
|
|
134
|
-
class="flex-1 min-h-0 flex flex-col overflow-hidden"
|
|
135
|
-
>
|
|
136
|
-
<div class="flex-1 min-h-0 overflow-y-auto divide-y divide-gray-100 dark:divide-gray-800">
|
|
137
|
-
<SelectableListItem
|
|
138
|
-
v-for="r in runs"
|
|
139
|
-
:key="r.id"
|
|
140
|
-
:selected="selectedRunId === r.id"
|
|
141
|
-
:icon="getRunStatusIcon(r.status)"
|
|
142
|
-
:icon-class="getRunStatusIconClass(r.status)"
|
|
143
|
-
:subtitle="truncateId(r.id)"
|
|
144
|
-
:meta="formatTime(r.createdAt)"
|
|
145
|
-
@click="selectRun(r.id)"
|
|
146
|
-
>
|
|
147
|
-
<template #title>
|
|
148
|
-
Run {{ r.id?.substring(0, 8) }}
|
|
149
|
-
</template>
|
|
150
|
-
<template #badge>
|
|
151
|
-
<FlowRunStatusBadge
|
|
152
|
-
:is-running="r.status === 'running'"
|
|
153
|
-
:is-completed="r.status === 'completed'"
|
|
154
|
-
:is-failed="r.status === 'failed'"
|
|
155
|
-
:is-canceled="r.status === 'canceled'"
|
|
156
|
-
:is-stalled="r.status === 'stalled'"
|
|
157
|
-
:is-awaiting="r.status === 'awaiting'"
|
|
158
|
-
/>
|
|
159
|
-
</template>
|
|
160
|
-
<template #meta>
|
|
161
|
-
<span>{{ formatTime(r.createdAt) }}</span>
|
|
162
|
-
<span
|
|
163
|
-
v-if="r.stepCount > 0"
|
|
164
|
-
class="flex items-center gap-1"
|
|
165
|
-
>
|
|
166
|
-
<UIcon
|
|
167
|
-
name="i-lucide-list-checks"
|
|
168
|
-
class="w-3 h-3"
|
|
169
|
-
/>
|
|
170
|
-
{{ r.completedSteps }}/{{ r.stepCount }}
|
|
171
|
-
</span>
|
|
172
|
-
<span
|
|
173
|
-
v-if="r.completedAt && r.startedAt"
|
|
174
|
-
class="flex items-center gap-1"
|
|
175
|
-
>
|
|
176
|
-
<UIcon
|
|
177
|
-
name="i-lucide-timer"
|
|
178
|
-
class="w-3 h-3"
|
|
179
|
-
/>
|
|
180
|
-
{{ formatDuration(r.startedAt, r.completedAt) }}
|
|
181
|
-
</span>
|
|
182
|
-
</template>
|
|
183
|
-
</SelectableListItem>
|
|
184
|
-
|
|
185
|
-
<!-- Loading indicator -->
|
|
186
|
-
<div
|
|
187
|
-
v-if="loadingRuns"
|
|
188
|
-
class="px-4 py-3 text-center text-xs text-gray-400"
|
|
189
|
-
>
|
|
190
|
-
<UIcon
|
|
191
|
-
name="i-lucide-loader-2"
|
|
192
|
-
class="w-4 h-4 animate-spin inline-block"
|
|
193
|
-
/>
|
|
194
|
-
<span class="ml-2">Loading runs...</span>
|
|
195
|
-
</div>
|
|
196
|
-
</div>
|
|
197
|
-
|
|
198
|
-
<!-- Pagination Footer -->
|
|
199
|
-
<div
|
|
200
|
-
v-if="totalRuns > runsPerPage"
|
|
201
|
-
class="border-t border-gray-200 dark:border-gray-800 px-4 py-3 flex items-center justify-center shrink-0"
|
|
202
|
-
>
|
|
203
|
-
<UPagination
|
|
204
|
-
v-model:page="currentPage"
|
|
205
|
-
:items-per-page="runsPerPage"
|
|
206
|
-
:total="totalRuns"
|
|
207
|
-
size="xs"
|
|
208
|
-
/>
|
|
209
|
-
</div>
|
|
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
|
-
<div
|
|
237
|
-
class="w-1.5 h-1.5 rounded-full"
|
|
238
|
-
:class="isReconnecting ? 'bg-amber-500 animate-pulse' : 'bg-emerald-500 animate-pulse'"
|
|
239
|
-
/>
|
|
240
|
-
<span>{{ isReconnecting ? "Reconnecting" : "Live" }}</span>
|
|
241
|
-
</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="enhancedStepList"
|
|
289
|
-
:flow-name="selectedFlow || void 0"
|
|
290
|
-
:run-id="selectedRunId || void 0"
|
|
291
|
-
:trigger-name="flowState.state.value.meta?.triggerName"
|
|
292
|
-
:trigger-type="flowState.state.value.meta?.triggerType"
|
|
293
|
-
:flow-def="selectedFlowDef"
|
|
294
|
-
:stall-timeout="runSnapshot.stallTimeout"
|
|
295
|
-
@select-step="handleSelectStep"
|
|
296
|
-
@cancel-flow="handleCancelFlow"
|
|
297
|
-
@restart-flow="handleRestartFlow"
|
|
298
|
-
/>
|
|
299
|
-
</div>
|
|
300
|
-
</div>
|
|
301
|
-
|
|
302
|
-
<!-- Right: Combined Logs & Events -->
|
|
303
|
-
<div class="flex-1 min-w-0 max-w-[50%] bg-white dark:bg-gray-950 flex flex-col min-h-0 overflow-hidden">
|
|
304
|
-
<FlowRunTimeline
|
|
305
|
-
:events="timeline"
|
|
306
|
-
:logs="filteredLogs"
|
|
307
|
-
:is-live="isConnected"
|
|
308
|
-
@export="exportTimelineJson"
|
|
309
|
-
/>
|
|
310
|
-
</div>
|
|
311
|
-
</div>
|
|
312
|
-
</div>
|
|
313
|
-
</div>
|
|
314
|
-
</div>
|
|
315
|
-
</div>
|
|
316
|
-
|
|
317
|
-
<!-- Start Flow Modal -->
|
|
318
|
-
<UModal v-model:open="startFlowModalOpen">
|
|
319
|
-
<template #header>
|
|
320
|
-
<div class="flex items-center justify-between">
|
|
321
|
-
<div>
|
|
322
|
-
<h3 class="text-lg font-semibold">
|
|
323
|
-
Start Flow Run
|
|
324
|
-
</h3>
|
|
325
|
-
<p class="text-sm text-gray-500 mt-1">
|
|
326
|
-
{{ selectedFlow }}
|
|
327
|
-
</p>
|
|
328
|
-
</div>
|
|
329
|
-
</div>
|
|
330
|
-
</template>
|
|
331
|
-
<template #body>
|
|
332
|
-
<div class="space-y-4">
|
|
333
|
-
<div>
|
|
334
|
-
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
|
335
|
-
Input Data (JSON)
|
|
336
|
-
</label>
|
|
337
|
-
<UTextarea
|
|
338
|
-
v-model="flowInputJson"
|
|
339
|
-
:rows="12"
|
|
340
|
-
placeholder="{\n "key": "value"\n}"
|
|
341
|
-
class="w-full font-mono text-sm"
|
|
342
|
-
/>
|
|
343
|
-
<p
|
|
344
|
-
v-if="jsonError"
|
|
345
|
-
class="text-xs text-red-500 mt-2"
|
|
346
|
-
>
|
|
347
|
-
{{ jsonError }}
|
|
348
|
-
</p>
|
|
349
|
-
</div>
|
|
350
|
-
</div>
|
|
351
|
-
</template>
|
|
352
|
-
<template #footer>
|
|
353
|
-
<div class="flex justify-end gap-2">
|
|
354
|
-
<UButton
|
|
355
|
-
color="neutral"
|
|
356
|
-
variant="ghost"
|
|
357
|
-
@click="startFlowModalOpen = false"
|
|
358
|
-
>
|
|
359
|
-
Cancel
|
|
360
|
-
</UButton>
|
|
361
|
-
<UButton
|
|
362
|
-
color="primary"
|
|
363
|
-
:loading="startingFlow"
|
|
364
|
-
:disabled="!!jsonError"
|
|
365
|
-
@click="startFlowRun"
|
|
366
|
-
>
|
|
367
|
-
Start Flow
|
|
368
|
-
</UButton>
|
|
369
|
-
</div>
|
|
370
|
-
</template>
|
|
371
|
-
</UModal>
|
|
372
|
-
|
|
373
|
-
<!-- Confirm Dialog -->
|
|
374
|
-
<ConfirmDialog
|
|
375
|
-
v-model:open="confirmDialogOpen"
|
|
376
|
-
:title="confirmDialogConfig.title"
|
|
377
|
-
:description="confirmDialogConfig.description"
|
|
378
|
-
:items="confirmDialogConfig.items"
|
|
379
|
-
:warning="confirmDialogConfig.warning"
|
|
380
|
-
:loading="clearingHistory"
|
|
381
|
-
confirm-label="Clear History"
|
|
382
|
-
confirm-color="error"
|
|
383
|
-
icon="i-lucide-trash-2"
|
|
384
|
-
icon-color="error"
|
|
385
|
-
@confirm="confirmDialogConfig.onConfirm"
|
|
386
|
-
/>
|
|
387
|
-
</div>
|
|
388
|
-
</template>
|
|
389
|
-
|
|
390
|
-
<script setup>
|
|
391
|
-
import { ref, computed, watch } from "#imports";
|
|
392
|
-
import FlowDiagram from "../../components/flow/Diagram.vue";
|
|
393
|
-
import FlowRunOverview from "../../components/flow/RunOverview.vue";
|
|
394
|
-
import FlowRunTimeline from "../../components/flow/RunTimeline.vue";
|
|
395
|
-
import FlowRunStatusBadge from "../../components/flow/RunStatusBadge.vue";
|
|
396
|
-
import ConfirmDialog from "../../components/ConfirmDialog.vue";
|
|
397
|
-
import SelectableListItem from "../../components/SelectableListItem.vue";
|
|
398
|
-
import { useRoute, useRouter } from "#app";
|
|
399
|
-
import { useAnalyzedFlows } from "../../composables/useAnalyzedFlows";
|
|
400
|
-
import { useFlowRuns } from "../../composables/useFlowRuns";
|
|
401
|
-
import { useFlowRunTimeline } from "../../composables/useFlowRunTimeline";
|
|
402
|
-
import { useFlowRunsUpdates } from "../../composables/useFlowRunsUpdates";
|
|
403
|
-
import { useComponentRouter } from "../../composables/useComponentRouter";
|
|
404
|
-
const componentRouter = useComponentRouter();
|
|
405
|
-
const router = useRouter();
|
|
406
|
-
const route = useRoute();
|
|
407
|
-
const selectedFlow = computed(() => {
|
|
408
|
-
const path = componentRouter.route.value?.path || "";
|
|
409
|
-
const match = path.match(/\/flows\/([^/]+)/);
|
|
410
|
-
return match && match[1] ? decodeURIComponent(match[1]) : null;
|
|
411
|
-
});
|
|
412
|
-
const goBack = () => {
|
|
413
|
-
componentRouter.push("/flows");
|
|
414
|
-
};
|
|
415
|
-
const selectedRunId = ref("");
|
|
416
|
-
const mainTab = ref("diagram");
|
|
417
|
-
const mainTabs = computed(() => [
|
|
418
|
-
{ label: "Diagram", value: "diagram", icon: "i-lucide-git-branch" },
|
|
419
|
-
{
|
|
420
|
-
label: "Timeline",
|
|
421
|
-
value: "timeline",
|
|
422
|
-
icon: "i-lucide-activity",
|
|
423
|
-
disabled: !selectedRunId.value
|
|
424
|
-
}
|
|
425
|
-
]);
|
|
426
|
-
watch(selectedRunId, (newRunId, oldRunId) => {
|
|
427
|
-
if (newRunId && newRunId !== oldRunId) {
|
|
428
|
-
mainTab.value = "timeline";
|
|
429
|
-
} else if (!newRunId) {
|
|
430
|
-
mainTab.value = "diagram";
|
|
431
|
-
}
|
|
432
|
-
});
|
|
433
|
-
const flows = useAnalyzedFlows();
|
|
434
|
-
const selectedFlowDef = computed(() => {
|
|
435
|
-
if (!selectedFlow.value) return null;
|
|
436
|
-
return flows.value.find((f) => f.id === selectedFlow.value);
|
|
437
|
-
});
|
|
438
|
-
const selectedFlowRef = computed(() => selectedFlow.value || "");
|
|
439
|
-
const selectedRunIdRef = computed(() => selectedRunId.value || "");
|
|
440
|
-
const runsPerPage = 20;
|
|
441
|
-
const currentPage = computed({
|
|
442
|
-
get: () => {
|
|
443
|
-
const page = route.query.page;
|
|
444
|
-
return page ? Number.parseInt(page, 10) : 1;
|
|
445
|
-
},
|
|
446
|
-
set: (value) => {
|
|
447
|
-
router.replace({
|
|
448
|
-
query: {
|
|
449
|
-
...route.query,
|
|
450
|
-
page: value > 1 ? value.toString() : void 0
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
const runsQueryOptions = computed(() => ({
|
|
456
|
-
limit: runsPerPage,
|
|
457
|
-
offset: (currentPage.value - 1) * runsPerPage
|
|
458
|
-
}));
|
|
459
|
-
const {
|
|
460
|
-
runs: runsResponse,
|
|
461
|
-
refresh: refreshRuns,
|
|
462
|
-
status: runsStatus
|
|
463
|
-
} = useFlowRuns(selectedFlowRef, runsQueryOptions);
|
|
464
|
-
const runs = computed(() => runsResponse.value?.items || []);
|
|
465
|
-
const totalRuns = computed(() => runsResponse.value?.total || 0);
|
|
466
|
-
const loadingRuns = computed(() => runsStatus.value === "pending");
|
|
467
|
-
const { flowState, isConnected, isReconnecting } = useFlowRunTimeline(selectedFlowRef, selectedRunIdRef);
|
|
468
|
-
const { shouldRefreshRuns, resetRefreshFlag } = useFlowRunsUpdates(selectedFlowRef);
|
|
469
|
-
watch(shouldRefreshRuns, async (shouldRefresh) => {
|
|
470
|
-
if (shouldRefresh) {
|
|
471
|
-
await refreshRuns();
|
|
472
|
-
resetRefreshFlag();
|
|
473
|
-
}
|
|
474
|
-
});
|
|
475
|
-
const startFlowModalOpen = ref(false);
|
|
476
|
-
const flowInputJson = ref("{}");
|
|
477
|
-
const jsonError = ref("");
|
|
478
|
-
const startingFlow = ref(false);
|
|
479
|
-
const clearingHistory = ref(false);
|
|
480
|
-
const confirmDialogOpen = ref(false);
|
|
481
|
-
const confirmDialogConfig = ref({
|
|
482
|
-
title: "",
|
|
483
|
-
description: "",
|
|
484
|
-
items: [],
|
|
485
|
-
warning: "",
|
|
486
|
-
onConfirm: () => {
|
|
487
|
-
}
|
|
488
|
-
});
|
|
489
|
-
const flowActionsItems = computed(() => [[
|
|
490
|
-
{
|
|
491
|
-
label: "Clear History",
|
|
492
|
-
icon: "i-lucide-trash-2",
|
|
493
|
-
disabled: clearingHistory.value,
|
|
494
|
-
onSelect: () => confirmClearHistory()
|
|
495
|
-
}
|
|
496
|
-
]]);
|
|
497
|
-
watch(flowInputJson, (value) => {
|
|
498
|
-
try {
|
|
499
|
-
JSON.parse(value);
|
|
500
|
-
jsonError.value = "";
|
|
501
|
-
} catch (err) {
|
|
502
|
-
jsonError.value = err instanceof Error ? err.message : "Invalid JSON";
|
|
503
|
-
}
|
|
504
|
-
});
|
|
505
|
-
const formatTime = (timestamp) => {
|
|
506
|
-
const date = new Date(timestamp);
|
|
507
|
-
const now = /* @__PURE__ */ new Date();
|
|
508
|
-
const diff = now.getTime() - date.getTime();
|
|
509
|
-
const seconds = Math.floor(diff / 1e3);
|
|
510
|
-
const minutes = Math.floor(seconds / 60);
|
|
511
|
-
const hours = Math.floor(minutes / 60);
|
|
512
|
-
const days = Math.floor(hours / 24);
|
|
513
|
-
if (days > 0) return `${days}d ago`;
|
|
514
|
-
if (hours > 0) return `${hours}h ago`;
|
|
515
|
-
if (minutes > 0) return `${minutes}m ago`;
|
|
516
|
-
if (seconds > 10) return `${seconds}s ago`;
|
|
517
|
-
return "just now";
|
|
518
|
-
};
|
|
519
|
-
const formatDuration = (start, end) => {
|
|
520
|
-
const startTime = new Date(start).getTime();
|
|
521
|
-
const endTime = new Date(end).getTime();
|
|
522
|
-
const diff = endTime - startTime;
|
|
523
|
-
const seconds = Math.floor(diff / 1e3);
|
|
524
|
-
const minutes = Math.floor(seconds / 60);
|
|
525
|
-
const hours = Math.floor(minutes / 60);
|
|
526
|
-
if (hours > 0) {
|
|
527
|
-
const remainingMinutes = minutes % 60;
|
|
528
|
-
return `${hours}h ${remainingMinutes}m`;
|
|
529
|
-
}
|
|
530
|
-
if (minutes > 0) {
|
|
531
|
-
const remainingSeconds = seconds % 60;
|
|
532
|
-
return `${minutes}m ${remainingSeconds}s`;
|
|
533
|
-
}
|
|
534
|
-
return `${seconds}s`;
|
|
535
|
-
};
|
|
536
|
-
const truncateId = (id) => {
|
|
537
|
-
if (!id || id.length <= 16) return id;
|
|
538
|
-
return `${id.substring(0, 8)}...${id.substring(id.length - 8)}`;
|
|
539
|
-
};
|
|
540
|
-
const getRunStatusIcon = (status) => {
|
|
541
|
-
switch (status) {
|
|
542
|
-
case "running":
|
|
543
|
-
return "i-lucide-loader-2";
|
|
544
|
-
case "completed":
|
|
545
|
-
return "i-lucide-check-circle";
|
|
546
|
-
case "failed":
|
|
547
|
-
return "i-lucide-x-circle";
|
|
548
|
-
case "canceled":
|
|
549
|
-
return "i-lucide-ban";
|
|
550
|
-
case "stalled":
|
|
551
|
-
return "i-lucide-alert-triangle";
|
|
552
|
-
case "awaiting":
|
|
553
|
-
return "i-lucide-pause-circle";
|
|
554
|
-
default:
|
|
555
|
-
return "i-lucide-circle";
|
|
556
|
-
}
|
|
557
|
-
};
|
|
558
|
-
const getRunStatusIconClass = (status) => {
|
|
559
|
-
switch (status) {
|
|
560
|
-
case "running":
|
|
561
|
-
return "text-blue-500 animate-spin";
|
|
562
|
-
case "completed":
|
|
563
|
-
return "text-emerald-500";
|
|
564
|
-
case "failed":
|
|
565
|
-
return "text-red-500";
|
|
566
|
-
case "canceled":
|
|
567
|
-
return "text-gray-500";
|
|
568
|
-
case "stalled":
|
|
569
|
-
return "text-amber-500";
|
|
570
|
-
case "awaiting":
|
|
571
|
-
return "text-purple-500";
|
|
572
|
-
default:
|
|
573
|
-
return "text-gray-400";
|
|
574
|
-
}
|
|
575
|
-
};
|
|
576
|
-
const runSnapshot = computed(() => {
|
|
577
|
-
const state = flowState.state.value;
|
|
578
|
-
const flowMeta = selectedFlowMeta.value;
|
|
579
|
-
return {
|
|
580
|
-
status: state.status,
|
|
581
|
-
startedAt: state.startedAt,
|
|
582
|
-
completedAt: state.completedAt,
|
|
583
|
-
logsCount: state.logs.length,
|
|
584
|
-
lastLogLevel: state.logs.length > 0 ? state.logs[state.logs.length - 1]?.level : void 0,
|
|
585
|
-
// Use stallTimeout from event data if available, otherwise fall back to static flow definition
|
|
586
|
-
stallTimeout: state.meta?.stallTimeout || flowMeta?.analyzed?.stallTimeout
|
|
587
|
-
};
|
|
588
|
-
});
|
|
589
|
-
const selectedStepKey = ref(null);
|
|
590
|
-
const timeline = computed(() => {
|
|
591
|
-
const events = flowState.events.value;
|
|
592
|
-
if (!selectedStepKey.value) return events;
|
|
593
|
-
return events.filter((e) => e.stepName === selectedStepKey.value);
|
|
594
|
-
});
|
|
595
|
-
const filteredLogs = computed(() => {
|
|
596
|
-
const logs = flowState.state.value.logs;
|
|
597
|
-
if (!selectedStepKey.value) return logs;
|
|
598
|
-
return logs.filter((log) => log.stepName === selectedStepKey.value);
|
|
599
|
-
});
|
|
600
|
-
const selectedFlowMeta = computed(() => {
|
|
601
|
-
const id = selectedFlow.value;
|
|
602
|
-
if (!id) return null;
|
|
603
|
-
return (flows.value || []).find((f) => f?.id === id) || null;
|
|
604
|
-
});
|
|
605
|
-
const enhancedStepList = computed(() => {
|
|
606
|
-
const steps = flowState.stepList.value;
|
|
607
|
-
const flowMeta = selectedFlowMeta.value;
|
|
608
|
-
if (!flowMeta?.analyzed?.steps) return steps;
|
|
609
|
-
const stepTimeoutMap = /* @__PURE__ */ new Map();
|
|
610
|
-
for (const [stepName, analyzedStep] of Object.entries(flowMeta.analyzed.steps)) {
|
|
611
|
-
if (analyzedStep.stepTimeout !== void 0) {
|
|
612
|
-
stepTimeoutMap.set(stepName, analyzedStep.stepTimeout);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
return steps.map((step) => {
|
|
616
|
-
const baseStepName = step.key.includes(":await-") ? step.key.split(":await-")[0] : step.key;
|
|
617
|
-
const staticTimeout = stepTimeoutMap.get(baseStepName);
|
|
618
|
-
if (staticTimeout !== void 0) {
|
|
619
|
-
return { ...step, stepTimeout: staticTimeout };
|
|
620
|
-
}
|
|
621
|
-
return step;
|
|
622
|
-
});
|
|
623
|
-
});
|
|
624
|
-
const handleSelectStep = (stepKey) => {
|
|
625
|
-
selectedStepKey.value = stepKey;
|
|
626
|
-
};
|
|
627
|
-
const handleCancelFlow = async () => {
|
|
628
|
-
if (!selectedFlow.value || !selectedRunId.value) return;
|
|
629
|
-
try {
|
|
630
|
-
await $fetch(`/api/_flows/${selectedFlow.value}/runs/${selectedRunId.value}/cancel`, {
|
|
631
|
-
method: "POST"
|
|
632
|
-
});
|
|
633
|
-
} catch (error) {
|
|
634
|
-
console.error("Failed to cancel flow:", error);
|
|
635
|
-
}
|
|
636
|
-
};
|
|
637
|
-
const handleRestartFlow = async () => {
|
|
638
|
-
if (!selectedFlow.value || !selectedRunId.value) return;
|
|
639
|
-
try {
|
|
640
|
-
const result = await $fetch(`/api/_flows/${selectedFlow.value}/runs/${selectedRunId.value}/restart`, {
|
|
641
|
-
method: "POST"
|
|
642
|
-
});
|
|
643
|
-
if (result?.newRunId) {
|
|
644
|
-
selectedRunId.value = result.newRunId;
|
|
645
|
-
mainTab.value = "timeline";
|
|
646
|
-
}
|
|
647
|
-
await refreshRuns();
|
|
648
|
-
} catch (error) {
|
|
649
|
-
console.error("Failed to restart flow:", error);
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
const diagramStepStates = computed(() => {
|
|
653
|
-
if (!selectedRunId.value) return void 0;
|
|
654
|
-
return flowState.state.value.steps;
|
|
655
|
-
});
|
|
656
|
-
const selectRun = (runId) => {
|
|
657
|
-
selectedRunId.value = runId;
|
|
658
|
-
mainTab.value = "timeline";
|
|
659
|
-
};
|
|
660
|
-
const exportTimelineJson = () => {
|
|
661
|
-
const blob = new Blob([JSON.stringify(flowState.events.value, null, 2)], { type: "application/json" });
|
|
662
|
-
const url = URL.createObjectURL(blob);
|
|
663
|
-
const a = document.createElement("a");
|
|
664
|
-
a.href = url;
|
|
665
|
-
a.download = `flow-${selectedFlow.value}-${selectedRunId.value}-events-${(/* @__PURE__ */ new Date()).toISOString()}.json`;
|
|
666
|
-
document.body.appendChild(a);
|
|
667
|
-
a.click();
|
|
668
|
-
a.remove();
|
|
669
|
-
URL.revokeObjectURL(url);
|
|
670
|
-
};
|
|
671
|
-
const handleNodeAction = async (payload) => {
|
|
672
|
-
const _stepName = payload.id.split(":")[1];
|
|
673
|
-
if (!selectedRunId.value) {
|
|
674
|
-
alert("Please select a flow run first to view logs or details.");
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
mainTab.value = "timeline";
|
|
678
|
-
};
|
|
679
|
-
const openStartFlowModal = () => {
|
|
680
|
-
flowInputJson.value = "{}";
|
|
681
|
-
jsonError.value = "";
|
|
682
|
-
startFlowModalOpen.value = true;
|
|
683
|
-
};
|
|
684
|
-
const startFlowRun = async () => {
|
|
685
|
-
if (!selectedFlow.value || jsonError.value) return;
|
|
686
|
-
try {
|
|
687
|
-
startingFlow.value = true;
|
|
688
|
-
const input = JSON.parse(flowInputJson.value);
|
|
689
|
-
const result = await $fetch(`/api/_flows/${encodeURIComponent(selectedFlow.value)}/start`, {
|
|
690
|
-
method: "POST",
|
|
691
|
-
body: input
|
|
692
|
-
});
|
|
693
|
-
startFlowModalOpen.value = false;
|
|
694
|
-
flowInputJson.value = "{}";
|
|
695
|
-
if (result?.flowId) {
|
|
696
|
-
selectedRunId.value = result.flowId;
|
|
697
|
-
mainTab.value = "timeline";
|
|
698
|
-
}
|
|
699
|
-
await refreshRuns();
|
|
700
|
-
} catch (err) {
|
|
701
|
-
console.error("Failed to start flow:", err);
|
|
702
|
-
jsonError.value = err instanceof Error ? err.message : "Failed to start flow";
|
|
703
|
-
} finally {
|
|
704
|
-
startingFlow.value = false;
|
|
705
|
-
}
|
|
706
|
-
};
|
|
707
|
-
const confirmClearHistory = () => {
|
|
708
|
-
if (!selectedFlow.value) return;
|
|
709
|
-
confirmDialogConfig.value = {
|
|
710
|
-
title: "Clear Flow History",
|
|
711
|
-
description: `Are you sure you want to clear all history for "${selectedFlow.value}"?`,
|
|
712
|
-
items: [
|
|
713
|
-
"All flow run events",
|
|
714
|
-
"All flow run logs",
|
|
715
|
-
"The runs index"
|
|
716
|
-
],
|
|
717
|
-
warning: "This action cannot be undone.",
|
|
718
|
-
onConfirm: () => {
|
|
719
|
-
clearFlowHistory();
|
|
720
|
-
}
|
|
721
|
-
};
|
|
722
|
-
confirmDialogOpen.value = true;
|
|
723
|
-
};
|
|
724
|
-
const clearFlowHistory = async () => {
|
|
725
|
-
if (!selectedFlow.value) return;
|
|
726
|
-
try {
|
|
727
|
-
clearingHistory.value = true;
|
|
728
|
-
await $fetch(`/api/_flows/${encodeURIComponent(selectedFlow.value)}/clear-history`, {
|
|
729
|
-
method: "DELETE"
|
|
730
|
-
});
|
|
731
|
-
confirmDialogOpen.value = false;
|
|
732
|
-
selectedRunId.value = "";
|
|
733
|
-
mainTab.value = "diagram";
|
|
734
|
-
await refreshRuns();
|
|
735
|
-
} catch (err) {
|
|
736
|
-
console.error("Failed to clear history:", err);
|
|
737
|
-
confirmDialogConfig.value = {
|
|
738
|
-
title: "Error Clearing History",
|
|
739
|
-
description: `Failed to clear history: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
740
|
-
items: [],
|
|
741
|
-
warning: "",
|
|
742
|
-
onConfirm: () => {
|
|
743
|
-
confirmDialogOpen.value = false;
|
|
744
|
-
}
|
|
745
|
-
};
|
|
746
|
-
} finally {
|
|
747
|
-
clearingHistory.value = false;
|
|
748
|
-
}
|
|
749
|
-
};
|
|
750
|
-
</script>
|