@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.
Files changed (95) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +3 -2
  3. package/dist/runtime/app/components/DashboardCard.d.vue.ts +1 -1
  4. package/dist/runtime/app/components/DashboardCard.vue.d.ts +1 -1
  5. package/dist/runtime/app/composables/useWorkers.d.ts +57 -0
  6. package/dist/runtime/app/composables/useWorkers.js +42 -0
  7. package/dist/runtime/app/pages/dashboard.vue +1 -654
  8. package/dist/runtime/app/pages/index.vue +25 -41
  9. package/dist/runtime/app/pages/workers.vue +458 -0
  10. package/dist/runtime/server/api/_workers/index.get.d.ts +8 -0
  11. package/dist/runtime/server/api/_workers/index.get.js +14 -0
  12. package/package.json +12 -11
  13. package/dist/runtime/app/components/ComponentRouter.d.vue.ts +0 -46
  14. package/dist/runtime/app/components/ComponentRouter.vue +0 -26
  15. package/dist/runtime/app/components/ComponentRouter.vue.d.ts +0 -46
  16. package/dist/runtime/app/components/ComponentShell.d.vue.ts +0 -23
  17. package/dist/runtime/app/components/ComponentShell.vue +0 -97
  18. package/dist/runtime/app/components/ComponentShell.vue.d.ts +0 -23
  19. package/dist/runtime/app/components/ConfirmDialog.d.vue.ts +0 -33
  20. package/dist/runtime/app/components/ConfirmDialog.vue +0 -120
  21. package/dist/runtime/app/components/ConfirmDialog.vue.d.ts +0 -33
  22. package/dist/runtime/app/composables/useComponentRouter.d.ts +0 -46
  23. package/dist/runtime/app/composables/useComponentRouter.js +0 -248
  24. package/dist/runtime/app/pages/flows/[name].vue +0 -750
  25. package/dist/runtime/app/pages/flows/index.d.vue.ts +0 -3
  26. package/dist/runtime/app/pages/flows/index.vue +0 -381
  27. package/dist/runtime/app/pages/flows/index.vue.d.ts +0 -3
  28. package/dist/runtime/app/pages/queues/index.d.vue.ts +0 -3
  29. package/dist/runtime/app/pages/queues/index.vue +0 -236
  30. package/dist/runtime/app/pages/queues/index.vue.d.ts +0 -3
  31. package/dist/runtime/app/pages/queues/job.d.vue.ts +0 -3
  32. package/dist/runtime/app/pages/queues/job.vue +0 -261
  33. package/dist/runtime/app/pages/queues/job.vue.d.ts +0 -3
  34. package/dist/runtime/app/pages/queues/jobs.d.vue.ts +0 -3
  35. package/dist/runtime/app/pages/queues/jobs.vue +0 -595
  36. package/dist/runtime/app/pages/queues/jobs.vue.d.ts +0 -3
  37. package/dist/runtime/app/pages/settings/scheduler.d.vue.ts +0 -3
  38. package/dist/runtime/app/pages/settings/scheduler.vue +0 -310
  39. package/dist/runtime/app/pages/settings/scheduler.vue.d.ts +0 -3
  40. package/dist/runtime/app/pages/triggers/[name]/edit.d.vue.ts +0 -3
  41. package/dist/runtime/app/pages/triggers/[name]/edit.vue +0 -429
  42. package/dist/runtime/app/pages/triggers/[name]/edit.vue.d.ts +0 -3
  43. package/dist/runtime/app/pages/triggers/[name].d.vue.ts +0 -3
  44. package/dist/runtime/app/pages/triggers/[name].vue +0 -870
  45. package/dist/runtime/app/pages/triggers/[name].vue.d.ts +0 -3
  46. package/dist/runtime/app/pages/triggers/index.d.vue.ts +0 -3
  47. package/dist/runtime/app/pages/triggers/index.vue +0 -525
  48. package/dist/runtime/app/pages/triggers/index.vue.d.ts +0 -3
  49. package/dist/runtime/app/pages/triggers/new.d.vue.ts +0 -3
  50. package/dist/runtime/app/pages/triggers/new.vue +0 -610
  51. package/dist/runtime/app/pages/triggers/new.vue.d.ts +0 -3
  52. package/dist/runtime/server/api/_flows/[name]/clear-history.delete.d.ts +0 -10
  53. package/dist/runtime/server/api/_flows/[name]/clear-history.delete.js +0 -49
  54. package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.d.ts +0 -2
  55. package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.js +0 -21
  56. package/dist/runtime/server/api/_flows/[name]/runs/[runId]/restart.post.d.ts +0 -2
  57. package/dist/runtime/server/api/_flows/[name]/runs/[runId]/restart.post.js +0 -21
  58. package/dist/runtime/server/api/_flows/[name]/runs.get.d.ts +0 -17
  59. package/dist/runtime/server/api/_flows/[name]/runs.get.js +0 -64
  60. package/dist/runtime/server/api/_flows/[name]/start.post.d.ts +0 -2
  61. package/dist/runtime/server/api/_flows/[name]/start.post.js +0 -9
  62. package/dist/runtime/server/api/_flows/index.get.d.ts +0 -7
  63. package/dist/runtime/server/api/_flows/index.get.js +0 -5
  64. package/dist/runtime/server/api/_flows/recent-runs.get.d.ts +0 -15
  65. package/dist/runtime/server/api/_flows/recent-runs.get.js +0 -67
  66. package/dist/runtime/server/api/_flows/ws.d.ts +0 -80
  67. package/dist/runtime/server/api/_flows/ws.js +0 -309
  68. package/dist/runtime/server/api/_queues/[name]/job/[id].get.d.ts +0 -2
  69. package/dist/runtime/server/api/_queues/[name]/job/[id].get.js +0 -14
  70. package/dist/runtime/server/api/_queues/[name]/job/index.get.d.ts +0 -2
  71. package/dist/runtime/server/api/_queues/[name]/job/index.get.js +0 -39
  72. package/dist/runtime/server/api/_queues/index.get.d.ts +0 -2
  73. package/dist/runtime/server/api/_queues/index.get.js +0 -106
  74. package/dist/runtime/server/api/_queues/ws.d.ts +0 -48
  75. package/dist/runtime/server/api/_queues/ws.js +0 -215
  76. package/dist/runtime/server/api/_scheduler/jobs.get.d.ts +0 -19
  77. package/dist/runtime/server/api/_scheduler/jobs.get.js +0 -36
  78. package/dist/runtime/server/api/_triggers/[name]/events.get.d.ts +0 -6
  79. package/dist/runtime/server/api/_triggers/[name]/events.get.js +0 -43
  80. package/dist/runtime/server/api/_triggers/[name]/index.get.d.ts +0 -6
  81. package/dist/runtime/server/api/_triggers/[name]/index.get.js +0 -76
  82. package/dist/runtime/server/api/_triggers/[name].delete.d.ts +0 -7
  83. package/dist/runtime/server/api/_triggers/[name].delete.js +0 -37
  84. package/dist/runtime/server/api/_triggers/[name].patch.d.ts +0 -7
  85. package/dist/runtime/server/api/_triggers/[name].patch.js +0 -117
  86. package/dist/runtime/server/api/_triggers/index.get.d.ts +0 -6
  87. package/dist/runtime/server/api/_triggers/index.get.js +0 -44
  88. package/dist/runtime/server/api/_triggers/index.post.d.ts +0 -7
  89. package/dist/runtime/server/api/_triggers/index.post.js +0 -124
  90. package/dist/runtime/server/api/_triggers/stats.get.d.ts +0 -6
  91. package/dist/runtime/server/api/_triggers/stats.get.js +0 -41
  92. package/dist/runtime/server/api/_triggers/ws.d.ts +0 -74
  93. package/dist/runtime/server/api/_triggers/ws.js +0 -315
  94. /package/dist/runtime/app/pages/{flows/[name].d.vue.ts → workers.d.vue.ts} +0 -0
  95. /package/dist/runtime/app/pages/{flows/[name].vue.d.ts → workers.vue.d.ts} +0 -0
