1
1
import { type RunningWorkflowSummary } from "@squonk/data-manager-client" ;
2
- import {
3
- useGetRunningWorkflow ,
4
- useGetRunningWorkflowSteps ,
5
- } from "@squonk/data-manager-client/workflow" ;
2
+ import { useGetRunningWorkflow } from "@squonk/data-manager-client/workflow" ;
6
3
7
- import {
8
- AccountTreeRounded as AccountTreeRoundedIcon ,
9
- Person as PersonIcon ,
10
- } from "@mui/icons-material" ;
11
- import {
12
- Timeline ,
13
- TimelineConnector ,
14
- TimelineContent ,
15
- TimelineDot ,
16
- TimelineItem ,
17
- TimelineOppositeContent ,
18
- TimelineSeparator ,
19
- } from "@mui/lab" ;
20
- import {
21
- Alert ,
22
- Box ,
23
- Divider ,
24
- ListItem ,
25
- ListItemIcon ,
26
- ListItemText ,
27
- Typography ,
28
- } from "@mui/material" ;
4
+ import { Alert } from "@mui/material" ;
29
5
30
- import { getErrorMessage } from "../../utils/next/orvalError " ;
6
+ import { useIsUserAdminOrEditorOfCurrentProject } from "../../hooks/projectHooks " ;
31
7
import { CenterLoader } from "../CenterLoader" ;
32
- import { HorizontalList } from "../HorizontalList" ;
33
- import { LocalTime } from "../LocalTime" ;
34
- import { NextLink } from "../NextLink" ;
35
8
import { ResultCard } from "../results/ResultCard" ;
36
- import { type StatusIconProps } from "../results/StatusIcon" ;
9
+ import { TerminateWorkflowButton } from "../TerminateWorkflowButton" ;
10
+ import { RunningWorkflowCollapsed } from "./RunningWorkflowCollapsed" ;
37
11
38
12
export interface RunningWorkflowCardProps {
39
- /**
40
- * The ID of the running workflow to display
41
- */
42
13
runningWorkflowId : string ;
43
- /**
44
- * Optionally, a summary object for the workflow
45
- */
46
14
workflowSummary ?: RunningWorkflowSummary ;
47
- /**
48
- * Whether the card should have its collapsed content visible immediately. Defaults to true.
49
- */
50
15
collapsedByDefault ?: boolean ;
51
16
}
52
17
53
- // Map workflow status to StatusIcon-compatible state
54
- function mapWorkflowStatusToState ( status ?: string ) : StatusIconProps [ "state" ] {
18
+ function mapWorkflowStatusToState ( status ?: string ) {
55
19
switch ( status ) {
56
20
case "RUNNING" :
57
21
return "RUNNING" ;
@@ -65,152 +29,42 @@ function mapWorkflowStatusToState(status?: string): StatusIconProps["state"] {
65
29
}
66
30
}
67
31
68
- /**
69
- * Expandable card that displays details about a running workflow.
70
- * Fetches details and steps using the workflow ID.
71
- */
72
32
export const RunningWorkflowCard = ( {
73
33
runningWorkflowId,
74
34
workflowSummary,
75
35
collapsedByDefault = true ,
76
36
} : RunningWorkflowCardProps ) => {
77
- const {
78
- data : workflow ,
79
- isLoading : isWorkflowLoading ,
80
- error : workflowError ,
81
- } = useGetRunningWorkflow ( runningWorkflowId ) ;
82
- const {
83
- data : stepsData ,
84
- isLoading : isStepsLoading ,
85
- error : stepsError ,
86
- } = useGetRunningWorkflowSteps ( runningWorkflowId ) ;
37
+ const { data : workflow , isLoading, error } = useGetRunningWorkflow ( runningWorkflowId ) ;
87
38
88
- // stepsData?.running_workflow_steps is the array of steps
89
- const steps = stepsData ?. running_workflow_steps ;
39
+ const hasPermission = useIsUserAdminOrEditorOfCurrentProject ( ) ;
90
40
91
- if ( isWorkflowLoading || isStepsLoading ) {
41
+ if ( isLoading ) {
92
42
return < CenterLoader /> ;
93
43
}
94
-
95
- if ( workflowError ) {
96
- return (
97
- < Alert severity = "error" > Failed to load workflow: { getErrorMessage ( workflowError ) } </ Alert >
98
- ) ;
99
- }
100
- if ( stepsError ) {
101
- return (
102
- < Alert severity = "error" > Failed to load workflow steps: { getErrorMessage ( stepsError ) } </ Alert >
103
- ) ;
44
+ if ( error ) {
45
+ return < Alert severity = "error" > Failed to load workflow</ Alert > ;
104
46
}
105
47
106
- // Expanded content: Timeline of steps
107
- const timeline =
108
- steps && steps . length > 0 ? (
109
- < Timeline sx = { { p : 0 , m : 0 } } >
110
- { steps . map ( ( step , idx ) => {
111
- const showStopped = step . stopped && step . stopped !== step . started ;
112
- return (
113
- < TimelineItem key = { step . id } >
114
- < TimelineOppositeContent sx = { { flex : "unset" } } >
115
- < Typography variant = "caption" >
116
- { ! ! step . started && (
117
- < LocalTime showTime showDate = { false } utcTimestamp = { step . started } />
118
- ) }
119
- { ! ! showStopped && (
120
- < >
121
- { " " }
122
- < span style = { { fontStyle : "italic" } } > to </ span >
123
- { ! ! step . stopped && (
124
- < LocalTime showTime showDate = { false } utcTimestamp = { step . stopped } />
125
- ) }
126
- </ >
127
- ) }
128
- </ Typography >
129
- </ TimelineOppositeContent >
130
- < TimelineSeparator >
131
- < TimelineDot
132
- color = {
133
- step . status === "SUCCESS"
134
- ? "success"
135
- : step . status === "FAILURE"
136
- ? "error"
137
- : "info"
138
- }
139
- />
140
- { idx < steps . length - 1 && < TimelineConnector /> }
141
- </ TimelineSeparator >
142
- < TimelineContent >
143
- < Typography variant = "subtitle2" >
144
- { step . instance_id ? (
145
- < NextLink
146
- component = "a"
147
- href = { {
148
- pathname : "/results/instance/[instanceId]" ,
149
- query : { instanceId : step . instance_id } ,
150
- } }
151
- >
152
- { step . name }
153
- </ NextLink >
154
- ) : (
155
- step . name
156
- ) }
157
- </ Typography >
158
- < Typography variant = "body2" > Status: { step . status } </ Typography >
159
- { ! ! step . error_msg && (
160
- < Typography color = "error" variant = "body2" >
161
- Error: { step . error_msg }
162
- </ Typography >
163
- ) }
164
- </ TimelineContent >
165
- </ TimelineItem >
166
- ) ;
167
- } ) }
168
- </ Timeline >
169
- ) : (
170
- < Typography variant = "body2" > No steps found for this workflow.</ Typography >
171
- ) ;
172
-
173
- // Collapsed content: key workflow details and timeline (expanded content)
174
- const collapsed = (
175
- < Box >
176
- < HorizontalList >
177
- < ListItem >
178
- < ListItemIcon sx = { { minWidth : "40px" } } >
179
- < PersonIcon />
180
- </ ListItemIcon >
181
- < ListItemText primary = { workflow ?. running_user } secondary = "User" />
182
- </ ListItem >
183
- { ! ! workflow ?. project . name && (
184
- < ListItem >
185
- < ListItemIcon sx = { { minWidth : "40px" } } >
186
- < AccountTreeRoundedIcon />
187
- </ ListItemIcon >
188
- < ListItemText primary = { workflow . project . name } secondary = "Project" />
189
- </ ListItem >
190
- ) }
191
- </ HorizontalList >
192
- < Divider sx = { { my : 2 } } />
193
- < Typography gutterBottom variant = "h6" >
194
- Workflow Steps
195
- </ Typography >
196
- { timeline }
197
- </ Box >
198
- ) ;
199
-
200
48
return (
201
49
< ResultCard
202
50
accentColor = "#f1c40f"
203
- actions = { ( ) => null }
204
- collapsed = { collapsed }
51
+ actions = { ( ) => (
52
+ < TerminateWorkflowButton
53
+ disabled = { ! hasPermission }
54
+ runningWorkflowId = { runningWorkflowId }
55
+ status = { workflow ?. status ?? workflowSummary ?. status }
56
+ />
57
+ ) }
58
+ collapsed = { < RunningWorkflowCollapsed runningWorkflowId = { runningWorkflowId } /> }
205
59
collapsedByDefault = { collapsedByDefault }
206
- createdDateTime = { workflow ?. started ?? "" }
207
- finishedDateTime = { workflow ?. stopped ?? "" }
60
+ createdDateTime = { workflow ?. started ?? workflowSummary ?. started ?? "" }
61
+ finishedDateTime = { workflow ?. stopped ?? workflowSummary ?. stopped ?? "" }
208
62
href = { {
209
63
pathname : "/results/workflow/[workflowId]" ,
210
64
query : { workflowId : workflow ?. id ?? workflowSummary ?. id ?? "" } ,
211
65
} }
212
66
linkTitle = { workflow ?. name ?? workflowSummary ?. name ?? "Workflow" }
213
- state = { mapWorkflowStatusToState ( workflow ?. status ) }
67
+ state = { mapWorkflowStatusToState ( workflow ?. status ?? workflowSummary ?. status ) }
214
68
/>
215
69
) ;
216
70
} ;
0 commit comments