@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.
Files changed (153) hide show
  1. package/dist/module.d.mts +19 -1
  2. package/dist/module.mjs +21 -4
  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,738 @@
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-4 shrink-0">
5
+ <div class="flex items-center justify-between">
6
+ <div>
7
+ <h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">
8
+ Dashboard
9
+ </h1>
10
+ <p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
11
+ Overview of your Nuxt Queue system
12
+ </p>
13
+ </div>
14
+ <div
15
+ v-if="isConnected"
16
+ class="flex items-center gap-1.5 text-xs text-emerald-600 dark:text-emerald-400"
17
+ >
18
+ <div class="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
19
+ <span>Live</span>
20
+ </div>
21
+ </div>
22
+ </div>
23
+
24
+ <!-- Main Content -->
25
+ <div class="flex-1 min-h-0 overflow-y-auto p-6">
26
+ <div class="max-w-7xl mx-auto space-y-6">
27
+ <!-- System Stats Overview -->
28
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
29
+ <!-- Queues Card -->
30
+ <div
31
+ class="bg-gradient-to-br from-blue-500 to-blue-600 dark:from-blue-600 dark:to-blue-700 rounded-xl p-6 text-white shadow-lg hover:shadow-xl transition-shadow cursor-pointer"
32
+ @click="navigateTo('/queues')"
33
+ >
34
+ <div class="flex items-center justify-between mb-4">
35
+ <div class="p-3 bg-white/20 rounded-lg backdrop-blur-sm">
36
+ <UIcon
37
+ name="i-lucide-app-window"
38
+ class="w-6 h-6"
39
+ />
40
+ </div>
41
+ <UIcon
42
+ name="i-lucide-arrow-right"
43
+ class="w-5 h-5 opacity-60"
44
+ />
45
+ </div>
46
+ <div class="text-3xl font-bold mb-1">
47
+ {{ queuesStats?.total || 0 }}
48
+ </div>
49
+ <div class="text-sm opacity-90 mb-3">
50
+ Active Queues
51
+ </div>
52
+ <div class="flex items-center gap-4 text-xs opacity-75">
53
+ <div v-if="queuesStats?.pending > 0">
54
+ <span class="font-semibold">{{ formatNumber(queuesStats?.pending || 0) }}</span> pending
55
+ </div>
56
+ <div>
57
+ <span class="font-semibold">{{ formatNumber(queuesStats?.completed || 0) }}</span> completed
58
+ </div>
59
+ <div v-if="queuesStats?.failed > 0">
60
+ <span class="font-semibold">{{ formatNumber(queuesStats?.failed || 0) }}</span> failed
61
+ </div>
62
+ </div>
63
+ </div>
64
+
65
+ <!-- Flows Card -->
66
+ <div
67
+ class="bg-gradient-to-br from-purple-500 to-purple-600 dark:from-purple-600 dark:to-purple-700 rounded-xl p-6 text-white shadow-lg hover:shadow-xl transition-shadow cursor-pointer"
68
+ @click="navigateTo('/flows')"
69
+ >
70
+ <div class="flex items-center justify-between mb-4">
71
+ <div class="p-3 bg-white/20 rounded-lg backdrop-blur-sm">
72
+ <UIcon
73
+ name="i-lucide-git-branch"
74
+ class="w-6 h-6"
75
+ />
76
+ </div>
77
+ <UIcon
78
+ name="i-lucide-arrow-right"
79
+ class="w-5 h-5 opacity-60"
80
+ />
81
+ </div>
82
+ <div class="text-3xl font-bold mb-1">
83
+ {{ flowsStats?.total || 0 }}
84
+ </div>
85
+ <div class="text-sm opacity-90 mb-3">
86
+ Registered Flows
87
+ </div>
88
+ <div class="flex items-center gap-4 text-xs opacity-75">
89
+ <div v-if="flowsStats?.running > 0">
90
+ <span class="font-semibold">{{ flowsStats?.running }}</span> running
91
+ </div>
92
+ <div v-if="flowsStats?.awaiting > 0">
93
+ <span class="font-semibold">{{ flowsStats?.awaiting }}</span> awaiting
94
+ </div>
95
+ <div>
96
+ <span class="font-semibold">{{ formatNumber(flowsStats?.success || 0) }}</span> success
97
+ </div>
98
+ </div>
99
+ </div>
100
+
101
+ <!-- Triggers Card -->
102
+ <div
103
+ class="bg-gradient-to-br from-amber-500 to-amber-600 dark:from-amber-600 dark:to-amber-700 rounded-xl p-6 text-white shadow-lg hover:shadow-xl transition-shadow cursor-pointer"
104
+ @click="navigateTo('/triggers')"
105
+ >
106
+ <div class="flex items-center justify-between mb-4">
107
+ <div class="p-3 bg-white/20 rounded-lg backdrop-blur-sm">
108
+ <UIcon
109
+ name="i-lucide-zap"
110
+ class="w-6 h-6"
111
+ />
112
+ </div>
113
+ <UIcon
114
+ name="i-lucide-arrow-right"
115
+ class="w-5 h-5 opacity-60"
116
+ />
117
+ </div>
118
+ <div class="text-3xl font-bold mb-1">
119
+ {{ triggersStats?.total || 0 }}
120
+ </div>
121
+ <div class="text-sm opacity-90 mb-3">
122
+ Active Triggers
123
+ </div>
124
+ <div class="flex items-center gap-4 text-xs opacity-75">
125
+ <div>
126
+ <span class="font-semibold">{{ formatNumber(triggersStats?.totalFires || 0) }}</span> fires
127
+ </div>
128
+ <div>
129
+ <span class="font-semibold">{{ formatNumber(triggersStats?.totalFlowsStarted || 0) }}</span> flows started
130
+ </div>
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ <!-- Quick Actions & Recent Activity -->
136
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
137
+ <!-- Quick Actions -->
138
+ <div class="bg-white dark:bg-gray-950 rounded-xl border border-gray-200 dark:border-gray-800 p-6">
139
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center gap-2">
140
+ <UIcon
141
+ name="i-lucide-rocket"
142
+ class="w-5 h-5 text-blue-500"
143
+ />
144
+ Quick Actions
145
+ </h2>
146
+ <div class="space-y-2">
147
+ <button
148
+ class="w-full flex items-center justify-between p-3 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-900 transition-colors text-left group"
149
+ @click="navigateTo('/queues')"
150
+ >
151
+ <div class="flex items-center gap-3">
152
+ <div class="p-2 bg-blue-50 dark:bg-blue-950/50 rounded-lg group-hover:bg-blue-100 dark:group-hover:bg-blue-900/50 transition-colors">
153
+ <UIcon
154
+ name="i-lucide-app-window"
155
+ class="w-4 h-4 text-blue-600 dark:text-blue-400"
156
+ />
157
+ </div>
158
+ <div>
159
+ <div class="text-sm font-medium text-gray-900 dark:text-gray-100">
160
+ View All Queues
161
+ </div>
162
+ <div class="text-xs text-gray-500 dark:text-gray-400">
163
+ Manage job queues and workers
164
+ </div>
165
+ </div>
166
+ </div>
167
+ <UIcon
168
+ name="i-lucide-chevron-right"
169
+ class="w-4 h-4 text-gray-400 group-hover:text-gray-600 dark:group-hover:text-gray-300"
170
+ />
171
+ </button>
172
+
173
+ <button
174
+ class="w-full flex items-center justify-between p-3 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-900 transition-colors text-left group"
175
+ @click="navigateTo('/flows')"
176
+ >
177
+ <div class="flex items-center gap-3">
178
+ <div class="p-2 bg-purple-50 dark:bg-purple-950/50 rounded-lg group-hover:bg-purple-100 dark:group-hover:bg-purple-900/50 transition-colors">
179
+ <UIcon
180
+ name="i-lucide-git-branch"
181
+ class="w-4 h-4 text-purple-600 dark:text-purple-400"
182
+ />
183
+ </div>
184
+ <div>
185
+ <div class="text-sm font-medium text-gray-900 dark:text-gray-100">
186
+ Monitor Flows
187
+ </div>
188
+ <div class="text-xs text-gray-500 dark:text-gray-400">
189
+ Track workflow executions
190
+ </div>
191
+ </div>
192
+ </div>
193
+ <UIcon
194
+ name="i-lucide-chevron-right"
195
+ class="w-4 h-4 text-gray-400 group-hover:text-gray-600 dark:group-hover:text-gray-300"
196
+ />
197
+ </button>
198
+
199
+ <button
200
+ class="w-full flex items-center justify-between p-3 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-900 transition-colors text-left group"
201
+ @click="navigateTo('/triggers')"
202
+ >
203
+ <div class="flex items-center gap-3">
204
+ <div class="p-2 bg-amber-50 dark:bg-amber-950/50 rounded-lg group-hover:bg-amber-100 dark:group-hover:bg-amber-900/50 transition-colors">
205
+ <UIcon
206
+ name="i-lucide-zap"
207
+ class="w-4 h-4 text-amber-600 dark:text-amber-400"
208
+ />
209
+ </div>
210
+ <div>
211
+ <div class="text-sm font-medium text-gray-900 dark:text-gray-100">
212
+ Manage Triggers
213
+ </div>
214
+ <div class="text-xs text-gray-500 dark:text-gray-400">
215
+ Configure event triggers
216
+ </div>
217
+ </div>
218
+ </div>
219
+ <UIcon
220
+ name="i-lucide-chevron-right"
221
+ class="w-4 h-4 text-gray-400 group-hover:text-gray-600 dark:group-hover:text-gray-300"
222
+ />
223
+ </button>
224
+ </div>
225
+ </div>
226
+
227
+ <!-- System Overview -->
228
+ <div class="bg-white dark:bg-gray-950 rounded-xl border border-gray-200 dark:border-gray-800 p-6">
229
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center gap-2">
230
+ <UIcon
231
+ name="i-lucide-activity"
232
+ class="w-5 h-5 text-emerald-500"
233
+ />
234
+ System Overview
235
+ </h2>
236
+ <div class="space-y-3">
237
+ <!-- Jobs Processed -->
238
+ <div class="flex items-center justify-between p-3 rounded-lg bg-gray-50 dark:bg-gray-900">
239
+ <div class="flex items-center gap-3">
240
+ <div class="p-2 bg-blue-100 dark:bg-blue-950 rounded-lg">
241
+ <UIcon
242
+ name="i-lucide-package"
243
+ class="w-4 h-4 text-blue-600 dark:text-blue-400"
244
+ />
245
+ </div>
246
+ <div>
247
+ <div class="text-xs text-gray-500 dark:text-gray-400">
248
+ Jobs Processed
249
+ </div>
250
+ <div class="text-sm font-semibold text-gray-900 dark:text-gray-100">
251
+ {{ formatNumber(queuesStats?.completed || 0) }} completed
252
+ </div>
253
+ </div>
254
+ </div>
255
+ <div
256
+ v-if="queuesStats?.failed > 0"
257
+ class="text-xs text-red-600 dark:text-red-400"
258
+ >
259
+ {{ queuesStats?.failed }} failed
260
+ </div>
261
+ </div>
262
+
263
+ <!-- Flow Runs -->
264
+ <div class="flex items-center justify-between p-3 rounded-lg bg-gray-50 dark:bg-gray-900">
265
+ <div class="flex items-center gap-3">
266
+ <div class="p-2 bg-purple-100 dark:bg-purple-950 rounded-lg">
267
+ <UIcon
268
+ name="i-lucide-git-branch"
269
+ class="w-4 h-4 text-purple-600 dark:text-purple-400"
270
+ />
271
+ </div>
272
+ <div>
273
+ <div class="text-xs text-gray-500 dark:text-gray-400">
274
+ Total Flow Runs
275
+ </div>
276
+ <div class="text-sm font-semibold text-gray-900 dark:text-gray-100">
277
+ {{ formatNumber(flowsStats?.totalRuns || 0) }} runs
278
+ </div>
279
+ </div>
280
+ </div>
281
+ <div
282
+ v-if="flowsStats?.running > 0"
283
+ class="text-xs text-blue-600 dark:text-blue-400 font-semibold"
284
+ >
285
+ {{ flowsStats?.running }} running
286
+ </div>
287
+ </div>
288
+
289
+ <!-- Trigger Fires -->
290
+ <div class="flex items-center justify-between p-3 rounded-lg bg-gray-50 dark:bg-gray-900">
291
+ <div class="flex items-center gap-3">
292
+ <div class="p-2 bg-amber-100 dark:bg-amber-950 rounded-lg">
293
+ <UIcon
294
+ name="i-lucide-zap"
295
+ class="w-4 h-4 text-amber-600 dark:text-amber-400"
296
+ />
297
+ </div>
298
+ <div>
299
+ <div class="text-xs text-gray-500 dark:text-gray-400">
300
+ Trigger → Flow Starts
301
+ </div>
302
+ <div class="text-sm font-semibold text-gray-900 dark:text-gray-100">
303
+ {{ formatNumber(triggersStats?.totalFlowsStarted || 0) }} flows
304
+ </div>
305
+ </div>
306
+ </div>
307
+ <div class="text-xs text-gray-500 dark:text-gray-400">
308
+ from {{ formatNumber(triggersStats?.totalFires || 0) }} fires
309
+ </div>
310
+ </div>
311
+ </div>
312
+ </div>
313
+ </div>
314
+
315
+ <!-- Recent Items Grid -->
316
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
317
+ <!-- Recent Queues -->
318
+ <div class="bg-white dark:bg-gray-950 rounded-xl border border-gray-200 dark:border-gray-800 p-6">
319
+ <div class="flex items-center justify-between mb-4">
320
+ <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2">
321
+ <UIcon
322
+ name="i-lucide-app-window"
323
+ class="w-4 h-4 text-blue-500"
324
+ />
325
+ Active Queues
326
+ </h3>
327
+ <button
328
+ class="text-xs text-blue-600 dark:text-blue-400 hover:underline"
329
+ @click="navigateTo('/queues')"
330
+ >
331
+ View all
332
+ </button>
333
+ </div>
334
+ <div
335
+ v-if="!recentQueues || recentQueues.length === 0"
336
+ class="text-center py-8 text-sm text-gray-400"
337
+ >
338
+ <UIcon
339
+ name="i-lucide-inbox"
340
+ class="w-8 h-8 mx-auto mb-2 text-gray-300 dark:text-gray-700"
341
+ />
342
+ <p>No queues found</p>
343
+ </div>
344
+ <div
345
+ v-else
346
+ class="space-y-2"
347
+ >
348
+ <div
349
+ v-for="queue in recentQueues.slice(0, 5)"
350
+ :key="queue.name"
351
+ class="flex items-center justify-between p-2 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-900 transition-colors cursor-pointer"
352
+ @click="navigateTo(`/queues?queue=${encodeURIComponent(queue.name)}`)"
353
+ >
354
+ <div class="flex items-center gap-2 min-w-0">
355
+ <div
356
+ class="w-2 h-2 rounded-full"
357
+ :class="queue.isPaused ? 'bg-gray-400' : 'bg-blue-500'"
358
+ />
359
+ <span class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">{{ queue.name }}</span>
360
+ </div>
361
+ <div class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400 shrink-0">
362
+ <span
363
+ v-if="(queue.counts?.active || 0) + (queue.counts?.waiting || 0) > 0"
364
+ class="text-blue-600 dark:text-blue-400 font-medium"
365
+ >
366
+ {{ (queue.counts?.active || 0) + (queue.counts?.waiting || 0) }} pending
367
+ </span>
368
+ <span v-else>
369
+ {{ queue.counts?.completed || 0 }} completed
370
+ </span>
371
+ </div>
372
+ </div>
373
+ </div>
374
+ </div>
375
+
376
+ <!-- Recent Flow Runs -->
377
+ <div class="bg-white dark:bg-gray-950 rounded-xl border border-gray-200 dark:border-gray-800 p-6">
378
+ <div class="flex items-center justify-between mb-4">
379
+ <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2">
380
+ <UIcon
381
+ name="i-lucide-git-branch"
382
+ class="w-4 h-4 text-purple-500"
383
+ />
384
+ Recent Flow Runs
385
+ </h3>
386
+ <button
387
+ class="text-xs text-purple-600 dark:text-purple-400 hover:underline"
388
+ @click="navigateTo('/flows')"
389
+ >
390
+ View all
391
+ </button>
392
+ </div>
393
+ <div
394
+ v-if="!recentFlowRuns || recentFlowRuns.length === 0"
395
+ class="text-center py-8 text-sm text-gray-400"
396
+ >
397
+ <UIcon
398
+ name="i-lucide-inbox"
399
+ class="w-8 h-8 mx-auto mb-2 text-gray-300 dark:text-gray-700"
400
+ />
401
+ <p>No flow runs found</p>
402
+ </div>
403
+ <div
404
+ v-else
405
+ class="space-y-2"
406
+ >
407
+ <div
408
+ v-for="run in recentFlowRuns"
409
+ :key="run.id"
410
+ class="flex items-center justify-between p-2 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-900 transition-colors cursor-pointer"
411
+ @click="navigateTo(`/flows/${encodeURIComponent(run.flowName)}`)"
412
+ >
413
+ <div class="flex items-center gap-2 min-w-0">
414
+ <div
415
+ class="w-2 h-2 rounded-full"
416
+ :class="{
417
+ 'bg-purple-500': (run.stats?.running || 0) > 0,
418
+ 'bg-amber-500': (run.stats?.awaiting || 0) > 0 && (run.stats?.running || 0) === 0,
419
+ 'bg-red-500': (run.stats?.failure || 0) > 0 && (run.stats?.running || 0) === 0 && (run.stats?.awaiting || 0) === 0,
420
+ 'bg-emerald-500': (run.stats?.success || 0) > 0 && (run.stats?.running || 0) === 0 && (run.stats?.awaiting || 0) === 0 && (run.stats?.failure || 0) === 0,
421
+ 'bg-gray-400': (run.stats?.total || 0) === 0
422
+ }"
423
+ />
424
+ <span class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">{{ run.flowDisplayName || run.flowName }}</span>
425
+ </div>
426
+ <div class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400 shrink-0">
427
+ <span
428
+ v-if="(run.stats?.running || 0) > 0"
429
+ class="text-purple-600 dark:text-purple-400 font-medium"
430
+ >
431
+ {{ run.stats?.running }} running
432
+ </span>
433
+ <span
434
+ v-else-if="(run.stats?.awaiting || 0) > 0"
435
+ class="text-amber-600 dark:text-amber-400 font-medium"
436
+ >
437
+ {{ run.stats?.awaiting }} awaiting
438
+ </span>
439
+ <span v-else>
440
+ {{ run.stats?.total || 0 }} runs
441
+ </span>
442
+ </div>
443
+ </div>
444
+ </div>
445
+ </div>
446
+
447
+ <!-- Recent Triggers -->
448
+ <div class="bg-white dark:bg-gray-950 rounded-xl border border-gray-200 dark:border-gray-800 p-6">
449
+ <div class="flex items-center justify-between mb-4">
450
+ <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2">
451
+ <UIcon
452
+ name="i-lucide-zap"
453
+ class="w-4 h-4 text-amber-500"
454
+ />
455
+ Active Triggers
456
+ </h3>
457
+ <button
458
+ class="text-xs text-amber-600 dark:text-amber-400 hover:underline"
459
+ @click="navigateTo('/triggers')"
460
+ >
461
+ View all
462
+ </button>
463
+ </div>
464
+ <div
465
+ v-if="!recentTriggers || recentTriggers.length === 0"
466
+ class="text-center py-8 text-sm text-gray-400"
467
+ >
468
+ <UIcon
469
+ name="i-lucide-inbox"
470
+ class="w-8 h-8 mx-auto mb-2 text-gray-300 dark:text-gray-700"
471
+ />
472
+ <p>No triggers found</p>
473
+ </div>
474
+ <div
475
+ v-else
476
+ class="space-y-2"
477
+ >
478
+ <div
479
+ v-for="trigger in recentTriggers.slice(0, 5)"
480
+ :key="trigger.name"
481
+ class="flex items-center justify-between p-2 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-900 transition-colors cursor-pointer"
482
+ @click="navigateTo(`/triggers/${encodeURIComponent(trigger.name)}`)"
483
+ >
484
+ <div class="flex items-center gap-2 min-w-0">
485
+ <div class="w-2 h-2 rounded-full bg-amber-500" />
486
+ <span class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">{{ trigger.displayName || trigger.name }}</span>
487
+ </div>
488
+ <span class="text-xs text-gray-500 dark:text-gray-400 shrink-0">{{ trigger.stats?.totalFires || 0 }}</span>
489
+ </div>
490
+ </div>
491
+ </div>
492
+ </div>
493
+ </div>
494
+ </div>
495
+ </div>
496
+ </template>
497
+
498
+ <script setup>
499
+ import { ref, computed, onMounted } from "#imports";
500
+ import { UIcon } from "#components";
501
+ import { useComponentRouter } from "../composables/useComponentRouter";
502
+ import { useQueuesWebSocket } from "../composables/useQueuesWebSocket";
503
+ import { useFlowWebSocket } from "../composables/useFlowWebSocket";
504
+ import { useTriggerWebSocket } from "../composables/useTriggerWebSocket";
505
+ const router = useComponentRouter();
506
+ const queues = ref([]);
507
+ const flows = ref([]);
508
+ const triggers = ref([]);
509
+ const { connected: queuesConnected, subscribe: subscribeQueue } = useQueuesWebSocket();
510
+ const { connected: flowsConnected, subscribeStats: subscribeFlowStats } = useFlowWebSocket();
511
+ const { connected: triggersConnected, subscribeStats: subscribeTriggerStats } = useTriggerWebSocket();
512
+ onMounted(async () => {
513
+ if (import.meta.server) return;
514
+ const [queuesResponse, flowsResponse, triggersResponse] = await Promise.all([
515
+ $fetch("/api/_queues"),
516
+ $fetch("/api/_flows"),
517
+ $fetch("/api/_triggers")
518
+ ]);
519
+ queues.value = Array.isArray(queuesResponse) ? queuesResponse : [];
520
+ const analyzedFlows = Array.isArray(flowsResponse) ? flowsResponse : [];
521
+ flows.value = analyzedFlows.map((flow) => ({
522
+ id: flow.id,
523
+ name: flow.id,
524
+ // Add name property for consistency
525
+ displayName: flow.id,
526
+ entry: flow.entry,
527
+ steps: flow.analyzed?.steps || flow.steps || {},
528
+ levels: flow.analyzed?.levels || [],
529
+ maxLevel: flow.analyzed?.maxLevel || 0,
530
+ stallTimeout: flow.analyzed?.stallTimeout,
531
+ awaitPatterns: flow.analyzed?.awaitPatterns,
532
+ hasAwait: flow.analyzed?.awaitPatterns?.steps?.length > 0 || false,
533
+ timeout: flow.timeout,
534
+ stats: { total: 0, success: 0, failure: 0, cancel: 0, running: 0, awaiting: 0 }
535
+ }));
536
+ triggers.value = (Array.isArray(triggersResponse) ? triggersResponse : []).map((trigger) => ({
537
+ ...trigger,
538
+ stats: trigger.stats || {
539
+ totalFires: 0,
540
+ totalFlowsStarted: 0,
541
+ lastFiredAt: null,
542
+ activeSubscribers: 0
543
+ }
544
+ }));
545
+ for (const queue of queues.value) {
546
+ subscribeQueue(
547
+ queue.name,
548
+ (counts) => {
549
+ queues.value = queues.value.map(
550
+ (q) => q.name === queue.name ? { ...q, counts } : q
551
+ );
552
+ }
553
+ );
554
+ }
555
+ subscribeFlowStats({
556
+ onInitial: (data) => {
557
+ updateFlowStats(data);
558
+ },
559
+ onUpdate: (data) => {
560
+ updateFlowStats(data);
561
+ }
562
+ });
563
+ subscribeTriggerStats({
564
+ onInitial: (data) => {
565
+ updateTriggerStats(data);
566
+ },
567
+ onUpdate: (data) => {
568
+ updateTriggerStats(data);
569
+ }
570
+ });
571
+ });
572
+ function updateFlowStats(data) {
573
+ const flowId = data?.id;
574
+ if (!flowId) {
575
+ console.warn("[Dashboard] No flow ID in stats update:", data);
576
+ return;
577
+ }
578
+ if (!flows.value || flows.value.length === 0) {
579
+ console.warn("[Dashboard] Flows array not initialized yet");
580
+ return;
581
+ }
582
+ const flowIndex = flows.value.findIndex((f) => f.id === flowId);
583
+ if (flowIndex === -1) {
584
+ console.warn("[Dashboard] Flow not found in list:", flowId, "available:", flows.value.map((f) => f.id));
585
+ return;
586
+ }
587
+ const metadata = data?.metadata;
588
+ if (!metadata) {
589
+ console.warn("[Dashboard] No metadata in update:", data);
590
+ return;
591
+ }
592
+ const newStats = {
593
+ total: metadata.stats?.total || metadata.total || metadata["stats.total"] || 0,
594
+ success: metadata.stats?.success || metadata.success || metadata["stats.success"] || 0,
595
+ failure: metadata.stats?.failure || metadata.failure || metadata["stats.failure"] || 0,
596
+ cancel: metadata.stats?.cancel || metadata.cancel || metadata["stats.cancel"] || 0,
597
+ running: metadata.stats?.running || metadata.running || metadata["stats.running"] || 0,
598
+ awaiting: metadata.stats?.awaiting || metadata.awaiting || metadata["stats.awaiting"] || 0
599
+ };
600
+ const updatedFlow = {
601
+ ...flows.value[flowIndex],
602
+ stats: newStats,
603
+ lastRunAt: metadata.lastRunAt,
604
+ lastCompletedAt: metadata.lastCompletedAt
605
+ };
606
+ flows.value = [
607
+ ...flows.value.slice(0, flowIndex),
608
+ updatedFlow,
609
+ ...flows.value.slice(flowIndex + 1)
610
+ ];
611
+ }
612
+ function updateTriggerStats(data) {
613
+ const triggerName = data?.id;
614
+ if (!triggerName) {
615
+ console.warn("[Dashboard] No trigger name in stats update:", data);
616
+ return;
617
+ }
618
+ if (!triggers.value || triggers.value.length === 0) {
619
+ console.warn("[Dashboard] Triggers array not initialized yet, storing for later");
620
+ return;
621
+ }
622
+ const triggerIndex = triggers.value.findIndex((t) => t.name === triggerName);
623
+ if (triggerIndex === -1) {
624
+ console.warn("[Dashboard] Trigger not found in list:", triggerName, "available:", triggers.value.map((t) => t.name));
625
+ return;
626
+ }
627
+ const metadata = data?.metadata;
628
+ if (!metadata) {
629
+ console.warn("[Dashboard] No metadata in update:", data);
630
+ return;
631
+ }
632
+ const newStats = {
633
+ totalFires: metadata.stats?.totalFires || metadata.totalFires || metadata["stats.totalFires"] || 0,
634
+ totalFlowsStarted: metadata.stats?.totalFlowsStarted || metadata.totalFlowsStarted || metadata["stats.totalFlowsStarted"] || 0,
635
+ last24h: metadata.stats?.last24h || metadata.last24h || metadata["stats.last24h"] || 0,
636
+ successCount: metadata.stats?.successCount || metadata.successCount || metadata["stats.successCount"] || 0,
637
+ failureCount: metadata.stats?.failureCount || metadata.failureCount || metadata["stats.failureCount"] || 0,
638
+ lastFiredAt: metadata.stats?.lastFiredAt || metadata.lastFiredAt || metadata["stats.lastFiredAt"],
639
+ activeSubscribers: metadata.stats?.activeSubscribers || metadata.activeSubscribers || metadata["stats.activeSubscribers"] || 0
640
+ };
641
+ const updatedTrigger = {
642
+ ...triggers.value[triggerIndex],
643
+ stats: newStats,
644
+ lastActivityAt: metadata.lastActivityAt
645
+ };
646
+ triggers.value = [
647
+ ...triggers.value.slice(0, triggerIndex),
648
+ updatedTrigger,
649
+ ...triggers.value.slice(triggerIndex + 1)
650
+ ];
651
+ }
652
+ const queuesStats = computed(() => {
653
+ const pending = queues.value.reduce((sum, q) => {
654
+ const counts = q.counts || {};
655
+ return sum + (counts.active || 0) + (counts.waiting || 0) + (counts.delayed || 0);
656
+ }, 0);
657
+ const completed = queues.value.reduce((sum, q) => sum + (q.counts?.completed || 0), 0);
658
+ const failed = queues.value.reduce((sum, q) => sum + (q.counts?.failed || 0), 0);
659
+ return {
660
+ total: queues.value.length,
661
+ pending,
662
+ completed,
663
+ failed
664
+ };
665
+ });
666
+ const flowsStats = computed(() => {
667
+ const totalRuns = flows.value.reduce((sum, f) => sum + (f.stats?.total || 0), 0);
668
+ const running = flows.value.reduce((sum, f) => sum + (f.stats?.running || 0), 0);
669
+ const awaiting = flows.value.reduce((sum, f) => sum + (f.stats?.awaiting || 0), 0);
670
+ const success = flows.value.reduce((sum, f) => sum + (f.stats?.success || 0), 0);
671
+ const failure = flows.value.reduce((sum, f) => sum + (f.stats?.failure || 0), 0);
672
+ return {
673
+ total: flows.value.length,
674
+ active: flows.value.filter((f) => f.enabled !== false).length,
675
+ totalRuns,
676
+ running,
677
+ awaiting,
678
+ success,
679
+ failure
680
+ };
681
+ });
682
+ const triggersStats = computed(() => {
683
+ const totalFires = triggers.value.reduce((sum, t) => sum + (t.stats?.totalFires || 0), 0);
684
+ const totalFlowsStarted = triggers.value.reduce((sum, t) => sum + (t.stats?.totalFlowsStarted || 0), 0);
685
+ const totalSubscriptions = triggers.value.reduce((sum, t) => sum + (t.subscriptionCount || 0), 0);
686
+ return {
687
+ total: triggers.value.length,
688
+ totalFires,
689
+ totalFlowsStarted,
690
+ totalSubscriptions
691
+ };
692
+ });
693
+ const recentQueues = computed(() => {
694
+ return queues.value.map((q) => ({
695
+ ...q,
696
+ displayName: q.name,
697
+ status: q.isPaused ? "paused" : "active"
698
+ })).sort((a, b) => {
699
+ const aPending = (a.counts?.active || 0) + (a.counts?.waiting || 0);
700
+ const bPending = (b.counts?.active || 0) + (b.counts?.waiting || 0);
701
+ return bPending - aPending;
702
+ });
703
+ });
704
+ const recentFlowRuns = computed(() => {
705
+ return [...flows.value].filter((f) => f.stats?.total > 0).sort((a, b) => {
706
+ const aActive = (a.stats?.running || 0) + (a.stats?.awaiting || 0);
707
+ const bActive = (b.stats?.running || 0) + (b.stats?.awaiting || 0);
708
+ if (aActive !== bActive) return bActive - aActive;
709
+ const aTime = a.lastRunAt || 0;
710
+ const bTime = b.lastRunAt || 0;
711
+ return bTime - aTime;
712
+ }).slice(0, 5).map((f) => ({
713
+ id: f.name,
714
+ flowName: f.name,
715
+ flowDisplayName: f.displayName || f.name,
716
+ stats: f.stats
717
+ }));
718
+ });
719
+ const recentTriggers = computed(() => {
720
+ return [...triggers.value].sort((a, b) => {
721
+ const aFires = a.stats?.totalFires || 0;
722
+ const bFires = b.stats?.totalFires || 0;
723
+ return bFires - aFires;
724
+ });
725
+ });
726
+ const isConnected = computed(() => {
727
+ return queuesConnected.value || flowsConnected.value || triggersConnected.value;
728
+ });
729
+ const navigateTo = (path) => {
730
+ router.push(path);
731
+ };
732
+ const formatNumber = (num) => {
733
+ if (num == null) return "0";
734
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
735
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
736
+ return num.toString();
737
+ };
738
+ </script>