@nvent-addon/app 0.5.4 → 0.5.6

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 (30) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +9 -1
  3. package/dist/runtime/app/components/ComponentShell.d.vue.ts +1 -0
  4. package/dist/runtime/app/components/ComponentShell.vue +13 -3
  5. package/dist/runtime/app/components/ComponentShell.vue.d.ts +1 -0
  6. package/dist/runtime/app/components/DashboardCard.d.vue.ts +15 -0
  7. package/dist/runtime/app/components/DashboardCard.vue +76 -0
  8. package/dist/runtime/app/components/DashboardCard.vue.d.ts +15 -0
  9. package/dist/runtime/app/components/TimelineList.vue +222 -31
  10. package/dist/runtime/app/components/flow/AwaitNode.d.vue.ts +19 -2
  11. package/dist/runtime/app/components/flow/AwaitNode.vue +317 -29
  12. package/dist/runtime/app/components/flow/AwaitNode.vue.d.ts +19 -2
  13. package/dist/runtime/app/components/flow/Diagram.d.vue.ts +2 -1
  14. package/dist/runtime/app/components/flow/Diagram.vue +138 -74
  15. package/dist/runtime/app/components/flow/Diagram.vue.d.ts +2 -1
  16. package/dist/runtime/app/components/flow/NodeCard.d.vue.ts +14 -11
  17. package/dist/runtime/app/components/flow/NodeCard.vue +119 -35
  18. package/dist/runtime/app/components/flow/NodeCard.vue.d.ts +14 -11
  19. package/dist/runtime/app/components/flow/RunOverview.d.vue.ts +1 -0
  20. package/dist/runtime/app/components/flow/RunOverview.vue +85 -86
  21. package/dist/runtime/app/components/flow/RunOverview.vue.d.ts +1 -0
  22. package/dist/runtime/app/components/flow/RunTimeline.vue +51 -22
  23. package/dist/runtime/app/components/flow/StepSelector.vue +124 -46
  24. package/dist/runtime/app/composables/useFlowRunTimeline.d.ts +6 -0
  25. package/dist/runtime/app/composables/useFlowState.d.ts +8 -1
  26. package/dist/runtime/app/composables/useFlowState.js +34 -59
  27. package/dist/runtime/app/pages/dashboard.vue +51 -103
  28. package/dist/runtime/app/pages/flows/[name].vue +26 -3
  29. package/dist/runtime/app/pages/triggers/[name]/edit.vue +1 -1
  30. package/package.json +1 -1
@@ -1,46 +1,258 @@
1
1
  <template>
2
2
  <div
3
- class="px-3 py-2 rounded-lg border-2 min-w-[180px] bg-white dark:bg-gray-900 transition-all duration-300"
3
+ class="rounded-lg border-2 w-[280px] min-h-[140px] bg-white dark:bg-gray-900 transition-all duration-300 shadow-sm flex flex-col overflow-hidden"
4
4
  :class="borderClass"
5
5
  >
6
- <div class="flex items-center justify-between gap-2 text-xs mb-2">
7
- <div class="flex items-center gap-2">
8
- <UIcon
9
- :name="getAwaitIcon(data.awaitType)"
10
- class="w-3.5 h-3.5"
6
+ <!-- Header -->
7
+ <div class="px-2.5 py-1.5 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex-shrink-0">
8
+ <div class="flex items-center justify-between gap-2">
9
+ <div class="flex items-center gap-1.5 min-w-0">
10
+ <UIcon
11
+ :name="getAwaitIcon(data.awaitType)"
12
+ :class="iconColorClass"
13
+ class="w-4 h-4 flex-shrink-0"
14
+ />
15
+ <span class="font-semibold text-xs text-gray-700 dark:text-gray-300 uppercase tracking-wide truncate">
16
+ {{ data.awaitType }}
17
+ </span>
18
+ </div>
19
+ <UBadge
20
+ :label="statusLabel"
21
+ size="xs"
22
+ :color="statusColor"
11
23
  />
12
- <span class="font-medium text-gray-700 dark:text-gray-300">
13
- {{ data.label }}
14
- </span>
15
24
  </div>
16
- <UBadge
17
- :label="statusLabel"
18
- size="xs"
19
- :color="statusColor"
20
- />
21
25
  </div>
