11import { useEffect } from "react" ;
22import Notifications from "../../../shared/Notifications" ;
33import {
4+ getLatestWorkflowOperation ,
45 getModalWorkflowId ,
56 getWorkflow ,
67 isFetchingWorkflowDetails ,
@@ -11,6 +12,10 @@ import { getUserInformation } from "../../../../selectors/userInfoSelectors";
1112import { useAppDispatch , useAppSelector } from "../../../../store" ;
1213import {
1314 fetchWorkflowDetails ,
15+ fetchWorkflowOperationDetails ,
16+ fetchWorkflowOperations ,
17+ fetchWorkflows ,
18+ setModalWorkflowId ,
1419 setModalWorkflowTabHierarchy ,
1520} from "../../../../slices/eventDetailsSlice" ;
1621import { removeNotificationWizardForm } from "../../../../slices/notificationSlice" ;
@@ -20,6 +25,8 @@ import { useTranslation } from "react-i18next";
2025import ButtonLikeAnchor from "../../../shared/ButtonLikeAnchor" ;
2126import { ParseKeys } from "i18next" ;
2227import ModalContentTable from "../../../shared/modals/ModalContentTable" ;
28+ import EventDetailsWorkflowErrors from "./EventDetailsWorkflowErrors" ;
29+ import { Operation , WorfklowOperationsTableBody } from "./EventDetailsWorkflowOperations" ;
2330
2431/**
2532 * This component manages the workflow details for the workflows tab of the event details modal
@@ -38,8 +45,19 @@ const EventDetailsWorkflowDetails = ({
3845 const isFetching = useAppSelector ( state => isFetchingWorkflowDetails ( state ) ) ;
3946
4047 useEffect ( ( ) => {
41- dispatch ( fetchWorkflowDetails ( { eventId, workflowId } ) ) ;
42- // eslint-disable-next-line react-hooks/exhaustive-deps
48+ // Get latest workflow. Ideally we would have an endpoint that gives us the latest workflow straight up.
49+ if ( ! workflowId ) {
50+ dispatch ( fetchWorkflows ( eventId ) ) . unwrap ( )
51+ . then ( workflows => {
52+ const currentWorkflow = workflows . entries [ workflows . entries . length - 1 ] ;
53+ dispatch ( fetchWorkflowDetails ( { eventId, workflowId : currentWorkflow . id } ) ) ;
54+ dispatch ( setModalWorkflowId ( currentWorkflow . id ) ) ;
55+ } ,
56+ ) ;
57+ } else {
58+ dispatch ( fetchWorkflowDetails ( { eventId, workflowId } ) ) ;
59+ }
60+ // eslint-disable-next-line react-hooks/exhaustive-deps
4361 } , [ ] ) ;
4462
4563 const openSubTab = ( tabType : WorkflowTabHierarchy ) => {
@@ -58,15 +76,21 @@ const EventDetailsWorkflowDetails = ({
5876 /* Hierarchy navigation */
5977 < EventDetailsTabHierarchyNavigation
6078 openSubTab = { openSubTab }
61- hierarchyDepth = { 0 }
62- translationKey0 = { "EVENTS.EVENTS.DETAILS.WORKFLOW_DETAILS.TITLE" }
63- subTabArgument0 = { "workflow-details" }
79+ hierarchyDepth = { 1 }
80+ translationKey0 = { "EVENTS.EVENTS.DETAILS.WORKFLOW_INSTANCES.TITLE" }
81+ subTabArgument0 = { "workflows" }
82+ translationKey1 = { "EVENTS.EVENTS.DETAILS.WORKFLOW_DETAILS.TITLE" }
83+ subTabArgument1 = { "workflow-details" }
6484 />
6585 }
6686 >
6787 { /* Notifications */ }
6888 < Notifications context = "not_corner" />
6989
90+ < OperationsPreview eventId = { eventId } openSubTab = { openSubTab } />
91+
92+ < EventDetailsWorkflowErrors eventId = { eventId } />
93+
7094 { /* the contained view is only displayed, if the data has been fetched */ }
7195 { isFetching || (
7296 < >
@@ -206,66 +230,43 @@ const EventDetailsWorkflowDetails = ({
206230 </ div >
207231 ) }
208232
209- { /* 'More Information' table */ }
233+ </ >
234+ ) }
235+
236+ { /* empty view for displaying, while the data is being fetched */ }
237+ { isFetching && (
238+ < >
239+ { /* 'Workflow Operation table */ }
210240 < div className = "obj tbl-container more-info-actions" >
241+ < header >
242+ { t ( "EVENTS.EVENTS.DETAILS.WORKFLOW_DETAILS.OPERATIONS" ) }
243+ </ header >
244+
245+ < table className = "main-tbl" >
246+ < tbody >
247+ < tr />
248+ </ tbody >
249+ </ table >
250+ </ div >
251+
252+ { /* 'Workflow Errors' table */ }
253+ < div className = "obj tbl-details" >
211254 < header >
212255 {
213256 t (
214- "EVENTS.EVENTS.DETAILS.WORKFLOWS.MORE_INFO " ,
215- ) /* More Information */
257+ "EVENTS.EVENTS.DETAILS.ERRORS_AND_WARNINGS.HEADER " ,
258+ ) /* Errors & Warnings */
216259 }
217260 </ header >
218-
219- { /* links to 'Operations' or 'Errors & Warnings' sub-Tabs */ }
220261 < div className = "obj-container" >
221- < ul >
222- < li >
223- < span >
224- {
225- t (
226- "EVENTS.EVENTS.DETAILS.WORKFLOW_OPERATIONS.DETAILS_LINK" ,
227- ) /* Operations */
228- }
229- </ span >
230- < ButtonLikeAnchor
231- extraClassName = "details-link"
232- onClick = { ( ) => openSubTab ( "workflow-operations" ) }
233- >
234- {
235- t (
236- "EVENTS.EVENTS.DETAILS.WORKFLOWS.DETAILS" ,
237- ) /* Details */
238- }
239- </ ButtonLikeAnchor >
240- </ li >
241- < li >
242- < span >
243- {
244- t (
245- "EVENTS.EVENTS.DETAILS.ERRORS_AND_WARNINGS.TITLE" ,
246- ) /* Errors & Warnings */
247- }
248- </ span >
249- < ButtonLikeAnchor
250- extraClassName = "details-link"
251- onClick = { ( ) => openSubTab ( "errors-and-warnings" ) }
252- >
253- {
254- t (
255- "EVENTS.EVENTS.DETAILS.WORKFLOWS.DETAILS" ,
256- ) /* Details */
257- }
258- </ ButtonLikeAnchor >
259- </ li >
260- </ ul >
262+ < table className = "main-tbl vertical-headers" >
263+ < tbody >
264+ < tr />
265+ </ tbody >
266+ </ table >
261267 </ div >
262268 </ div >
263- </ >
264- ) }
265269
266- { /* empty view for displaying, while the data is being fetched */ }
267- { isFetching && (
268- < >
269270 { /* 'Workflow Details' table */ }
270271 < div className = "obj tbl-details" >
271272 < header >
@@ -303,57 +304,98 @@ const EventDetailsWorkflowDetails = ({
303304 </ div >
304305 </ div >
305306 ) }
306-
307- { /* 'More Information' table */ }
308- < div className = "obj tbl-container more-info-actions" >
309- < header >
310- {
311- t (
312- "EVENTS.EVENTS.DETAILS.WORKFLOWS.MORE_INFO" ,
313- ) /* More Information */
314- }
315- </ header >
316- < div className = "obj-container" >
317- < ul >
318- < li >
319- < span >
320- {
321- t (
322- "EVENTS.EVENTS.DETAILS.WORKFLOW_OPERATIONS.DETAILS_LINK" ,
323- ) /* Operations */
324- }
325- </ span >
326- < ButtonLikeAnchor extraClassName = "details-link" >
327- {
328- t (
329- "EVENTS.EVENTS.DETAILS.WORKFLOWS.DETAILS" ,
330- ) /* Details */
331- }
332- </ ButtonLikeAnchor >
333- </ li >
334- < li >
335- < span >
336- {
337- t (
338- "EVENTS.EVENTS.DETAILS.ERRORS_AND_WARNINGS.TITLE" ,
339- ) /* Errors & Warnings */
340- }
341- </ span >
342- < ButtonLikeAnchor extraClassName = "details-link" >
343- {
344- t (
345- "EVENTS.EVENTS.DETAILS.WORKFLOWS.DETAILS" ,
346- ) /* Details */
347- }
348- </ ButtonLikeAnchor >
349- </ li >
350- </ ul >
351- </ div >
352- </ div >
353307 </ >
354308 ) }
355309 </ ModalContentTable >
356310 ) ;
357311} ;
358312
313+ const OperationsPreview = ( {
314+ eventId,
315+ openSubTab,
316+ } : {
317+ eventId : string ,
318+ openSubTab : ( tab : WorkflowTabHierarchy ) => void ,
319+ } ) => {
320+ const { t } = useTranslation ( ) ;
321+ const dispatch = useAppDispatch ( ) ;
322+
323+ const workflowId = useAppSelector ( state => getModalWorkflowId ( state ) ) ;
324+ const operationsEntry = useAppSelector ( state => getLatestWorkflowOperation ( state ) ) ;
325+ const workflow = useAppSelector ( state => getWorkflow ( state ) ) ;
326+
327+ // Parse translation key to state
328+ let workflowDone = false ;
329+ if ( "status" in workflow ) {
330+ const workflowStatus = workflow . status . split ( "." ) . pop ( ) ;
331+ workflowDone = ! ( workflowStatus === "SUCCEEDED" || workflowStatus === "FAILED" || workflowStatus === "STOPPED" ) ;
332+ }
333+
334+ const loadWorkflowOperations = async ( ) => {
335+ // Fetching workflow operations from server
336+ if ( workflowId ) {
337+ dispatch ( fetchWorkflowOperations ( { eventId, workflowId } ) ) ;
338+ }
339+ } ;
340+
341+ useEffect ( ( ) => {
342+ // Fetch workflow operations initially
343+ loadWorkflowOperations ( ) . then ( ) ;
344+
345+ // Fetch workflow operations every 5 seconds
346+ const fetchWorkflowOperationsInterval = setInterval ( loadWorkflowOperations , 5000 ) ;
347+
348+ // Unmount interval
349+ return ( ) => clearInterval ( fetchWorkflowOperationsInterval ) ;
350+ // eslint-disable-next-line react-hooks/exhaustive-deps
351+ } , [ ] ) ;
352+
353+ const openDetailsSubTab = ( tabType : WorkflowTabHierarchy , operationId : number | undefined = undefined ) => {
354+ dispatch ( removeNotificationWizardForm ( ) ) ;
355+ dispatch ( setModalWorkflowTabHierarchy ( tabType ) ) ;
356+ if ( tabType === "workflow-operation-details" ) {
357+ dispatch ( fetchWorkflowOperationDetails ( { eventId, workflowId, operationId } ) ) . then ( ) ;
358+ }
359+ } ;
360+
361+ return (
362+ < div className = "obj tbl-container more-info-actions" >
363+ < header >
364+ { workflowDone
365+ ? t ( "EVENTS.EVENTS.DETAILS.WORKFLOW_DETAILS.CURRENT_OPERATION" )
366+ : t ( "EVENTS.EVENTS.DETAILS.WORKFLOW_DETAILS.OPERATIONS" )
367+ }
368+ </ header >
369+
370+ { workflowDone && < >
371+ < WorfklowOperationsTableBody
372+ operations = { operationsEntry
373+ ? [ { operation : operationsEntry . operation , operationId : operationsEntry . index } ]
374+ : [ ]
375+ }
376+ openSubTab = { openDetailsSubTab }
377+ />
378+ < hr style = { { height : "1px" , border : 0 , borderTop : "1px solid #ccc" , margin : "0" , padding : "0" } } />
379+ </ > }
380+
381+ { /* links to 'Operations' or 'Errors & Warnings' sub-Tabs */ }
382+ < div className = "obj-container" >
383+ < ul >
384+ < li >
385+ < span >
386+ { t ( "EVENTS.EVENTS.DETAILS.WORKFLOW_OPERATIONS.DETAILS_LINK" ) /* Operations */ }
387+ </ span >
388+ < ButtonLikeAnchor
389+ extraClassName = "details-link"
390+ onClick = { ( ) => openSubTab ( "workflow-operations" ) }
391+ >
392+ { t ( "EVENTS.EVENTS.DETAILS.WORKFLOWS.DETAILS" ) /* Details */ }
393+ </ ButtonLikeAnchor >
394+ </ li >
395+ </ ul >
396+ </ div >
397+ </ div >
398+ ) ;
399+ }
400+
359401export default EventDetailsWorkflowDetails ;
0 commit comments