@@ -1,870 +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="getTriggerIcon(trigger?.type || 'event')"
19
- class="w-5 h-5"
20
- :class="getTriggerIconColor(trigger?.type || 'event')"
21
- />
22
- <span>{{ trigger?.displayName || trigger?.name }}</span>
23
- </h1>
24
- <div class="flex items-center gap-2 mt-1">
25
- <UBadge
26
- v-if="trigger"
27
- :label="trigger.type"
28
- :color="getTriggerTypeColor(trigger.type)"
29
- variant="subtle"
30
- size="xs"
31
- />
32
- <UBadge
33
- v-if="trigger"
34
- :label="trigger.scope"
35
- color="neutral"
36
- variant="subtle"
37
- size="xs"
38
- />
39
- <UBadge
40
- v-if="trigger"
41
- :label="trigger.status"
42
- :color="trigger.status === 'active' ? 'success' : trigger.status === 'inactive' ? 'warning' : 'neutral'"
43
- variant="subtle"
44
- size="xs"
45
- />
46
- </div>
47
- </div>
48
- </div>
49
- <div class="flex items-center gap-3">
50
- <UButton
51
- icon="i-lucide-pencil"
52
- color="neutral"
53
- variant="outline"
54
- size="sm"
55
- @click="goToEdit"
56
- >
57
- Edit
58
- </UButton>
59
- <div
60
- v-if="isConnected"
61
- class="flex items-center gap-1.5 text-xs text-emerald-600 dark:text-emerald-400"
62
- >
63
- <div class="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
64
- <span>Live</span>
65
- </div>
66
- <div
67
- v-else-if="isReconnecting"
68
- class="flex items-center gap-1.5 text-xs text-amber-600 dark:text-amber-400"
69
- >
70
- <div class="w-2 h-2 rounded-full bg-amber-500 animate-pulse" />
71
- <span>Reconnecting...</span>
72
- </div>
73
- </div>
74
- </div>
75
- </div>
76
-
77
- <!-- Main Content -->
78
- <div class="flex-1 min-h-0 overflow-hidden">
79
- <div
80
- v-if="status === 'pending' && !trigger"
81
- class="h-full flex items-center justify-center"
82
- >
83
- <div class="text-center">
84
- <UIcon
85
- name="i-lucide-loader-2"
86
- class="w-8 h-8 animate-spin mx-auto mb-2 text-gray-400"
87
- />
88
- <p class="text-sm text-gray-500 dark:text-gray-400">
89
- Loading trigger...
90
- </p>
91
- </div>
92
- </div>
93
-
94
- <div
95
- v-else-if="!trigger"
96
- class="h-full flex items-center justify-center"
97
- >
98
- <div class="text-center">
99
- <UIcon
100
- name="i-lucide-alert-circle"
101
- class="w-8 h-8 mx-auto mb-2 text-gray-300 dark:text-gray-700"
102
- />
103
- <p class="text-sm text-gray-500 dark:text-gray-400">
104
- Trigger not found
105
- </p>
106
- </div>
107
- </div>
108
-
109
- <div
110
- v-else
111
- class="h-full flex gap-px bg-gray-200 dark:bg-gray-800"
112
- >
113
- <!-- Left: Events List -->
114
- <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">
115
- <div class="px-4 py-3 border-b border-gray-200 dark:border-gray-800 flex items-center justify-between shrink-0">
116
- <h2 class="text-sm font-medium text-gray-900 dark:text-gray-100">
117
- Events
118
- </h2>
119
- <div class="flex items-center gap-2">
120
- <USelectMenu
121
- v-model="eventTypeFilter"
122
- :items="eventTypeFilterOptions"
123
- value-key="value"
124
- placeholder="All Events"
125
- size="xs"
126
- class="w-40"
127
- >
128
- <template #label>
129
- <div class="flex items-center gap-2">
130
- <UIcon
131
- :name="getFilterIcon(eventTypeFilter)"
132
- class="w-4 h-4"
133
- :class="getFilterIconColor(eventTypeFilter)"
134
- />
135
- <span class="text-xs">{{ getFilterLabel(eventTypeFilter) }}</span>
136
- </div>
137
- </template>
138
- <template #option="{ option }">
139
- <div class="flex items-center gap-2">
140
- <UIcon
141
- :name="getFilterIcon(option.value)"
142
- class="w-4 h-4"
143
- :class="getFilterIconColor(option.value)"
144
- />
145
- <span>{{ option.label }}</span>
146
- </div>
147
- </template>
148
- </USelectMenu>
149
- </div>
150
- </div>
151
-
152
- <div
153
- v-if="eventsStatus === 'pending' && !events"
154
- class="flex-1 flex items-center justify-center"
155
- >
156
- <div class="text-center">
157
- <UIcon
158
- name="i-lucide-loader-2"
159
- class="w-8 h-8 animate-spin mx-auto mb-2 text-gray-400"
160
- />
161
- <p class="text-sm text-gray-500 dark:text-gray-400">
162
- Loading events...
163
- </p>
164
- </div>
165
- </div>
166
-
167
- <div
168
- v-else-if="!events || events.events.length === 0"
169
- class="flex-1 flex items-center justify-center"
170
- >
171
- <div class="text-center">
172
- <UIcon
173
- name="i-lucide-inbox"
174
- class="w-8 h-8 mx-auto mb-2 text-gray-300 dark:text-gray-700"
175
- />
176
- <p class="text-sm text-gray-500 dark:text-gray-400">
177
- No events yet
178
- </p>
179
- </div>
180
- </div>
181
-
182
- <div
183
- v-else
184
- class="flex-1 min-h-0 overflow-y-auto"
185
- >
186
- <div class="divide-y divide-gray-100 dark:divide-gray-800">
187
- <SelectableListItem
188
- v-for="(event, idx) in paginatedEvents"
189
- :key="idx"
190
- :selected="!!(selectedEvent && selectedEvent.type === event.type && (selectedEvent.ts || selectedEvent.timestamp) === (event.ts || event.timestamp))"
191
- :icon="getEventIcon(event.type)"
192
- :icon-class="getEventIconColor(event.type)"
193
- :title="event.type"
194
- :subtitle="formatDate(event.ts || event.timestamp)"
195
- :badge="event.type.split('.')[1] || 'event'"
196
- :badge-color="getEventBadgeColor(event.type)"
197
- :meta="formatTime(event.ts || event.timestamp)"
198
- @click="selectEvent(event)"
199
- />
200
- </div>
201
- </div>
202
-
203
- <!-- Pagination Footer -->
204
- <div
205
- v-if="events && (events.total || events.count) > eventsPerPage"
206
- class="border-t border-gray-200 dark:border-gray-800 px-4 py-3 flex items-center justify-center shrink-0"
207
- >
208
- <UPagination
209
- v-model:page="currentPage"
210
- :items-per-page="eventsPerPage"
211
- :total="events.total || events.count"
212
- size="xs"
213
- />
214
- </div>
215
- </div>
216
-
217
- <!-- Right: Overview or Event Details -->
218
- <div class="flex-1 min-w-0 bg-white dark:bg-gray-950 flex flex-col min-h-0 overflow-hidden">
219
- <div class="px-4 py-2.5 border-b border-gray-200 dark:border-gray-800 shrink-0">
220
- <div class="flex items-center justify-between">
221
- <UTabs
222
- v-model="activeTab"
223
- :items="tabItems"
224
- size="xs"
225
- :ui="{
226
- root: 'gap-0',
227
- trigger: 'px-2 py-0.5'
228
- }"
229
- />
230
- </div>
231
- </div>
232
-
233
- <div class="flex-1 min-h-0 overflow-y-auto">
234
- <!-- Overview Tab -->
235
- <div
236
- v-if="activeTab === 'overview'"
237
- class="p-6 space-y-6"
238
- >
239
- <!-- Stats Cards -->
240
- <div>
241
- <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 mb-4">
242
- Trigger Statistics
243
- </h3>
244
- <div class="grid grid-cols-2 gap-4">
245
- <StatCard
246
- icon="i-lucide-zap"
247
- :count="trigger.stats.totalFires"
248
- label="Total Fires"
249
- variant="gray"
250
- />
251
- <StatCard
252
- icon="i-lucide-git-branch"
253
- :count="trigger.stats.activeSubscribers"
254
- label="Active Subscribers"
255
- variant="purple"
256
- />
257
- <StatCard
258
- v-if="trigger.stats.lastFiredAt"
259
- icon="i-lucide-clock"
260
- :count="formatTime(new Date(trigger.stats.lastFiredAt).getTime())"
261
- label="Last Fired"
262
- variant="blue"
263
- />
264
- <StatCard
265
- icon="i-lucide-users"
266
- :count="trigger.subscriptionCount"
267
- label="Subscriptions"
268
- variant="emerald"
269
- />
270
- </div>
271
- </div>
272
-
273
- <!-- Description -->
274
- <div
275
- v-if="trigger.description"
276
- class="bg-blue-50 dark:bg-blue-950/30 border border-blue-200 dark:border-blue-800 rounded-lg p-4"
277
- >
278
- <div class="flex items-start gap-2">
279
- <UIcon
280
- name="i-lucide-info"
281
- class="w-4 h-4 text-blue-600 dark:text-blue-400 mt-0.5 shrink-0"
282
- />
283
- <p class="text-sm text-blue-900 dark:text-blue-100">
284
- {{ trigger.description }}
285
- </p>
286
- </div>
287
- </div>
288
-
289
- <!-- Configuration -->
290
- <div>
291
- <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 mb-4">
292
- Configuration
293
- </h3>
294
- <div class="bg-gray-50 dark:bg-gray-900/50 rounded-lg p-4 space-y-3">
295
- <div class="flex items-center justify-between py-2 border-b border-gray-200 dark:border-gray-800">
296
- <span class="text-sm text-gray-600 dark:text-gray-400">Name</span>
297
- <span class="text-sm font-medium font-mono text-gray-900 dark:text-gray-100">{{ trigger.name }}</span>
298
- </div>
299
- <div class="flex items-center justify-between py-2 border-b border-gray-200 dark:border-gray-800">
300
- <span class="text-sm text-gray-600 dark:text-gray-400">Type</span>
301
- <UBadge
302
- :label="trigger.type"
303
- :color="getTriggerTypeColor(trigger.type)"
304
- variant="subtle"
305
- size="xs"
306
- />
307
- </div>
308
- <div class="flex items-center justify-between py-2 border-b border-gray-200 dark:border-gray-800">
309
- <span class="text-sm text-gray-600 dark:text-gray-400">Scope</span>
310
- <span class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ trigger.scope }}</span>
311
- </div>
312
- <div
313
- v-if="trigger.source"
314
- class="flex items-center justify-between py-2 border-b border-gray-200 dark:border-gray-800"
315
- >
316
- <span class="text-sm text-gray-600 dark:text-gray-400">Source</span>
317
- <span class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ trigger.source }}</span>
318
- </div>
319
- <div
320
- v-if="trigger.registeredAt"
321
- class="flex items-center justify-between py-2 border-b border-gray-200 dark:border-gray-800"
322
- >
323
- <span class="text-sm text-gray-600 dark:text-gray-400">Registered</span>
324
- <span class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ formatDate(trigger.registeredAt) }}</span>
325
- </div>
326
- <div
327
- v-if="trigger.stats.lastFiredAt"
328
- class="flex items-center justify-between py-2 border-b border-gray-200 dark:border-gray-800"
329
- >
330
- <span class="text-sm text-gray-600 dark:text-gray-400">Last Fired</span>
331
- <span class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ formatTime(trigger.stats.lastFiredAt) }}</span>
332
- </div>
333
- <div
334
- v-if="trigger.lastActivityAt"
335
- class="flex items-center justify-between py-2"
336
- >
337
- <span class="text-sm text-gray-600 dark:text-gray-400">Last Modified</span>
338
- <span class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ formatTime(trigger.lastActivityAt) }}</span>
339
- </div>
340
- </div>
341
- </div>
342
-
343
- <!-- Webhook Config -->
344
- <div v-if="trigger.webhook">
345
- <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 mb-3">
346
- Webhook Configuration
347
- </h3>
348
- <div class="space-y-3">
349
- <div class="bg-gray-50 dark:bg-gray-900/50 rounded-lg p-3">
350
- <div class="flex items-center justify-between mb-2">
351
- <span class="text-xs text-gray-500 dark:text-gray-400">Webhook URL</span>
352
- <UButton
353
- v-if="trigger.webhook.fullUrl"
354
- icon="i-lucide-copy"
355
- size="xs"
356
- color="neutral"
357
- variant="ghost"
358
- @click="copyToClipboard(trigger.webhook.fullUrl)"
359
- />
360
- </div>
361
- <code class="text-xs font-mono text-blue-600 dark:text-blue-400 break-all">
362
- {{ trigger.webhook.fullUrl || trigger.webhook.path }}
363
- </code>
364
- </div>
365
- <div class="flex items-center justify-between py-2 border-b border-gray-100 dark:border-gray-800">
366
- <span class="text-sm text-gray-500 dark:text-gray-400">Method</span>
367
- <UBadge
368
- :label="trigger.webhook.method || 'POST'"
369
- color="neutral"
370
- variant="subtle"
371
- size="xs"
372
- />
373
- </div>
374
- </div>
375
- </div>
376
-
377
- <!-- Schedule Config -->
378
- <div v-if="trigger.schedule">
379
- <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 mb-3">
380
- Schedule Configuration
381
- </h3>
382
- <div class="space-y-2 text-sm">
383
- <div
384
- v-if="trigger.schedule.cron"
385
- class="flex items-center justify-between py-2 border-b border-gray-100 dark:border-gray-800"
386
- >
387
- <span class="text-gray-500 dark:text-gray-400">Cron Expression</span>
388
- <span class="font-mono text-gray-900 dark:text-gray-100">{{ trigger.schedule.cron }}</span>
389
- </div>
390
- <div
391
- v-if="trigger.schedule.interval"
392
- class="flex items-center justify-between py-2 border-b border-gray-100 dark:border-gray-800"
393
- >
394
- <span class="text-gray-500 dark:text-gray-400">Interval</span>
395
- <span class="text-gray-900 dark:text-gray-100">{{ formatInterval(trigger.schedule.interval) }}</span>
396
- </div>
397
- <div
398
- v-if="trigger.schedule.timezone"
399
- class="flex items-center justify-between py-2"
400
- >
401
- <span class="text-gray-500 dark:text-gray-400">Timezone</span>
402
- <span class="text-gray-900 dark:text-gray-100">{{ trigger.schedule.timezone }}</span>
403
- </div>
404
- </div>
405
- </div>
406
-
407
- <!-- Subscribed Flows -->
408
- <div>
409
- <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 mb-4">
410
- Subscribed Flows ({{ trigger.subscriptionCount }})
411
- </h3>
412
- <div
413
- v-if="trigger.subscriptions.length === 0"
414
- class="text-center py-8"
415
- >
416
- <UIcon
417
- name="i-lucide-git-branch-plus"
418
- class="w-8 h-8 mx-auto mb-2 text-gray-300 dark:text-gray-700"
419
- />
420
- <p class="text-sm text-gray-500 dark:text-gray-400">
421
- No subscriptions
422
- </p>
423
- </div>
424
- <div
425
- v-else
426
- class="space-y-2"
427
- >
428
- <div
429
- v-for="sub in trigger.subscriptions"
430
- :key="`${sub.flowName}-${sub.triggerName}`"
431
- class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-900/50 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-900 transition-colors cursor-pointer"
432
- @click="goToFlow(sub.flowName)"
433
- >
434
- <div class="flex items-center gap-2 min-w-0">
435
- <UIcon
436
- name="i-lucide-git-branch"
437
- class="w-4 h-4 text-blue-500 shrink-0"
438
- />
439
- <span class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">{{ sub.flowName }}</span>
440
- </div>
441
- <div class="flex items-center gap-2">
442
- <UBadge
443
- :label="sub.mode"
444
- :color="sub.mode === 'auto' ? 'success' : 'neutral'"
445
- variant="subtle"
446
- size="xs"
447
- />
448
- <UIcon
449
- name="i-lucide-arrow-right"
450
- class="w-4 h-4 text-gray-400"
451
- />
452
- </div>
453
- </div>
454
- </div>
455
- </div>
456
- </div>
457
-
458
- <!-- Event Details Tab -->
459
- <div
460
- v-else-if="activeTab === 'details' && selectedEvent"
461
- class="p-6 space-y-6"
462
- >
463
- <!-- Event Info -->
464
- <div>
465
- <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center gap-2">
466
- <UIcon
467
- :name="getEventIcon(selectedEvent.type)"
468
- class="w-5 h-5"
469
- :class="getEventIconColor(selectedEvent.type)"
470
- />
471
- <span>Event Information</span>
472
- </h3>
473
- <div class="bg-gray-50 dark:bg-gray-900/50 rounded-lg p-4 space-y-3">
474
- <div class="flex items-center justify-between py-2 border-b border-gray-200 dark:border-gray-800">
475
- <span class="text-sm text-gray-600 dark:text-gray-400">Type</span>
476
- <UBadge
477
- :label="selectedEvent.type"
478
- :color="getEventBadgeColor(selectedEvent.type)"
479
- variant="subtle"
480
- size="xs"
481
- />
482
- </div>
483
- <div class="flex items-center justify-between py-2">
484
- <span class="text-sm text-gray-600 dark:text-gray-400">Timestamp</span>
485
- <span class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ formatDate(selectedEvent.ts || selectedEvent.timestamp) }}</span>
486
- </div>
487
- </div>
488
- </div>
489
-
490
- <!-- Event Data -->
491
- <div>
492
- <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 mb-4">
493
- Event Data
494
- </h3>
495
- <div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 overflow-x-auto">
496
- <pre class="text-xs font-mono">{{ JSON.stringify(selectedEvent.data, null, 2) }}</pre>
497
- </div>
498
- </div>
499
-
500
- <!-- Raw Event -->
501
- <div>
502
- <h3 class="text-sm font-semibold text-gray-900 dark:text-gray-100 mb-4">
503
- Raw Event
504
- </h3>
505
- <div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 overflow-x-auto">
506
- <pre class="text-xs font-mono">{{ JSON.stringify(selectedEvent, null, 2) }}</pre>
507
- </div>
508
- </div>
509
- </div>
510
- </div>
511
- </div>
512
- </div>
513
- </div>
514
- </div>
515
- </template>
516
-
517
- <script setup>
518
- import { ref, computed, onUnmounted, watch, onMounted } from "#imports";
519
- import { useTrigger, useTriggerEvents } from "../../composables/useTrigger";
520
- import { useComponentRouter } from "../../composables/useComponentRouter";
521
- import { useTriggerWebSocket } from "../../composables/useTriggerWebSocket";
522
- import { useRoute, useRouter } from "#app";
523
- import StatCard from "../../components/StatCard.vue";
524
- import SelectableListItem from "../../components/SelectableListItem.vue";
525
- const componentRouter = useComponentRouter();
526
- const router = useRouter();
527
- const route = useRoute();
528
- const triggerName = computed(() => {
529
- const name = componentRouter.route.value?.params?.name;
530
- return name || null;
531
- });
532
- const { trigger, status } = useTrigger(triggerName);
533
- const eventTypeFilter = computed({
534
- get: () => route.query.type || "all",
535
- set: (value) => {
536
- router.push({
537
- query: {
538
- ...route.query,
539
- type: value === "all" ? void 0 : value,
540
- page: void 0
541
- // Reset page when filter changes
542
- }
543
- });
544
- }
545
- });
546
- const currentPage = computed({
547
- get: () => {
548
- const page = route.query.page;
549
- return page ? Number.parseInt(page, 10) : 1;
550
- },
551
- set: (value) => {
552
- router.push({
553
- query: {
554
- ...route.query,
555
- page: value > 1 ? value.toString() : void 0
556
- }
557
- });
558
- }
559
- });
560
- const eventTypeFilterOptions = [
561
- { label: "All Events", value: "all" },
562
- { label: "Fired", value: "trigger.fired" },
563
- { label: "Registered", value: "trigger.registered" },
564
- { label: "Updated", value: "trigger.updated" }
565
- ];
566
- const eventsPerPage = 20;
567
- const eventQueryOptions = computed(() => {
568
- const options = {
569
- limit: eventsPerPage,
570
- offset: (currentPage.value - 1) * eventsPerPage
571
- };
572
- if (eventTypeFilter.value !== "all") {
573
- options.types = [eventTypeFilter.value];
574
- }
575
- return options;
576
- });
577
- const { events: fetchedEvents, refresh: refreshEvents, status: eventsStatus } = useTriggerEvents(triggerName, eventQueryOptions);
578
- const liveEvents = ref([]);
579
- const { connected: isConnected, reconnecting: isReconnecting, subscribe, unsubscribe, subscribeStats, unsubscribeStats } = useTriggerWebSocket();
580
- const pendingStatsUpdate = ref(null);
581
- watch(trigger, (newTrigger) => {
582
- if (newTrigger && pendingStatsUpdate.value) {
583
- const metadata = pendingStatsUpdate.value.metadata;
584
- if (metadata) {
585
- trigger.value = {
586
- ...newTrigger,
587
- stats: {
588
- totalFires: metadata.stats?.totalFires || metadata.totalFires || metadata["stats.totalFires"] || newTrigger.stats.totalFires || 0,
589
- totalFlowsStarted: metadata.stats?.totalFlowsStarted || metadata.totalFlowsStarted || metadata["stats.totalFlowsStarted"] || newTrigger.stats.totalFlowsStarted || 0,
590
- activeSubscribers: metadata.stats?.activeSubscribers || metadata.activeSubscribers || metadata["stats.activeSubscribers"] || newTrigger.stats.activeSubscribers || 0,
591
- lastFiredAt: metadata.stats?.lastFiredAt || metadata.lastFiredAt || metadata["stats.lastFiredAt"] || newTrigger.stats.lastFiredAt
592
- },
593
- lastActivityAt: metadata.lastActivityAt || newTrigger.lastActivityAt
594
- };
595
- pendingStatsUpdate.value = null;
596
- }
597
- }
598
- });
599
- function setupSubscriptions(newName) {
600
- subscribe({
601
- triggerName: newName,
602
- onEvent: (event) => {
603
- liveEvents.value = [event, ...liveEvents.value].slice(0, 50);
604
- },
605
- onHistory: (events2) => {
606
- liveEvents.value = events2.slice(0, 50);
607
- }
608
- });
609
- subscribeStats({
610
- onInitial: (data) => {
611
- if (data.id !== newName) {
612
- return;
613
- }
614
- const metadata = data?.metadata;
615
- if (!metadata) {
616
- return;
617
- }
618
- if (!trigger.value) {
619
- pendingStatsUpdate.value = data;
620
- return;
621
- }
622
- trigger.value = {
623
- ...trigger.value,
624
- stats: {
625
- totalFires: metadata.stats?.totalFires || metadata.totalFires || metadata["stats.totalFires"] || trigger.value.stats.totalFires || 0,
626
- totalFlowsStarted: metadata.stats?.totalFlowsStarted || metadata.totalFlowsStarted || metadata["stats.totalFlowsStarted"] || trigger.value.stats.totalFlowsStarted || 0,
627
- activeSubscribers: metadata.stats?.activeSubscribers || metadata.activeSubscribers || metadata["stats.activeSubscribers"] || trigger.value.stats.activeSubscribers || 0,
628
- lastFiredAt: metadata.stats?.lastFiredAt || metadata.lastFiredAt || metadata["stats.lastFiredAt"] || trigger.value.stats.lastFiredAt
629
- },
630
- lastActivityAt: metadata.lastActivityAt || trigger.value.lastActivityAt
631
- };
632
- },
633
- onUpdate: (data) => {
634
- if (data.id !== newName) {
635
- return;
636
- }
637
- if (!trigger.value) {
638
- pendingStatsUpdate.value = data;
639
- return;
640
- }
641
- const metadata = data?.metadata;
642
- if (!metadata) {
643
- return;
644
- }
645
- trigger.value = {
646
- ...trigger.value,
647
- stats: {
648
- totalFires: metadata.stats?.totalFires || metadata.totalFires || metadata["stats.totalFires"] || trigger.value.stats.totalFires || 0,
649
- totalFlowsStarted: metadata.stats?.totalFlowsStarted || metadata.totalFlowsStarted || metadata["stats.totalFlowsStarted"] || trigger.value.stats.totalFlowsStarted || 0,
650
- activeSubscribers: metadata.stats?.activeSubscribers || metadata.activeSubscribers || metadata["stats.activeSubscribers"] || trigger.value.stats.activeSubscribers || 0,
651
- lastFiredAt: metadata.stats?.lastFiredAt || metadata.lastFiredAt || metadata["stats.lastFiredAt"] || trigger.value.stats.lastFiredAt
652
- },
653
- lastActivityAt: metadata.lastActivityAt || trigger.value.lastActivityAt
654
- };
655
- }
656
- });
657
- }
658
- onMounted(() => {
659
- if (import.meta.server) return;
660
- if (triggerName.value) {
661
- setupSubscriptions(triggerName.value);
662
- }
663
- });
664
- onUnmounted(() => {
665
- unsubscribe();
666
- unsubscribeStats();
667
- });
668
- const events = computed(() => {
669
- return fetchedEvents.value;
670
- });
671
- const paginatedEvents = computed(() => {
672
- if (!events.value) return [];
673
- if (currentPage.value === 1) {
674
- const filteredLiveEvents = liveEvents.value.filter((event) => {
675
- if (eventTypeFilter.value === "all") return true;
676
- return event.type === eventTypeFilter.value;
677
- });
678
- const allEvents = [...filteredLiveEvents, ...events.value.events || []];
679
- const seen = /* @__PURE__ */ new Set();
680
- const unique = allEvents.filter((event) => {
681
- const key = event.id || `${event.type}-${event.ts || event.timestamp}`;
682
- if (seen.has(key)) return false;
683
- seen.add(key);
684
- return true;
685
- });
686
- unique.sort((a, b) => {
687
- const aTime = a.ts || a.timestamp || 0;
688
- const bTime = b.ts || b.timestamp || 0;
689
- return bTime - aTime;
690
- });
691
- return unique.slice(0, eventsPerPage);
692
- }
693
- return events.value.events || [];
694
- });
695
- watch(() => eventQueryOptions.value, () => {
696
- refreshEvents();
697
- }, { deep: true });
698
- const activeTab = ref("overview");
699
- const tabItems = computed(() => [
700
- { label: "Overview", value: "overview", icon: "i-lucide-bar-chart-3" },
701
- {
702
- label: "Event Details",
703
- value: "details",
704
- icon: "i-lucide-file-text",
705
- disabled: !selectedEvent.value
706
- }
707
- ]);
708
- const selectedEvent = ref(null);
709
- const selectEvent = (event) => {
710
- selectedEvent.value = event;
711
- activeTab.value = "details";
712
- };
713
- watch(selectedEvent, (newEvent) => {
714
- if (newEvent) {
715
- activeTab.value = "details";
716
- } else {
717
- activeTab.value = "overview";
718
- }
719
- });
720
- const goBack = () => {
721
- componentRouter.push("/triggers");
722
- };
723
- const goToEdit = () => {
724
- if (triggerName.value) {
725
- componentRouter.push(`/triggers/${encodeURIComponent(triggerName.value)}/edit`);
726
- }
727
- };
728
- const goToFlow = (flowName) => {
729
- componentRouter.push(`/flows?flow=${encodeURIComponent(flowName)}`);
730
- };
731
- const getTriggerIcon = (type) => {
732
- switch (type) {
733
- case "event":
734
- return "i-lucide-radio";
735
- case "webhook":
736
- return "i-lucide-webhook";
737
- case "schedule":
738
- return "i-lucide-clock";
739
- case "manual":
740
- return "i-lucide-hand";
741
- default:
742
- return "i-lucide-zap";
743
- }
744
- };
745
- const getTriggerIconColor = (type) => {
746
- switch (type) {
747
- case "event":
748
- return "text-blue-500";
749
- case "webhook":
750
- return "text-purple-500";
751
- case "schedule":
752
- return "text-emerald-500";
753
- case "manual":
754
- return "text-amber-500";
755
- default:
756
- return "text-gray-500";
757
- }
758
- };
759
- const getTriggerTypeColor = (type) => {
760
- switch (type) {
761
- case "event":
762
- return "primary";
763
- case "webhook":
764
- return "success";
765
- case "schedule":
766
- return "warning";
767
- case "manual":
768
- return "neutral";
769
- default:
770
- return "neutral";
771
- }
772
- };
773
- const getEventIcon = (type) => {
774
- if (type.includes("fired")) return "i-lucide-zap";
775
- if (type.includes("registered")) return "i-lucide-plus-circle";
776
- if (type.includes("updated")) return "i-lucide-pencil";
777
- if (type.includes("subscription")) return "i-lucide-link";
778
- return "i-lucide-circle-dot";
779
- };
780
- const getEventIconColor = (type) => {
781
- if (type.includes("fired")) return "text-emerald-500";
782
- if (type.includes("registered")) return "text-blue-500";
783
- if (type.includes("updated")) return "text-amber-500";
784
- if (type.includes("subscription")) return "text-purple-500";
785
- return "text-gray-500";
786
- };
787
- const getEventBadgeColor = (type) => {
788
- if (type.includes("fired")) return "success";
789
- if (type.includes("registered")) return "primary";
790
- if (type.includes("updated")) return "warning";
791
- if (type.includes("subscription")) return "secondary";
792
- return "neutral";
793
- };
794
- const formatTime = (timestamp) => {
795
- const date = new Date(timestamp);
796
- const now = /* @__PURE__ */ new Date();
797
- const diff = now.getTime() - date.getTime();
798
- const seconds = Math.floor(diff / 1e3);
799
- const minutes = Math.floor(seconds / 60);
800
- const hours = Math.floor(minutes / 60);
801
- const days = Math.floor(hours / 24);
802
- if (days > 0) return `${days}d ago`;
803
- if (hours > 0) return `${hours}h ago`;
804
- if (minutes > 0) return `${minutes}m ago`;
805
- if (seconds > 10) return `${seconds}s ago`;
806
- return "just now";
807
- };
808
- const formatDate = (timestamp) => {
809
- return new Date(timestamp).toLocaleString();
810
- };
811
- const getFilterIcon = (value) => {
812
- switch (value) {
813
- case "trigger.fired":
814
- return "i-lucide-zap";
815
- case "trigger.registered":
816
- return "i-lucide-plus-circle";
817
- case "trigger.updated":
818
- return "i-lucide-pencil";
819
- default:
820
- return "i-lucide-filter";
821
- }
822
- };
823
- const getFilterIconColor = (value) => {
824
- switch (value) {
825
- case "trigger.fired":
826
- return "text-emerald-500";
827
- case "trigger.registered":
828
- return "text-blue-500";
829
- case "trigger.updated":
830
- return "text-amber-500";
831
- default:
832
- return "text-gray-500";
833
- }
834
- };
835
- const getFilterLabel = (value) => {
836
- const option = eventTypeFilterOptions.find((o) => o.value === value);
837
- return option?.label || "All Events";
838
- };
839
- const copyToClipboard = async (text) => {
840
- try {
841
- await navigator.clipboard.writeText(text);
842
- } catch (err) {
843
- console.error("Failed to copy:", err);
844
- }
845
- };
846
- const formatInterval = (seconds) => {
847
- const units = [
848
- { value: 86400, label: "day", pluralLabel: "days" },
849
- { value: 3600, label: "hour", pluralLabel: "hours" },
850
- { value: 60, label: "minute", pluralLabel: "minutes" },
851
- { value: 1, label: "second", pluralLabel: "seconds" }
852
- ];
853
- for (const unit of units) {
854
- if (seconds >= unit.value && seconds % unit.value === 0) {
855
- const count = seconds / unit.value;
856
- const label = count === 1 ? unit.label : unit.pluralLabel;
857
- return `Every ${count} ${label}`;
858
- }
859
- }
860
- if (seconds >= 60) {
861
- const minutes = Math.floor(seconds / 60);
862
- const remainingSeconds = seconds % 60;
863
- if (remainingSeconds === 0) {
864
- return `Every ${minutes} ${minutes === 1 ? "minute" : "minutes"}`;
865
- }
866
- return `Every ${minutes}m ${remainingSeconds}s`;
867
- }
868
- return `Every ${seconds} ${seconds === 1 ? "second" : "seconds"}`;
869
- };
870
- </script>