26
+
27
+ <!-- Body - Configuration Details -->
22
28
  <div
23
- v-if="data.awaitConfig"
24
- class="text-[10px] text-gray-500 dark:text-gray-400"
29
+ v-if="data.awaitConfig || data.awaitData"
30
+ class="px-3 py-2.5 flex-1 overflow-y-auto text-xs min-h-0"
25
31
  >
26
- <div v-if="data.awaitType === 'time'">
27
- Delay: {{ formatDelay(data.awaitConfig.delay) }}
32
+ <!-- Time-specific -->
33
+ <div
34
+ v-if="data.awaitType === 'time' && data.awaitConfig?.delay"
35
+ class="space-y-1"
36
+ >
37
+ <div class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 items-center text-[9px]">
38
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
39
+ <UIcon
40
+ name="i-lucide-timer"
41
+ class="w-3 h-3"
42
+ />
43
+ Delay:
44
+ </span>
45
+ <span class="font-mono text-gray-900 dark:text-gray-100 text-right">{{ formatDelay(data.awaitConfig.delay) }}</span>
46
+
47
+ <template v-if="nextTriggerTime">
48
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
49
+ <UIcon
50
+ name="i-lucide-calendar-clock"
51
+ class="w-3 h-3"
52
+ />
53
+ Triggers:
54
+ </span>
55
+ <span class="text-gray-900 dark:text-gray-100 text-right">{{ nextTriggerTime }}</span>
56
+ </template>
57
+ </div>
28
58
  </div>
29
- <div v-else-if="data.awaitType === 'event'">
30
- Event: {{ data.awaitConfig.event }}
59
+
60
+ <!-- Event-specific -->
61
+ <div
62
+ v-if="data.awaitType === 'event'"
63
+ class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 items-start text-[9px]"
64
+ >
65
+ <template v-if="data.awaitConfig?.event || data.awaitData?.eventName">
66
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
67
+ <UIcon
68
+ name="i-lucide-zap"
69
+ class="w-3 h-3"
70
+ />
71
+ Event:
72
+ </span>
73
+ <span class="font-mono text-gray-900 dark:text-gray-100 break-all text-right">
74
+ {{ data.awaitData?.eventName || data.awaitConfig?.event }}
75
+ </span>
76
+ </template>
77
+
78
+ <template v-if="data.awaitConfig?.filterKey || data.awaitData?.filterKey">
79
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
80
+ <UIcon
81
+ name="i-lucide-filter"
82
+ class="w-3 h-3"
83
+ />
84
+ Filter:
85
+ </span>
86
+ <span class="font-mono text-gray-900 dark:text-gray-100 text-right">
87
+ {{ data.awaitData?.filterKey || data.awaitConfig?.filterKey }}
88
+ </span>
89
+ </template>
31
90
  </div>
