11import { 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" ;
63
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" ;
295
30- import { getErrorMessage } from "../../utils/next/orvalError " ;
6+ import { useIsUserAdminOrEditorOfCurrentProject } from "../../hooks/projectHooks " ;
317import { CenterLoader } from "../CenterLoader" ;
32- import { HorizontalList } from "../HorizontalList" ;
33- import { LocalTime } from "../LocalTime" ;
34- import { NextLink } from "../NextLink" ;
358import { ResultCard } from "../results/ResultCard" ;
36- import { type StatusIconProps } from "../results/StatusIcon" ;
9+ import { TerminateWorkflowButton } from "../TerminateWorkflowButton" ;
10+ import { RunningWorkflowCollapsed } from "./RunningWorkflowCollapsed" ;
3711
3812export interface RunningWorkflowCardProps {
39- /**
40- * The ID of the running workflow to display
41- */
4213 runningWorkflowId : string ;
43- /**
44- * Optionally, a summary object for the workflow
45- */
4614 workflowSummary ?: RunningWorkflowSummary ;
47- /**
48- * Whether the card should have its collapsed content visible immediately. Defaults to true.
49- */
5015 collapsedByDefault ?: boolean ;
5116}
5217
53- // Map workflow status to StatusIcon-compatible state
54- function mapWorkflowStatusToState ( status ?: string ) : StatusIconProps [ "state" ] {
18+ function mapWorkflowStatusToState ( status ?: string ) {
5519 switch ( status ) {
5620 case "RUNNING" :
5721 return "RUNNING" ;
@@ -65,152 +29,42 @@ function mapWorkflowStatusToState(status?: string): StatusIconProps["state"] {
6529 }
6630}
6731
68- /**
69- * Expandable card that displays details about a running workflow.
70- * Fetches details and steps using the workflow ID.
71- */
7232export const RunningWorkflowCard = ( {
7333 runningWorkflowId,
7434 workflowSummary,
7535 collapsedByDefault = true ,
7636} : 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 ) ;
8738
88- // stepsData?.running_workflow_steps is the array of steps
89- const steps = stepsData ?. running_workflow_steps ;
39+ const hasPermission = useIsUserAdminOrEditorOfCurrentProject ( ) ;
9040
91- if ( isWorkflowLoading || isStepsLoading ) {
41+ if ( isLoading ) {
9242 return < CenterLoader /> ;
9343 }
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 > ;
10446 }
10547
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-
20048 return (
20149 < ResultCard
20250 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 } /> }
20559 collapsedByDefault = { collapsedByDefault }
206- createdDateTime = { workflow ?. started ?? "" }
207- finishedDateTime = { workflow ?. stopped ?? "" }
60+ createdDateTime = { workflow ?. started ?? workflowSummary ?. started ?? "" }
61+ finishedDateTime = { workflow ?. stopped ?? workflowSummary ?. stopped ?? "" }
20862 href = { {
20963 pathname : "/results/workflow/[workflowId]" ,
21064 query : { workflowId : workflow ?. id ?? workflowSummary ?. id ?? "" } ,
21165 } }
21266 linkTitle = { workflow ?. name ?? workflowSummary ?. name ?? "Workflow" }
213- state = { mapWorkflowStatusToState ( workflow ?. status ) }
67+ state = { mapWorkflowStatusToState ( workflow ?. status ?? workflowSummary ?. status ) }
21468 />
21569 ) ;
21670} ;
0 commit comments