@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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +9 -1
- package/dist/runtime/app/components/ComponentShell.d.vue.ts +1 -0
- package/dist/runtime/app/components/ComponentShell.vue +13 -3
- package/dist/runtime/app/components/ComponentShell.vue.d.ts +1 -0
- package/dist/runtime/app/components/DashboardCard.d.vue.ts +15 -0
- package/dist/runtime/app/components/DashboardCard.vue +76 -0
- package/dist/runtime/app/components/DashboardCard.vue.d.ts +15 -0
- package/dist/runtime/app/components/TimelineList.vue +222 -31
- package/dist/runtime/app/components/flow/AwaitNode.d.vue.ts +19 -2
- package/dist/runtime/app/components/flow/AwaitNode.vue +317 -29
- package/dist/runtime/app/components/flow/AwaitNode.vue.d.ts +19 -2
- package/dist/runtime/app/components/flow/Diagram.d.vue.ts +2 -1
- package/dist/runtime/app/components/flow/Diagram.vue +138 -74
- package/dist/runtime/app/components/flow/Diagram.vue.d.ts +2 -1
- package/dist/runtime/app/components/flow/NodeCard.d.vue.ts +14 -11
- package/dist/runtime/app/components/flow/NodeCard.vue +119 -35
- package/dist/runtime/app/components/flow/NodeCard.vue.d.ts +14 -11
- package/dist/runtime/app/components/flow/RunOverview.d.vue.ts +1 -0
- package/dist/runtime/app/components/flow/RunOverview.vue +85 -86
- package/dist/runtime/app/components/flow/RunOverview.vue.d.ts +1 -0
- package/dist/runtime/app/components/flow/RunTimeline.vue +51 -22
- package/dist/runtime/app/components/flow/StepSelector.vue +124 -46
- package/dist/runtime/app/composables/useFlowRunTimeline.d.ts +6 -0
- package/dist/runtime/app/composables/useFlowState.d.ts +8 -1
- package/dist/runtime/app/composables/useFlowState.js +34 -59
- package/dist/runtime/app/pages/dashboard.vue +51 -103
- package/dist/runtime/app/pages/flows/[name].vue +26 -3
- package/dist/runtime/app/pages/triggers/[name]/edit.vue +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import { type Ref } from '#imports';
|
|
|
5
5
|
* Reduces an array of events from the flow timeline into current state.
|
|
6
6
|
*/
|
|
7
7
|
export interface FlowState {
|
|
8
|
-
status: 'running' | 'completed' | 'failed' | 'canceled' | 'stalled';
|
|
8
|
+
status: 'running' | 'completed' | 'failed' | 'canceled' | 'stalled' | 'awaiting';
|
|
9
9
|
startedAt?: string;
|
|
10
10
|
completedAt?: string;
|
|
11
11
|
steps: Record<string, StepState>;
|
|
@@ -17,6 +17,7 @@ export interface StepState {
|
|
|
17
17
|
attempt: number;
|
|
18
18
|
startedAt?: string;
|
|
19
19
|
completedAt?: string;
|
|
20
|
+
scheduledTriggerAt?: string;
|
|
20
21
|
error?: string;
|
|
21
22
|
awaitType?: 'time' | 'event' | 'trigger';
|
|
22
23
|
awaitData?: any;
|
|
@@ -69,11 +70,13 @@ export declare function useFlowState(initialEvents?: EventRecord[]): {
|
|
|
69
70
|
isFailed: import("vue").ComputedRef<boolean>;
|
|
70
71
|
isCanceled: import("vue").ComputedRef<boolean>;
|
|
71
72
|
isStalled: import("vue").ComputedRef<boolean>;
|
|
73
|
+
isAwaiting: import("vue").ComputedRef<boolean>;
|
|
72
74
|
stepList: import("vue").ComputedRef<{
|
|
73
75
|
status: "pending" | "running" | "completed" | "failed" | "retrying" | "waiting" | "timeout";
|
|
74
76
|
attempt: number;
|
|
75
77
|
startedAt?: string;
|
|
76
78
|
completedAt?: string;
|
|
79
|
+
scheduledTriggerAt?: string;
|
|
77
80
|
error?: string;
|
|
78
81
|
awaitType?: "time" | "event" | "trigger";
|
|
79
82
|
awaitData?: any;
|
|
@@ -85,6 +88,7 @@ export declare function useFlowState(initialEvents?: EventRecord[]): {
|
|
|
85
88
|
attempt: number;
|
|
86
89
|
startedAt?: string;
|
|
87
90
|
completedAt?: string;
|
|
91
|
+
scheduledTriggerAt?: string;
|
|
88
92
|
error?: string;
|
|
89
93
|
awaitType?: "time" | "event" | "trigger";
|
|
90
94
|
awaitData?: any;
|
|
@@ -96,6 +100,7 @@ export declare function useFlowState(initialEvents?: EventRecord[]): {
|
|
|
96
100
|
attempt: number;
|
|
97
101
|
startedAt?: string;
|
|
98
102
|
completedAt?: string;
|
|
103
|
+
scheduledTriggerAt?: string;
|
|
99
104
|
error?: string;
|
|
100
105
|
awaitType?: "time" | "event" | "trigger";
|
|
101
106
|
awaitData?: any;
|
|
@@ -107,6 +112,7 @@ export declare function useFlowState(initialEvents?: EventRecord[]): {
|
|
|
107
112
|
attempt: number;
|
|
108
113
|
startedAt?: string;
|
|
109
114
|
completedAt?: string;
|
|
115
|
+
scheduledTriggerAt?: string;
|
|
110
116
|
error?: string;
|
|
111
117
|
awaitType?: "time" | "event" | "trigger";
|
|
112
118
|
awaitData?: any;
|
|
@@ -118,6 +124,7 @@ export declare function useFlowState(initialEvents?: EventRecord[]): {
|
|
|
118
124
|
attempt: number;
|
|
119
125
|
startedAt?: string;
|
|
120
126
|
completedAt?: string;
|
|
127
|
+
scheduledTriggerAt?: string;
|
|
121
128
|
error?: string;
|
|
122
129
|
awaitType?: "time" | "event" | "trigger";
|
|
123
130
|
awaitData?: any;
|
|
@@ -16,6 +16,7 @@ export function reduceFlowState(events) {
|
|
|
16
16
|
if (e.flowName) state.meta = { ...state.meta, flowName: e.flowName };
|
|
17
17
|
if (e.data?.flowName) state.meta = { ...state.meta, flowName: e.data.flowName };
|
|
18
18
|
if (e.data?.input) state.meta = { ...state.meta, input: e.data.input };
|
|
19
|
+
if (e.data?.stallTimeout) state.meta = { ...state.meta, stallTimeout: e.data.stallTimeout };
|
|
19
20
|
if (e.data?.trigger) {
|
|
20
21
|
state.meta = {
|
|
21
22
|
...state.meta,
|
|
@@ -43,7 +44,6 @@ export function reduceFlowState(events) {
|
|
|
43
44
|
case "flow.stalled":
|
|
44
45
|
state.status = "stalled";
|
|
45
46
|
if (e.data?.lastActivityAt) state.meta = { ...state.meta, lastActivityAt: e.data.lastActivityAt };
|
|
46
|
-
if (e.data?.stallTimeout) state.meta = { ...state.meta, stallTimeout: e.data.stallTimeout };
|
|
47
47
|
break;
|
|
48
48
|
case "step.started": {
|
|
49
49
|
if (!stepKey) break;
|
|
@@ -128,60 +128,44 @@ export function reduceFlowState(events) {
|
|
|
128
128
|
}
|
|
129
129
|
case "await.registered": {
|
|
130
130
|
if (!stepKey) break;
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
if (!state.steps[
|
|
134
|
-
state.steps[
|
|
131
|
+
const position = e.data?.position || "after";
|
|
132
|
+
const awaitKey = `${stepKey}:await-${position}`;
|
|
133
|
+
if (!state.steps[awaitKey]) {
|
|
134
|
+
state.steps[awaitKey] = { status: "waiting", attempt: 1 };
|
|
135
135
|
}
|
|
136
|
-
state.steps[
|
|
137
|
-
state.steps[
|
|
138
|
-
state.steps[
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
state.steps[awaitKey].status = "waiting";
|
|
137
|
+
state.steps[awaitKey].awaitType = e.data?.awaitType;
|
|
138
|
+
state.steps[awaitKey].awaitData = e.data;
|
|
139
|
+
const scheduledAt = e.data?.resolveAt || e.data?.nextOccurrence;
|
|
140
|
+
if (scheduledAt) {
|
|
141
|
+
state.steps[awaitKey].scheduledTriggerAt = new Date(scheduledAt).toISOString();
|
|
141
142
|
}
|
|
142
|
-
state.steps[awaitKeyBefore].status = "waiting";
|
|
143
|
-
state.steps[awaitKeyBefore].awaitType = e.data?.awaitType;
|
|
144
|
-
state.steps[awaitKeyBefore].awaitData = e.data;
|
|
145
143
|
break;
|
|
146
144
|
}
|
|
147
145
|
case "await.resolved": {
|
|
148
146
|
if (!stepKey) break;
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
if (!state.steps[
|
|
152
|
-
state.steps[
|
|
153
|
-
}
|
|
154
|
-
state.steps[awaitKeyAfter].status = "completed";
|
|
155
|
-
state.steps[awaitKeyAfter].completedAt = e.ts;
|
|
156
|
-
state.steps[awaitKeyAfter].awaitType = e.data?.awaitType;
|
|
157
|
-
if (e.data?.triggerData) state.steps[awaitKeyAfter].result = e.data.triggerData;
|
|
158
|
-
if (!state.steps[awaitKeyBefore]) {
|
|
159
|
-
state.steps[awaitKeyBefore] = { status: "completed", attempt: 1 };
|
|
147
|
+
const position = e.data?.position || "after";
|
|
148
|
+
const awaitKey = `${stepKey}:await-${position}`;
|
|
149
|
+
if (!state.steps[awaitKey]) {
|
|
150
|
+
state.steps[awaitKey] = { status: "completed", attempt: 1 };
|
|
160
151
|
}
|
|
161
|
-
state.steps[
|
|
162
|
-
state.steps[
|
|
163
|
-
state.steps[
|
|
164
|
-
if (e.data?.triggerData) state.steps[
|
|
152
|
+
state.steps[awaitKey].status = "completed";
|
|
153
|
+
state.steps[awaitKey].completedAt = e.ts;
|
|
154
|
+
state.steps[awaitKey].awaitType = e.data?.awaitType;
|
|
155
|
+
if (e.data?.triggerData) state.steps[awaitKey].result = e.data.triggerData;
|
|
165
156
|
break;
|
|
166
157
|
}
|
|
167
158
|
case "await.timeout": {
|
|
168
159
|
if (!stepKey) break;
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
if (!state.steps[
|
|
172
|
-
state.steps[
|
|
160
|
+
const position = e.data?.position || "after";
|
|
161
|
+
const awaitKey = `${stepKey}:await-${position}`;
|
|
162
|
+
if (!state.steps[awaitKey]) {
|
|
163
|
+
state.steps[awaitKey] = { status: "timeout", attempt: 1 };
|
|
173
164
|
}
|
|
174
|
-
state.steps[
|
|
175
|
-
state.steps[
|
|
176
|
-
state.steps[
|
|
177
|
-
state.steps[
|
|
178
|
-
if (!state.steps[awaitKeyBefore]) {
|
|
179
|
-
state.steps[awaitKeyBefore] = { status: "timeout", attempt: 1 };
|
|
180
|
-
}
|
|
181
|
-
state.steps[awaitKeyBefore].status = "timeout";
|
|
182
|
-
state.steps[awaitKeyBefore].error = `Await timeout`;
|
|
183
|
-
state.steps[awaitKeyBefore].completedAt = e.ts;
|
|
184
|
-
state.steps[awaitKeyBefore].awaitType = e.data?.awaitType;
|
|
165
|
+
state.steps[awaitKey].status = "timeout";
|
|
166
|
+
state.steps[awaitKey].error = `Await timeout`;
|
|
167
|
+
state.steps[awaitKey].completedAt = e.ts;
|
|
168
|
+
state.steps[awaitKey].awaitType = e.data?.awaitType;
|
|
185
169
|
break;
|
|
186
170
|
}
|
|
187
171
|
case "runner.log":
|
|
@@ -211,23 +195,12 @@ export function reduceFlowState(events) {
|
|
|
211
195
|
state.startedAt = events[0].ts;
|
|
212
196
|
}
|
|
213
197
|
if (state.status === "running" && state.startedAt && Object.keys(state.steps).length > 0) {
|
|
214
|
-
const
|
|
215
|
-
(s) => s.status === "running" || s.status === "retrying"
|
|
216
|
-
);
|
|
217
|
-
const hasFailedSteps = Object.values(state.steps).some((s) => s.status === "failed");
|
|
218
|
-
const allStepsTerminal = Object.values(state.steps).every(
|
|
219
|
-
(s) => s.status === "completed" || s.status === "failed" || s.status === "timeout"
|
|
198
|
+
const hasActiveRunningSteps = Object.values(state.steps).some(
|
|
199
|
+
(s) => s.status === "running" || s.status === "retrying"
|
|
220
200
|
);
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
} else {
|
|
225
|
-
state.status = "completed";
|
|
226
|
-
}
|
|
227
|
-
const latestCompletion = Object.values(state.steps).map((s) => s.completedAt).filter(Boolean).sort().pop();
|
|
228
|
-
if (latestCompletion) {
|
|
229
|
-
state.completedAt = latestCompletion;
|
|
230
|
-
}
|
|
201
|
+
const hasWaitingSteps = Object.values(state.steps).some((s) => s.status === "waiting");
|
|
202
|
+
if (hasWaitingSteps && !hasActiveRunningSteps) {
|
|
203
|
+
state.status = "awaiting";
|
|
231
204
|
}
|
|
232
205
|
}
|
|
233
206
|
return state;
|
|
@@ -249,6 +222,7 @@ export function useFlowState(initialEvents = []) {
|
|
|
249
222
|
const isFailed = computed(() => state.value.status === "failed");
|
|
250
223
|
const isCanceled = computed(() => state.value.status === "canceled");
|
|
251
224
|
const isStalled = computed(() => state.value.status === "stalled");
|
|
225
|
+
const isAwaiting = computed(() => state.value.status === "awaiting");
|
|
252
226
|
const stepList = computed(() => {
|
|
253
227
|
return Object.entries(state.value.steps).map(([key, step]) => ({
|
|
254
228
|
key,
|
|
@@ -281,6 +255,7 @@ export function useFlowState(initialEvents = []) {
|
|
|
281
255
|
isFailed,
|
|
282
256
|
isCanceled,
|
|
283
257
|
isStalled,
|
|
258
|
+
isAwaiting,
|
|
284
259
|
stepList,
|
|
285
260
|
runningSteps,
|
|
286
261
|
waitingSteps,
|
|
@@ -27,109 +27,54 @@
|
|
|
27
27
|
<!-- System Stats Overview -->
|
|
28
28
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
29
29
|
<!-- Queues Card -->
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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>
|
|
30
|
+
<NventDashboardCard
|
|
31
|
+
icon="i-lucide-app-window"
|
|
32
|
+
title="Active Queues"
|
|
33
|
+
color="blue"
|
|
34
|
+
:primary-stats="[
|
|
35
|
+
{ value: formatNumber(queuesStats?.active || 0), label: 'active' },
|
|
36
|
+
{ value: formatNumber((queuesStats?.waiting || 0) + (queuesStats?.delayed || 0)), label: 'waiting' },
|
|
37
|
+
...queuesStats?.failed > 0 ? [{ value: formatNumber(queuesStats?.failed || 0), label: 'failed' }] : []
|
|
38
|
+
]"
|
|
39
|
+
:secondary-stats="[
|
|
40
|
+
{ value: queuesStats?.total || 0, label: 'queues' },
|
|
41
|
+
{ value: formatNumber(queuesStats?.completed || 0), label: 'completed' }
|
|
42
|
+
]"
|
|
43
|
+
:on-click="() => navigateTo('/queues')"
|
|
44
|
+
/>
|
|
64
45
|
|
|
65
46
|
<!-- Flows Card -->
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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>
|
|
47
|
+
<NventDashboardCard
|
|
48
|
+
icon="i-lucide-git-branch"
|
|
49
|
+
title="Registered Flows"
|
|
50
|
+
color="purple"
|
|
51
|
+
:primary-stats="[
|
|
52
|
+
...flowsStats?.running > 0 ? [{ value: flowsStats?.running, label: 'running' }] : [],
|
|
53
|
+
...flowsStats?.awaiting > 0 ? [{ value: flowsStats?.awaiting, label: 'awaiting' }] : [],
|
|
54
|
+
...flowsStats?.success > 0 ? [{ value: formatNumber(flowsStats?.success), label: 'completed' }] : [],
|
|
55
|
+
...flowsStats?.failure > 0 ? [{ value: formatNumber(flowsStats?.failure), label: 'failed' }] : []
|
|
56
|
+
]"
|
|
57
|
+
:secondary-stats="[
|
|
58
|
+
{ value: flowsStats?.total || 0, label: 'flows' },
|
|
59
|
+
{ value: formatNumber(flowsStats?.totalRuns || 0), label: 'total runs' }
|
|
60
|
+
]"
|
|
61
|
+
:on-click="() => navigateTo('/flows')"
|
|
62
|
+
/>
|
|
100
63
|
|
|
101
64
|
<!-- Triggers Card -->
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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>
|
|
65
|
+
<NventDashboardCard
|
|
66
|
+
icon="i-lucide-zap"
|
|
67
|
+
title="Active Triggers"
|
|
68
|
+
color="amber"
|
|
69
|
+
:primary-stats="[
|
|
70
|
+
{ value: formatNumber(triggersStats?.totalFires || 0), label: 'fires' }
|
|
71
|
+
]"
|
|
72
|
+
:secondary-stats="[
|
|
73
|
+
{ value: triggersStats?.total || 0, label: 'triggers' },
|
|
74
|
+
{ value: formatNumber(triggersStats?.totalFlowsStarted || 0), label: 'flows started' }
|
|
75
|
+
]"
|
|
76
|
+
:on-click="() => navigateTo('/triggers')"
|
|
77
|
+
/>
|
|
133
78
|
</div>
|
|
134
79
|
|
|
135
80
|
<!-- Quick Actions & Recent Activity -->
|
|
@@ -649,14 +594,17 @@ function updateTriggerStats(data) {
|
|
|
649
594
|
];
|
|
650
595
|
}
|
|
651
596
|
const queuesStats = computed(() => {
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
597
|
+
const active = queues.value.reduce((sum, q) => sum + (q.counts?.active || 0), 0);
|
|
598
|
+
const waiting = queues.value.reduce((sum, q) => sum + (q.counts?.waiting || 0), 0);
|
|
599
|
+
const delayed = queues.value.reduce((sum, q) => sum + (q.counts?.delayed || 0), 0);
|
|
600
|
+
const pending = active + waiting + delayed;
|
|
656
601
|
const completed = queues.value.reduce((sum, q) => sum + (q.counts?.completed || 0), 0);
|
|
657
602
|
const failed = queues.value.reduce((sum, q) => sum + (q.counts?.failed || 0), 0);
|
|
658
603
|
return {
|
|
659
604
|
total: queues.value.length,
|
|
605
|
+
active,
|
|
606
|
+
waiting,
|
|
607
|
+
delayed,
|
|
660
608
|
pending,
|
|
661
609
|
completed,
|
|
662
610
|
failed
|
|
@@ -288,12 +288,13 @@
|
|
|
288
288
|
:run-status="runSnapshot.status"
|
|
289
289
|
:started-at="runSnapshot.startedAt"
|
|
290
290
|
:completed-at="runSnapshot.completedAt"
|
|
291
|
-
:steps="
|
|
291
|
+
:steps="enhancedStepList"
|
|
292
292
|
:flow-name="selectedFlow || void 0"
|
|
293
293
|
:run-id="selectedRunId || void 0"
|
|
294
294
|
:trigger-name="flowState.state.value.meta?.triggerName"
|
|
295
295
|
:trigger-type="flowState.state.value.meta?.triggerType"
|
|
296
296
|
:flow-def="selectedFlowDef"
|
|
297
|
+
:stall-timeout="runSnapshot.stallTimeout"
|
|
297
298
|
@select-step="handleSelectStep"
|
|
298
299
|
@cancel-flow="handleCancelFlow"
|
|
299
300
|
/>
|
|
@@ -423,7 +424,7 @@ const selectedRunId = computed({
|
|
|
423
424
|
const goBack = () => {
|
|
424
425
|
componentRouter.push("/flows");
|
|
425
426
|
};
|
|
426
|
-
const mainTab = ref("diagram");
|
|
427
|
+
const mainTab = ref(route.query.run ? "timeline" : "diagram");
|
|
427
428
|
const mainTabs = computed(() => [
|
|
428
429
|
{ label: "Diagram", value: "diagram", icon: "i-lucide-git-branch" },
|
|
429
430
|
{
|
|
@@ -533,12 +534,15 @@ const formatDuration = (start, end) => {
|
|
|
533
534
|
};
|
|
534
535
|
const runSnapshot = computed(() => {
|
|
535
536
|
const state = flowState.state.value;
|
|
537
|
+
const flowMeta = selectedFlowMeta.value;
|
|
536
538
|
return {
|
|
537
539
|
status: state.status,
|
|
538
540
|
startedAt: state.startedAt,
|
|
539
541
|
completedAt: state.completedAt,
|
|
540
542
|
logsCount: state.logs.length,
|
|
541
|
-
lastLogLevel: state.logs.length > 0 ? state.logs[state.logs.length - 1]?.level : void 0
|
|
543
|
+
lastLogLevel: state.logs.length > 0 ? state.logs[state.logs.length - 1]?.level : void 0,
|
|
544
|
+
// Use stallTimeout from event data if available, otherwise fall back to static flow definition
|
|
545
|
+
stallTimeout: state.meta?.stallTimeout || flowMeta?.analyzed?.stallTimeout
|
|
542
546
|
};
|
|
543
547
|
});
|
|
544
548
|
const selectedStepKey = ref(null);
|
|
@@ -557,6 +561,25 @@ const selectedFlowMeta = computed(() => {
|
|
|
557
561
|
if (!id) return null;
|
|
558
562
|
return (flows.value || []).find((f) => f?.id === id) || null;
|
|
559
563
|
});
|
|
564
|
+
const enhancedStepList = computed(() => {
|
|
565
|
+
const steps = flowState.stepList.value;
|
|
566
|
+
const flowMeta = selectedFlowMeta.value;
|
|
567
|
+
if (!flowMeta?.analyzed?.steps) return steps;
|
|
568
|
+
const stepTimeoutMap = /* @__PURE__ */ new Map();
|
|
569
|
+
for (const [stepName, analyzedStep] of Object.entries(flowMeta.analyzed.steps)) {
|
|
570
|
+
if (analyzedStep.stepTimeout !== void 0) {
|
|
571
|
+
stepTimeoutMap.set(stepName, analyzedStep.stepTimeout);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return steps.map((step) => {
|
|
575
|
+
const baseStepName = step.key.includes(":await-") ? step.key.split(":await-")[0] : step.key;
|
|
576
|
+
const staticTimeout = stepTimeoutMap.get(baseStepName);
|
|
577
|
+
if (staticTimeout !== void 0) {
|
|
578
|
+
return { ...step, stepTimeout: staticTimeout };
|
|
579
|
+
}
|
|
580
|
+
return step;
|
|
581
|
+
});
|
|
582
|
+
});
|
|
560
583
|
const handleSelectStep = (stepKey) => {
|
|
561
584
|
selectedStepKey.value = stepKey;
|
|
562
585
|
};
|
|
@@ -314,7 +314,7 @@ const hasChanges = computed(() => {
|
|
|
314
314
|
const saveError = ref(null);
|
|
315
315
|
const saveSuccess = ref(false);
|
|
316
316
|
const onSubmit = async (event) => {
|
|
317
|
-
if (!trigger.value) return;
|
|
317
|
+
if (!trigger.value || isSaving.value) return;
|
|
318
318
|
isSaving.value = true;
|
|
319
319
|
saveError.value = null;
|
|
320
320
|
saveSuccess.value = false;
|