32
- <div v-else-if="data.awaitType === 'webhook'">
33
- Webhook: {{ data.awaitConfig.method || "POST" }}
91
+
92
+ <!-- Webhook-specific -->
93
+ <template v-if="data.awaitType === 'webhook'">
94
+ <div class="space-y-1">
95
+ <div class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 items-center text-[9px]">
96
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
97
+ <UIcon
98
+ name="i-lucide-git-branch"
99
+ class="w-3 h-3"
100
+ />
101
+ Method:
102
+ </span>
103
+ <div class="flex justify-end">
104
+ <UBadge
105
+ v-if="data.awaitConfig?.method || data.awaitData?.method"
106
+ :label="data.awaitConfig?.method || data.awaitData?.method"
107
+ size="xs"
108
+ color="primary"
109
+ />
110
+ </div>
111
+
112
+ <template v-if="data.awaitData?.webhookUrl">
113
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
114
+ <UIcon
115
+ name="i-lucide-link"
116
+ class="w-3 h-3"
117
+ />
118
+ URL:
119
+ </span>
120
+ <div class="flex items-center gap-1 justify-end min-w-0">
121
+ <span class="font-mono text-gray-900 dark:text-gray-100 truncate text-right flex-1">
122
+ {{ data.awaitData.webhookUrl }}
123
+ </span>
124
+ <button
125
+ type="button"
126
+ class="flex-shrink-0 p-0.5 hover:bg-gray-100 dark:hover:bg-gray-800 rounded transition-colors"
127
+ :title="copiedUrl === data.awaitData.webhookUrl ? 'Copied!' : 'Copy URL'"
128
+ @click.stop="copyToClipboard(data.awaitData.webhookUrl)"
129
+ >
130
+ <UIcon
131
+ :name="copiedUrl === data.awaitData.webhookUrl ? 'i-lucide-check' : 'i-lucide-copy'"
132
+ class="w-3 h-3"
133
+ :class="copiedUrl === data.awaitData.webhookUrl ? 'text-emerald-600 dark:text-emerald-400' : 'text-gray-600 dark:text-gray-400'"
134
+ />
135
+ </button>
136
+ </div>
137
+ </template>
138
+
139
+ <template v-else-if="data.awaitConfig?.path">
140
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
141
+ <UIcon
142
+ name="i-lucide-route"
143
+ class="w-3 h-3"
144
+ />
145
+ Path:
146
+ </span>
147
+ <div class="font-mono text-gray-900 dark:text-gray-100 truncate text-right">
148
+ {{ data.awaitConfig.path }}
149
+ </div>
150
+ </template>
151
+ </div>
152
+ </div>
153
+ </template>
154
+
155
+ <!-- Schedule-specific -->
156
+ <div
157
+ v-if="data.awaitType === 'schedule'"
158
+ class="space-y-1"
159
+ >
160
+ <div class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 items-center text-[9px]">
161
+ <template v-if="data.awaitConfig?.cron">
162
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
163
+ <UIcon
164
+ name="i-lucide-calendar-cog"
165
+ class="w-3 h-3"
166
+ />
167
+ Cron:
168
+ </span>
169
+ <span class="font-mono text-gray-900 dark:text-gray-100 text-right">{{ data.awaitConfig.cron }}</span>
170
+ </template>
171
+
172
+ <template v-if="cronDescription">
173
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
174
+ <UIcon
175
+ name="i-lucide-repeat"
176
+ class="w-3 h-3"
177
+ />
178
+ Pattern:
179
+ </span>
180
+ <span class="text-gray-500 dark:text-gray-400 text-right">{{ cronDescription }}</span>
181
+ </template>
182
+
183
+ <template v-if="nextCronTime">
184
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
185
+ <UIcon
186
+ name="i-lucide-calendar-check"
187
+ class="w-3 h-3"
188
+ />
189
+ Next:
190
+ </span>
191
+ <span class="text-gray-900 dark:text-gray-100 font-medium text-right">{{ nextCronTime }}</span>
192
+ </template>
193
+
194
+ <template v-if="data.awaitConfig?.timezone">
195
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
196
+ <UIcon
197
+ name="i-lucide-globe"
198
+ class="w-3 h-3"
199
+ />
200
+ Timezone:
201
+ </span>
202
+ <span class="font-mono text-gray-900 dark:text-gray-100 text-right">{{ data.awaitConfig.timezone }}</span>
203
+ </template>
204
+ </div>
205
+ </div>
206
+
207
+ <!-- Common: Timeout -->
208
+ <div
209
+ v-if="data.awaitConfig?.timeout || data.awaitData?.timeout"
210
+ class="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 items-center text-[9px] mt-2 pt-2 border-t border-gray-200 dark:border-gray-700"
211
+ >
212
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
213
+ <UIcon
214
+ name="i-lucide-hourglass"
215
+ class="w-3 h-3"
216
+ />
217
+ Timeout:
218
+ </span>
219
+ <span class="text-gray-900 dark:text-gray-100 text-right">
220
+ {{ formatDelay(data.awaitData?.timeout || data.awaitConfig?.timeout) }}
221
+ </span>
222
+ <template v-if="data.awaitConfig?.timeoutAction || data.awaitData?.timeoutAction">
223
+ <span class="text-gray-600 dark:text-gray-400 font-medium flex items-center gap-1">
224
+ <UIcon
225
+ name="i-lucide-shield-alert"
226
+ class="w-3 h-3"
227
+ />
228
+ Action:
229
+ </span>
230
+ <span class="text-gray-900 dark:text-gray-100 text-right">
231
+ {{ data.awaitData?.timeoutAction || data.awaitConfig?.timeoutAction }}
232
+ </span>
233
+ </template>
34
234
  </div>
