@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
@@ -102,39 +102,90 @@ const nodes = computed(() => {
102
102
  const f = props.flow;
103
103
  if (!f) return out;
104
104
  const states = props.stepStates || {};
105
- const colWidth = 250;
106
- const rowHeight = 140;
107
- const horizontalGap = 50;
108
- const verticalGap = 80;
109
- const entryToStepsGap = 140;
110
- const nodeWidth = 220;
105
+ const colWidth = 320;
106
+ const rowHeight = 180;
107
+ const horizontalGap = 60;
108
+ const verticalGap = 90;
109
+ const awaitRowHeight = 140;
110
+ const nodeWidth = 300;
111
111
  let y = 0;
112
112
  if (f.entry) {
113
113
  const entryState = states[f.entry.step];
114
114
  const status = mapStatusToNodeStatus(entryState?.status);
115
+ const entryStepTimeout = f.analyzed?.steps?.[f.entry.step]?.stepTimeout;
115
116
  out.push({
116
117
  id: `entry:${f.entry.step}`,
117
118
  position: { x: -nodeWidth / 2, y },
118
119
  data: {
119
120
  label: f.entry.step,
120
121
  queue: f.entry.queue,
122
+ workerId: f.entry.workerId,
121
123
  status,
122
124
  attempt: entryState?.attempt,
123
125
  error: entryState?.error,
124
126
  runtime: f.entry.runtime,
125
127
  runtype: f.entry.runtype,
126
- emits: f.entry.emits
128
+ emits: f.entry.emits,
129
+ awaitBefore: f.entry.awaitBefore,
130
+ awaitAfter: f.entry.awaitAfter,
131
+ stepTimeout: entryStepTimeout
127
132
  },
128
133
  type: "flow-entry",
129
134
  style: { minWidth: `${nodeWidth}px` }
130
135
  });
131
- y += rowHeight + entryToStepsGap;
136
+ y += rowHeight + verticalGap;
137
+ if (f.entry.awaitAfter) {
138
+ const awaitKey = `${f.entry.step}:await-after`;
139
+ const awaitState = states[awaitKey];
140
+ const awaitStatus = awaitState?.status === "waiting" ? "waiting" : awaitState?.status === "completed" ? "resolved" : awaitState?.status === "timeout" ? "timeout" : "idle";
141
+ out.push({
142
+ id: `await:entry-after:${f.entry.step}`,
143
+ position: { x: -120, y },
144
+ data: {
145
+ label: `Await (${f.entry.awaitAfter.type})`,
146
+ awaitType: f.entry.awaitAfter.type,
147
+ awaitConfig: f.entry.awaitAfter,
148
+ awaitData: awaitState?.awaitData,
149
+ status: awaitStatus,
150
+ scheduledTriggerAt: awaitState?.scheduledTriggerAt
151
+ },
152
+ type: "flow-await",
153
+ style: { minWidth: "180px" }
154
+ });
155
+ y += awaitRowHeight + verticalGap;
156
+ }
132
157
  }
133
158
  const steps = f.steps || {};
134
159
  if (f.analyzed?.levels && f.analyzed.levels.length > 0) {
135
- const levels = f.analyzed.levels.filter((level) => level.length > 0);
160
+ const levels = f.analyzed.levels.slice(1).filter((level) => level.length > 0);
136
161
  levels.forEach((levelSteps) => {
137
162
  if (levelSteps.length === 0) return;
163
+ const awaitNodesCreated = [];
164
+ levelSteps.forEach((stepName) => {
165
+ const step = steps[stepName];
166
+ if (!step?.awaitBefore) return;
167
+ const awaitState = states[`${stepName}:await-before`];
168
+ const awaitStatus = awaitState?.status === "waiting" ? "waiting" : awaitState?.status === "completed" ? "resolved" : awaitState?.status === "timeout" ? "timeout" : "idle";
169
+ out.push({
170
+ id: `await:step-before:${stepName}`,
171
+ position: { x: 0, y },
172
+ // Temporary position
173
+ data: {
174
+ label: `Await (${step.awaitBefore.type})`,
175
+ awaitType: step.awaitBefore.type,
176
+ awaitConfig: step.awaitBefore,
177
+ awaitData: awaitState?.awaitData,
178
+ status: awaitStatus,
179
+ scheduledTriggerAt: awaitState?.scheduledTriggerAt
180
+ },
181
+ type: "flow-await",
182
+ style: { minWidth: "180px" }
183
+ });
184
+ awaitNodesCreated.push(stepName);
185
+ });
186
+ if (awaitNodesCreated.length > 0) {
187
+ y += awaitRowHeight + verticalGap;
188
+ }
138
189
  const cols = Math.min(4, levelSteps.length);
139
190
  const rows = Math.ceil(levelSteps.length / cols);
140
191
  levelSteps.forEach((stepName, idx) => {
@@ -150,6 +201,8 @@ const nodes = computed(() => {
150
201
  const rowStartX = -rowWidth / 2;
151
202
  const x = rowStartX + col * (colWidth + horizontalGap);
152
203
  const yPos = y + row * (rowHeight + verticalGap);
204
+ const analyzedStep = f.analyzed?.steps?.[stepName];
205
+ const stepStepTimeout = analyzedStep?.stepTimeout;
153
206
  out.push({
154
207
  id: `step:${stepName}`,
155
208
  position: { x, y: yPos },
@@ -162,13 +215,46 @@ const nodes = computed(() => {
162
215
  error: stepState?.error,
163
216
  runtime: step?.runtime,
164
217
  runtype: step?.runtype,
165
- emits: step?.emits
218
+ subscribes: step?.subscribes,
219
+ emits: step?.emits,
220
+ awaitBefore: step?.awaitBefore,
221
+ awaitAfter: step?.awaitAfter,
222
+ stepTimeout: stepStepTimeout
166
223
  },
167
224
  type: "flow-step",
168
225
  style: { minWidth: `${nodeWidth}px` }
169
226
  });
227
+ if (step.awaitBefore) {
228
+ const awaitNode = out.find((n) => n.id === `await:step-before:${stepName}`);
229
+ if (awaitNode) {
230
+ awaitNode.position.x = x - 20;
231
+ }
232
+ }
233
+ if (step.awaitAfter && row === rows - 1 && idx === levelSteps.length - 1) {
234
+ const awaitKey = `${stepName}:await-after`;
235
+ const awaitState = states[awaitKey];
236
+ const awaitStatus = awaitState?.status === "waiting" ? "waiting" : awaitState?.status === "completed" ? "resolved" : awaitState?.status === "timeout" ? "timeout" : "idle";
237
+ out.push({
238
+ id: `await:step-after:${stepName}`,
239
+ position: { x: x - 20, y: yPos + rowHeight + verticalGap },
240
+ data: {
241
+ label: `Await (${step.awaitAfter.type})`,
242
+ awaitType: step.awaitAfter.type,
243
+ awaitConfig: step.awaitAfter,
244
+ awaitData: awaitState?.awaitData,
245
+ status: awaitStatus,
246
+ scheduledTriggerAt: awaitState?.scheduledTriggerAt
247
+ },
248
+ type: "flow-await",
249
+ style: { minWidth: "180px" }
250
+ });
251
+ }
170
252
  });
171
253
  y += rows * (rowHeight + verticalGap);
254
+ const lastStepName = levelSteps[levelSteps.length - 1];
255
+ if (lastStepName && steps[lastStepName]?.awaitAfter) {
256
+ y += awaitRowHeight + verticalGap;
257
+ }
172
258
  });
173
259
  } else {
174
260
  const names = Object.keys(steps);
@@ -186,6 +272,8 @@ const nodes = computed(() => {
186
272
  const rowStartX = -rowWidth / 2;
187
273
  const x = rowStartX + col * (colWidth + horizontalGap);
188
274
  const yPos = y + row * (rowHeight + verticalGap);
275
+ const analyzedStep = f.analyzed?.steps?.[name];
276
+ const stepStepTimeout = analyzedStep?.stepTimeout;
189
277
  out.push({
190
278
  id: `step:${name}`,
191
279
  position: { x, y: yPos },
@@ -198,57 +286,15 @@ const nodes = computed(() => {
198
286
  error: stepState?.error,
199
287
  runtime: step?.runtime,
200
288
  runtype: step?.runtype,
201
- emits: step?.emits
289
+ emits: step?.emits,
290
+ stepTimeout: stepStepTimeout
202
291
  },
203
292
  type: "flow-step",
204
293
  style: { minWidth: `${nodeWidth}px` }
205
294
  });
206
295
  });
207
296
  }
208
- const awaitNodes = [];
209
- const awaitGap = 70;
210
- if (f.entry?.awaitAfter && f.entry) {
211
- const entryNode = out.find((n) => n.id === `entry:${f.entry.step}`);
212
- if (entryNode) {
213
- const awaitKey = `${f.entry.step}:await-after`;
214
- const awaitState = states[awaitKey];
215
- const awaitStatus = awaitState?.status === "waiting" ? "waiting" : awaitState?.status === "completed" ? "resolved" : awaitState?.status === "timeout" ? "timeout" : "idle";
216
- awaitNodes.push({
217
- id: `await:entry-after:${f.entry.step}`,
218
- position: { x: -90, y: entryNode.position.y + rowHeight + awaitGap },
219
- data: {
220
- label: `Await (${f.entry.awaitAfter.type})`,
221
- awaitType: f.entry.awaitAfter.type,
222
- awaitConfig: f.entry.awaitAfter,
223
- status: awaitStatus
224
- },
225
- type: "flow-await",
226
- style: { minWidth: "180px" }
227
- });
228
- }
229
- }
230
- Object.entries(steps).forEach(([stepName, step]) => {
231
- if (step.awaitBefore) {
232
- const stepNode = out.find((n) => n.id === `step:${stepName}`);
233
- if (stepNode) {
234
- const awaitState = states[`${stepName}:await-before`];
235
- const awaitStatus = awaitState?.status === "waiting" ? "waiting" : awaitState?.status === "completed" ? "resolved" : awaitState?.status === "timeout" ? "timeout" : "idle";
236
- awaitNodes.push({
237
- id: `await:step-before:${stepName}`,
238
- position: { x: stepNode.position.x + 20, y: stepNode.position.y - awaitGap - 50 },
239
- data: {
240
- label: `Await (${step.awaitBefore.type})`,
241
- awaitType: step.awaitBefore.type,
242
- awaitConfig: step.awaitBefore,
243
- status: awaitStatus
244
- },
245
- type: "flow-await",
246
- style: { minWidth: "180px" }
247
- });
248
- }
249
- }
250
- });
251
- return [...out, ...awaitNodes];
297
+ return out;
252
298
  });
253
299
  function mapStatusToNodeStatus(status) {
254
300
  switch (status) {
@@ -277,22 +323,53 @@ const edges = computed(() => {
277
323
  function addEdge(source, target, label) {
278
324
  const id = `${source}->${target}${label ? `:${label}` : ""}`;
279
325
  if (added.has(id)) return;
280
- const sourceStep = source.split(":")[1];
281
- const targetStep = target.split(":")[1];
282
- const sourceState = sourceStep ? states[sourceStep] : void 0;
283
- const targetState = targetStep ? states[targetStep] : void 0;
284
- const shouldAnimate = props.flowStatus === "running" && sourceState?.status === "completed" && (targetState?.status === "running" || targetState?.status === "pending" || !targetState);
326
+ const getNodeState = (nodeId) => {
327
+ if (nodeId.startsWith("await:entry-after:")) {
328
+ const stepName = nodeId.replace("await:entry-after:", "");
329
+ return states[`${stepName}:await-after`];
330
+ }
331
+ if (nodeId.startsWith("await:step-before:")) {
332
+ const stepName = nodeId.replace("await:step-before:", "");
333
+ return states[`${stepName}:await-before`];
334
+ }
335
+ if (nodeId.startsWith("await:step-after:")) {
336
+ const stepName = nodeId.replace("await:step-after:", "");
337
+ return states[`${stepName}:await-after`];
338
+ }
339
+ const parts = nodeId.split(":");
340
+ return parts[1] ? states[parts[1]] : void 0;
341
+ };
342
+ const sourceState = getNodeState(source);
343
+ const targetState = getNodeState(target);
344
+ const shouldAnimate = (props.flowStatus === "running" || props.flowStatus === "awaiting") && (sourceState?.status === "completed" || sourceState?.status === "resolved") && (targetState?.status === "running" || targetState?.status === "pending" || targetState?.status === "waiting" || !targetState);
285
345
  added.add(id);
286
346
  out.push({ id, source, target, label, animated: shouldAnimate });
287
347
  }
288
348
  if (f.analyzed?.steps) {
289
349
  const analyzedSteps = f.analyzed.steps;
350
+ if (f.entry?.awaitAfter) {
351
+ const entryId = `entry:${f.entry.step}`;
352
+ const entryAwaitId = `await:entry-after:${f.entry.step}`;
353
+ addEdge(entryId, entryAwaitId);
354
+ }
290
355
  for (const [stepName, stepInfo] of Object.entries(analyzedSteps)) {
356
+ if (stepName === f.entry?.step) continue;
291
357
  const targetStep = steps[stepName];
292
358
  const target = `step:${stepName}`;
359
+ if (targetStep?.awaitAfter) {
360
+ const stepId = `step:${stepName}`;
361
+ const awaitAfterId = `await:step-after:${stepName}`;
362
+ addEdge(stepId, awaitAfterId);
363
+ }
293
364
  if (stepInfo.dependsOn.length > 0) {
294
365
  for (const depName of stepInfo.dependsOn) {
295
- const source = depName === f.entry?.step ? `entry:${depName}` : `step:${depName}`;
366
+ let source;
367
+ if (depName === f.entry?.step) {
368
+ source = f.entry.awaitAfter ? `await:entry-after:${depName}` : `entry:${depName}`;
369
+ } else {
370
+ const depStep = steps[depName];
371
+ source = depStep?.awaitAfter ? `await:step-after:${depName}` : `step:${depName}`;
372
+ }
296
373
  if (targetStep?.awaitBefore) {
297
374
  const awaitNodeId = `await:step-before:${stepName}`;
298
375
  addEdge(source, awaitNodeId);
@@ -301,19 +378,6 @@ const edges = computed(() => {
301
378
  addEdge(source, target);
302
379
  }
303
380
  }
304
- } else if (f.entry) {
305
- const entryId = `entry:${f.entry.step}`;
306
- if (f.entry.awaitAfter) {
307
- const awaitNodeId = `await:entry-after:${f.entry.step}`;
308
- addEdge(entryId, awaitNodeId);
309
- addEdge(awaitNodeId, target);
310
- } else if (targetStep?.awaitBefore) {
311
- const awaitNodeId = `await:step-before:${stepName}`;
312
- addEdge(entryId, awaitNodeId);
313
- addEdge(awaitNodeId, target);
314
- } else {
315
- addEdge(entryId, target);
316
- }
317
381
  }
318
382
  }
319
383
  } else {
@@ -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: {
@@ -1,4 +1,13 @@
1
1
  type Status = 'idle' | 'running' | 'error' | 'done' | 'canceled' | string | undefined;
2
+ interface AwaitConfig {
3
+ type: 'time' | 'event' | 'webhook' | 'schedule';
4
+ delay?: number;
5
+ event?: string;
6
+ method?: string;
7
+ timeout?: number;
8
+ timeoutAction?: 'fail' | 'continue';
9
+ cron?: string;
10
+ }
2
11
  type __VLS_Props = {
3
12
  id: string;
4
13
  data: {
@@ -10,20 +19,14 @@ type __VLS_Props = {
10
19
  error?: string;
11
20
  runtime?: 'nodejs' | 'python';
12
21
  runtype?: 'inprocess' | 'task';
22
+ subscribes?: string[];
13
23
  emits?: string[];
24
+ awaitBefore?: AwaitConfig;
25
+ awaitAfter?: AwaitConfig;
26
+ stepTimeout?: number;
14
27
  };
15
28
  kind?: 'entry' | 'step';
16
29
  };
17
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
18
- action: (payload: {
19
- id: string;
20
- action: "run" | "logs" | "details";
21
- }) => any;
22
- }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
23
- onAction?: ((payload: {
24
- id: string;
25
- action: "run" | "logs" | "details";
26
- }) => any) | undefined;
27
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
30
+ 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>;
28
31
  declare const _default: typeof __VLS_export;
29
32
  export default _default;
@@ -1,10 +1,9 @@
1
1
  <template>
2
2
  <UCard
3
- class="min-w-[220px] max-w-[300px]"
3
+ class="min-w-[260px] max-w-[340px]"
4
4
  :ui="{
5
5
  body: 'p-0',
6
- header: headerClass,
7
- footer: footerClass
6
+ header: headerClass
8
7
  }"
9
8
  >
10
9
  <template #header>
@@ -23,24 +22,49 @@
23
22
  {{ data?.label }}
24
23
  </p>
25
24
  </div>
26
- <UBadge
27
- :label="(data?.status || 'idle').toUpperCase()"
28
- size="xs"
29
- :color="statusColor(data?.status)"
30
- />
25
+ <div class="flex items-center gap-2">
26
+ <span
27
+ v-if="data?.stepTimeout !== void 0 && data.stepTimeout > 0"
28
+ class="flex items-center gap-1 text-xs opacity-80"
29
+ :title="`Step Execution Timeout: ${formatStepTimeout(data.stepTimeout)}`"
30
+ >
31
+ <UIcon
32
+ name="i-heroicons-clock-20-solid"
33
+ class="size-3"
34
+ />
35
+ <span>{{ formatStepTimeout(data.stepTimeout) }}</span>
36
+ </span>
37
+ <UBadge
38
+ :label="(data?.status || 'idle').toUpperCase()"
39
+ size="xs"
40
+ :color="statusColor(data?.status)"
41
+ />
42
+ </div>
31
43
  </div>
32
44
  </template>
33
45
 
34
46
  <div class="px-3 py-2 text-xs space-y-1">
35
47
  <div class="flex items-center justify-between">
36
- <span class="text-gray-500 dark:text-gray-400">Queue</span>
48
+ <span class="text-gray-500 dark:text-gray-400 flex items-center gap-1">
49
+ <UIcon
50
+ name="i-heroicons-queue-list-20-solid"
51
+ class="size-3"
52
+ />
53
+ Queue
54
+ </span>
37
55
  <span
38
56
  class="truncate ml-2 font-mono"
39
57
  :title="data?.queue"
40
58
  >{{ data?.queue || "-" }}</span>
41
59
  </div>
42
60
  <div class="flex items-center justify-between">
43
- <span class="text-gray-500 dark:text-gray-400">Worker</span>
61
+ <span class="text-gray-500 dark:text-gray-400 flex items-center gap-1">
62
+ <UIcon
63
+ name="i-heroicons-cpu-chip-20-solid"
64
+ class="size-3"
65
+ />
66
+ Worker
67
+ </span>
44
68
  <span
45
69
  class="truncate ml-2 font-mono"
46
70
  :title="data?.workerId"
@@ -58,11 +82,39 @@
58
82
  variant="soft"
59
83
  />
60
84
  </div>
85
+ <div
86
+ v-if="data?.subscribes && data.subscribes.length > 0"
87
+ class="flex items-start justify-between gap-2"
88
+ >
89
+ <span class="text-gray-500 dark:text-gray-400 flex items-center gap-1">
90
+ <UIcon
91
+ name="i-heroicons-arrow-down-on-square-stack-20-solid"
92
+ class="size-3"
93
+ />
94
+ Subscribes
95
+ </span>
96
+ <div class="flex flex-wrap gap-1 justify-end">
97
+ <UBadge
98
+ v-for="event in data.subscribes"
99
+ :key="event"
100
+ :label="event"
101
+ size="xs"
102
+ color="blue"
103
+ variant="soft"
104
+ />
105
+ </div>
106
+ </div>
61
107
  <div
62
108
  v-if="data?.emits && data.emits.length > 0"
63
109
  class="flex items-start justify-between gap-2"
64
110
  >
65
- <span class="text-gray-500 dark:text-gray-400">Emits</span>
111
+ <span class="text-gray-500 dark:text-gray-400 flex items-center gap-1">
112
+ <UIcon
113
+ name="i-heroicons-arrow-up-on-square-stack-20-solid"
114
+ class="size-3"
115
+ />
116
+ Emits
117
+ </span>
66
118
  <div class="flex flex-wrap gap-1 justify-end">
67
119
  <UBadge
68
120
  v-for="event in data.emits"
@@ -74,6 +126,42 @@
74
126
  />
75
127
  </div>
76
128
  </div>
129
+ <div
130
+ v-if="data?.awaitBefore"
131
+ class="flex items-center justify-between"
132
+ >
133
+ <span class="text-gray-500 dark:text-gray-400 flex items-center gap-1">
134
+ <UIcon
135
+ name="i-heroicons-clock-20-solid"
136
+ class="size-3"
137
+ />
138
+ Await Before
139
+ </span>
140
+ <UBadge
141
+ :label="data.awaitBefore.type"
142
+ size="xs"
143
+ color="violet"
144
+ variant="soft"
145
+ />
146
+ </div>
147
+ <div
148
+ v-if="data?.awaitAfter"
149
+ class="flex items-center justify-between"
150
+ >
151
+ <span class="text-gray-500 dark:text-gray-400 flex items-center gap-1">
152
+ <UIcon
153
+ name="i-heroicons-clock-20-solid"
154
+ class="size-3"
155
+ />
156
+ Await After
157
+ </span>
158
+ <UBadge
159
+ :label="data.awaitAfter.type"
160
+ size="xs"
161
+ color="violet"
162
+ variant="soft"
163
+ />
164
+ </div>
77
165
  <div
78
166
  v-if="data?.attempt && data.attempt > 1"
79
167
  class="flex items-center justify-between"
@@ -95,25 +183,6 @@
95
183
  </span>
96
184
  </div>
97
185
  </div>
98
-
99
- <template #footer>
100
- <div class="flex items-center justify-center gap-2 w-full">
101
- <UButton
102
- size="xs"
103
- color="neutral"
104
- label="Logs"
105
- icon="i-heroicons-document-text-20-solid"
106
- @click="handleAction('logs')"
107
- />
108
- <UButton
109
- size="xs"
110
- color="neutral"
111
- label="Details"
112
- icon="i-heroicons-information-circle-20-solid"
113
- @click="handleAction('details')"
114
- />
115
- </div>
116
- </template>
117
186
  </UCard>
118
187
  </template>
119
188
 
@@ -124,12 +193,7 @@ const props = defineProps({
124
193
  data: { type: Object, required: true },
125
194
  kind: { type: String, required: false }
126
195
  });
127
- const emit = defineEmits(["action"]);
128
- function handleAction(action) {
129
- emit("action", { id: props.id, action });
130
- }
131
196
  const headerClass = computed(() => props.kind === "entry" ? "px-3 py-2 bg-gradient-to-br from-emerald-800 to-emerald-700 text-emerald-50 rounded-t" : "px-3 py-2 bg-gradient-to-br from-gray-800 to-gray-700 text-gray-100 rounded-t");
132
- const footerClass = computed(() => props.kind === "entry" ? "px-3 pb-2 pt-1" : "px-3 pb-2 pt-1");
133
197
  const runnerIcon = computed(() => {
134
198
  const runtime = props.data?.runtime;
135
199
  if (runtime === "python") {
@@ -154,4 +218,24 @@ function statusColor(status) {
154
218
  return "neutral";
155
219
  }
156
220
  }
221
+ function formatStepTimeout(ms) {
222
+ if (!ms || ms === 0) return "None";
223
+ const seconds = Math.floor(ms / 1e3);
224
+ const minutes = Math.floor(seconds / 60);
225
+ const hours = Math.floor(minutes / 60);
226
+ const days = Math.floor(hours / 24);
227
+ if (days > 0) {
228
+ const remainingHours = hours % 24;
229
+ return remainingHours > 0 ? `${days}d ${remainingHours}h` : `${days}d`;
230
+ }
231
+ if (hours > 0) {
232
+ const remainingMinutes = minutes % 60;
233
+ return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`;
234
+ }
235
+ if (minutes > 0) {
236
+ const remainingSeconds = seconds % 60;
237
+ return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`;
238
+ }
239
+ return `${seconds}s`;
240
+ }
157
241
  </script>
@@ -1,4 +1,13 @@
1
1
  type Status = 'idle' | 'running' | 'error' | 'done' | 'canceled' | string | undefined;
2
+ interface AwaitConfig {
3
+ type: 'time' | 'event' | 'webhook' | 'schedule';
4
+ delay?: number;
5
+ event?: string;
6
+ method?: string;
7
+ timeout?: number;
8
+ timeoutAction?: 'fail' | 'continue';
9
+ cron?: string;
10
+ }
2
11
  type __VLS_Props = {
3
12
  id: string;
4
13
  data: {
@@ -10,20 +19,14 @@ type __VLS_Props = {
10
19
  error?: string;
11
20
  runtime?: 'nodejs' | 'python';
12
21
  runtype?: 'inprocess' | 'task';
22
+ subscribes?: string[];
13
23
  emits?: string[];
24
+ awaitBefore?: AwaitConfig;
25
+ awaitAfter?: AwaitConfig;
26
+ stepTimeout?: number;
14
27
  };
15
28
  kind?: 'entry' | 'step';
16
29
  };
17
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
18
- action: (payload: {
19
- id: string;
20
- action: "run" | "logs" | "details";
21
- }) => any;
22
- }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
23
- onAction?: ((payload: {
24
- id: string;
25
- action: "run" | "logs" | "details";
26
- }) => any) | undefined;
27
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
30
+ 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>;
28
31
  declare const _default: typeof __VLS_export;
29
32
  export default _default;
@@ -8,6 +8,7 @@ type __VLS_Props = {
8
8
  triggerName?: string;
9
9
  triggerType?: 'manual' | 'event' | 'webhook' | 'schedule';
10
10
  flowDef?: any;
11
+ stallTimeout?: number;
11
12
  };
12
13
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
14
  "select-step": (stepKey: string | null) => any;