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