Skip to content

Commit 4b51a05

Browse files
committed
Merge branch 'workflow-tab-rework' of Arnei/opencast-admin-interface into develop
Pull request #1362 Mini workflow tab rework
2 parents 8a52313 + dce4605 commit 4b51a05

11 files changed

Lines changed: 803 additions & 717 deletions

src/components/events/partials/ModalTabsAndPages/EventDetailsTabHierarchyNavigation.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const EventDetailsTabHierarchyNavigation = <T, >({
1919
subTabArgument1,
2020
translationKey2,
2121
subTabArgument2,
22+
translationKey3,
23+
subTabArgument3,
2224
}: {
2325
openSubTab: (tabType: T) => void,
2426
hierarchyDepth: number,
@@ -28,6 +30,8 @@ const EventDetailsTabHierarchyNavigation = <T, >({
2830
subTabArgument1?: T,
2931
translationKey2?: ParseKeys,
3032
subTabArgument2?: T,
33+
translationKey3?: ParseKeys,
34+
subTabArgument3?: T,
3135
}) => {
3236
const { t } = useTranslation();
3337

@@ -67,12 +71,25 @@ const EventDetailsTabHierarchyNavigation = <T, >({
6771
{hierarchyDepth > 1 && subTabArgument2 && (
6872
<ButtonLikeAnchor
6973
extraClassName="breadcrumb-link scope"
70-
style={styleNavHierarchy}
74+
style={
75+
hierarchyDepth === 2
76+
? styleNavHierarchy
77+
: styleNavHierarchyInactive
78+
}
7179
onClick={() => openSubTab(subTabArgument2)}
7280
>
7381
{translationKey2 && t(translationKey2)}
7482
</ButtonLikeAnchor>
7583
)}
84+
{hierarchyDepth > 2 && subTabArgument3 && (
85+
<ButtonLikeAnchor
86+
extraClassName="breadcrumb-link scope"
87+
style={styleNavHierarchy}
88+
onClick={() => openSubTab(subTabArgument3)}
89+
>
90+
{translationKey3 && t(translationKey3)}
91+
</ButtonLikeAnchor>
92+
)}
7693
</nav>
7794
);
7895
};

src/components/events/partials/ModalTabsAndPages/EventDetailsWorkflowDetails.tsx

Lines changed: 144 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useEffect } from "react";
22
import Notifications from "../../../shared/Notifications";
33
import {
4+
getLatestWorkflowOperation,
45
getModalWorkflowId,
56
getWorkflow,
67
isFetchingWorkflowDetails,
@@ -11,6 +12,10 @@ import { getUserInformation } from "../../../../selectors/userInfoSelectors";
1112
import { useAppDispatch, useAppSelector } from "../../../../store";
1213
import {
1314
fetchWorkflowDetails,
15+
fetchWorkflowOperationDetails,
16+
fetchWorkflowOperations,
17+
fetchWorkflows,
18+
setModalWorkflowId,
1419
setModalWorkflowTabHierarchy,
1520
} from "../../../../slices/eventDetailsSlice";
1621
import { removeNotificationWizardForm } from "../../../../slices/notificationSlice";
@@ -20,6 +25,8 @@ import { useTranslation } from "react-i18next";
2025
import ButtonLikeAnchor from "../../../shared/ButtonLikeAnchor";
2126
import { ParseKeys } from "i18next";
2227
import 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+
359401
export default EventDetailsWorkflowDetails;

0 commit comments

Comments
 (0)