35
235
  </div>
36
236
  </div>
37
237
  </template>
38
238
 
39
239
  <script setup>
40
- import { computed } from "#imports";
240
+ import { computed, ref } from "#imports";
41
241
  const props = defineProps({
42
242
  data: { type: Object, required: true }
43
243
  });
244
+ const copiedUrl = ref(null);
245
+ const copyToClipboard = async (text) => {
246
+ try {
247
+ await navigator.clipboard.writeText(text);
248
+ copiedUrl.value = text;
249
+ setTimeout(() => {
250
+ copiedUrl.value = null;
251
+ }, 2e3);
252
+ } catch (err) {
253
+ console.error("Failed to copy:", err);
254
+ }
255
+ };
44
256
  const statusLabel = computed(() => {
45
257
  return (props.data.status || "idle").toUpperCase();
46
258
  });
@@ -59,13 +271,27 @@ const statusColor = computed(() => {
59
271
  const borderClass = computed(() => {
60
272
  switch (props.data.status) {
61
273
  case "waiting":
62
- return "border-amber-400 dark:border-amber-500 bg-amber-50 dark:bg-amber-950/20";
274
+ return "border-amber-400 dark:border-amber-500";
63
275
  case "resolved":
64
- return "border-emerald-400 dark:border-emerald-500 bg-emerald-50 dark:bg-emerald-950/20";
276
+ return "border-emerald-400 dark:border-emerald-500";
65
277
  case "timeout":
66
- return "border-red-400 dark:border-red-500 bg-red-50 dark:bg-red-950/20";
278
+ return "border-red-400 dark:border-red-500";
279
+ default:
280
+ return "border-blue-300 dark:border-blue-700";
281
+ }
282
+ });
283
+ const iconColorClass = computed(() => {
284
+ switch (props.data.awaitType) {
285
+ case "time":
286
+ return "text-blue-600 dark:text-blue-400";
287
+ case "event":
288
+ return "text-purple-600 dark:text-purple-400";
289
+ case "webhook":
290
+ return "text-orange-600 dark:text-orange-400";
291
+ case "schedule":
292
+ return "text-green-600 dark:text-green-400";
67
293
  default:
68
- return "border-gray-300 dark:border-gray-700";
294
+ return "text-gray-600 dark:text-gray-400";
69
295
  }
70
296
  });
71
297
  function getAwaitIcon(type) {
@@ -76,6 +302,8 @@ function getAwaitIcon(type) {
76
302
  return "i-lucide-zap";
77
303
  case "webhook":
78
304
  return "i-lucide-webhook";
305
+ case "schedule":
306
+ return "i-lucide-calendar-clock";
79
307
  default:
80
308
  return "i-lucide-clock";
81
309
  }
@@ -84,7 +312,67 @@ function formatDelay(ms) {
84
312
  if (!ms) return "0ms";
85
313
  if (ms < 1e3) return `${ms}ms`;
86
314
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
87
- if (ms < 36e5) return `${(ms / 6e4).toFixed(1)}m`;
88
- return `${(ms / 36e5).toFixed(1)}h`;
315
+ if (ms < 6e4 * 60) return `${(ms / 6e4).toFixed(1)}m`;
316
+ if (ms < 6e4 * 60 * 24) return `${(ms / 36e5).toFixed(1)}h`;
317
+ return `${(ms / (36e5 * 24)).toFixed(1)}d`;
89
318
  }
319
+ const nextTriggerTime = computed(() => {
320
+ if (props.data.awaitType !== "time" || !props.data.awaitConfig?.delay) return null;
321
+ if (props.data.scheduledTriggerAt) {
322
+ const triggerTime = new Date(props.data.scheduledTriggerAt);
323
+ if (!Number.isNaN(triggerTime.getTime())) {
324
+ return triggerTime.toLocaleString(void 0, {
325
+ month: "short",
326
+ day: "numeric",
327
+ hour: "2-digit",
328
+ minute: "2-digit"
329
+ });
330
+ }
331
+ }
332
+ return "No schedule";
333
+ });
334
+ const cronDescription = computed(() => {
335
+ if (props.data.awaitType !== "schedule" || !props.data.awaitConfig?.cron) return null;
336
+ const cron = props.data.awaitConfig.cron;
337
+ const parts = cron.split(" ");
338
+ if (cron === "* * * * *") return "Next minute";
339
+ if (cron === "0 * * * *") return "Top of next hour";
340
+ if (cron === "0 0 * * *") return "Next midnight";
341
+ if (cron === "0 9 * * *") return "Next 9:00 AM";
342
+ if (cron === "0 0 * * 0") return "Next Sunday midnight";
343
+ if (cron === "0 0 1 * *") return "Next 1st of month";
344
+ try {
345
+ if (parts.length >= 5) {
346
+ const minute = parts[0] || "*";
347
+ const hour = parts[1] || "*";
348
+ const day = parts[2] || "*";
349
+ const month = parts[3] || "*";
350
+ const weekday = parts[4] || "*";
351
+ if (minute.startsWith("*/") && hour === "*") {
352
+ return `Next ${minute.slice(2)}min mark`;
353
+ }
354
+ if (minute !== "*" && hour !== "*" && day === "*" && month === "*" && weekday === "*") {
355
+ return `Next ${hour.padStart(2, "0")}:${minute.padStart(2, "0")}`;
356
+ }
357
+ }
358
+ } catch {
359
+ }
360
+ return `Next: ${cron}`;
361
+ });
362
+ const nextCronTime = computed(() => {
363
+ if (props.data.awaitType !== "schedule" || !props.data.awaitConfig?.cron) return null;
364
+ if (props.data.scheduledTriggerAt) {
365
+ const triggerTime = new Date(props.data.scheduledTriggerAt);
366
+ if (!Number.isNaN(triggerTime.getTime())) {
367
+ return triggerTime.toLocaleString(void 0, {
368
+ month: "short",
369
+ day: "numeric",
370
+ hour: "2-digit",
371
+ minute: "2-digit",
372
+ timeZoneName: "short"
373
+ });
374
+ }
375
+ }
376
+ return "No schedule";
377
+ });
90
378
  </script>
@@ -1,16 +1,33 @@
1
1
  interface AwaitConfig {
2
- type?: 'time' | 'event' | 'webhook';
2
+ type?: 'time' | 'event' | 'webhook' | 'schedule';
3
3
  delay?: number;
4
4
  event?: string;
5
+ filterKey?: string;
5
6
  method?: string;
7
+ path?: string;
8
+ cron?: string;
9
+ timezone?: string;
6
10
  timeout?: number;
11
+ timeoutAction?: 'fail' | 'continue' | 'retry';
12
+ }
13
+ interface AwaitData {
14
+ method?: string;
15
+ webhookUrl?: string;
16
+ timeout?: number;
17
+ timeoutAction?: string;
18
+ eventName?: string;
19
+ filterKey?: string;
20
+ [key: string]: any;
7
21
  }
8
22
  type __VLS_Props = {
9
23
  data: {
10
24
  label: string;
11
- awaitType?: 'time' | 'event' | 'webhook';
25
+ awaitType?: 'time' | 'event' | 'webhook' | 'schedule';
12
26
  awaitConfig?: AwaitConfig;
27
+ awaitData?: AwaitData;
13
28
  status?: 'idle' | 'waiting' | 'resolved' | 'timeout';
29
+ startedAt?: string | Date;
30
+ scheduledTriggerAt?: string | Date;
14
31
  };
15
32
  };
16
33
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -45,6 +45,7 @@ interface StepStatus {
45
45
  status: 'pending' | 'running' | 'completed' | 'failed' | 'retrying' | 'waiting' | 'timeout' | 'canceled';
46
46
  attempt?: number;
47
47
  error?: string;
48
+ scheduledTriggerAt?: string;
48
49
  }
49
50
  type __VLS_Props = {
50
51
  flow?: FlowMeta | null;
@@ -53,7 +54,7 @@ type __VLS_Props = {
53
54
  showMiniMap?: boolean;
54
55
  showBackground?: boolean;
55
56
  stepStates?: Record<string, StepStatus>;
56
- flowStatus?: 'running' | 'completed' | 'failed' | 'canceled' | 'stalled';
57
+ flowStatus?: 'running' | 'completed' | 'failed' | 'canceled' | 'stalled' | 'awaiting';
57
58
  };
58
59
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
59
60
  nodeSelected: (payload: {