Skip to content

Commit abccef4

Browse files
committed
Display latest wf operation in wf details tab
Instead of just presenting a link to the "All operations" subtab, this adds a display for the latest running task. The intention here is to give users a quick impression on the state of the workflow without having to access the wf operations subtab.
1 parent 6b82626 commit abccef4

4 files changed

Lines changed: 174 additions & 90 deletions

File tree

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

Lines changed: 105 additions & 66 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,8 @@ import { getUserInformation } from "../../../../selectors/userInfoSelectors";
1112
import { useAppDispatch, useAppSelector } from "../../../../store";
1213
import {
1314
fetchWorkflowDetails,
15+
fetchWorkflowOperationDetails,
16+
fetchWorkflowOperations,
1417
setModalWorkflowTabHierarchy,
1518
} from "../../../../slices/eventDetailsSlice";
1619
import { removeNotificationWizardForm } from "../../../../slices/notificationSlice";
@@ -21,6 +24,7 @@ import ButtonLikeAnchor from "../../../shared/ButtonLikeAnchor";
2124
import { ParseKeys } from "i18next";
2225
import ModalContentTable from "../../../shared/modals/ModalContentTable";
2326
import EventDetailsWorkflowErrors from "./EventDetailsWorkflowErrors";
27+
import { Operation } from "./EventDetailsWorkflowOperations";
2428

2529
/**
2630
* This component manages the workflow details for the workflows tab of the event details modal
@@ -68,6 +72,8 @@ const EventDetailsWorkflowDetails = ({
6872
{/* Notifications */}
6973
<Notifications context="not_corner" />
7074

75+
<OperationsPreview eventId={eventId} openSubTab={openSubTab}/>
76+
7177
<EventDetailsWorkflowErrors eventId={eventId} />
7278

7379
{/* the contained view is only displayed, if the data has been fetched */}
@@ -209,41 +215,6 @@ const EventDetailsWorkflowDetails = ({
209215
</div>
210216
)}
211217

212-
{/* 'More Information' table */}
213-
<div className="obj tbl-container more-info-actions">
214-
<header>
215-
{
216-
t(
217-
"EVENTS.EVENTS.DETAILS.WORKFLOWS.MORE_INFO",
218-
) /* More Information */
219-
}
220-
</header>
221-
222-
{/* links to 'Operations' or 'Errors & Warnings' sub-Tabs */}
223-
<div className="obj-container">
224-
<ul>
225-
<li>
226-
<span>
227-
{
228-
t(
229-
"EVENTS.EVENTS.DETAILS.WORKFLOW_OPERATIONS.DETAILS_LINK",
230-
) /* Operations */
231-
}
232-
</span>
233-
<ButtonLikeAnchor
234-
extraClassName="details-link"
235-
onClick={() => openSubTab("workflow-operations")}
236-
>
237-
{
238-
t(
239-
"EVENTS.EVENTS.DETAILS.WORKFLOWS.DETAILS",
240-
) /* Details */
241-
}
242-
</ButtonLikeAnchor>
243-
</li>
244-
</ul>
245-
</div>
246-
</div>
247218
</>
248219
)}
249220

@@ -287,41 +258,109 @@ const EventDetailsWorkflowDetails = ({
287258
</div>
288259
</div>
289260
)}
290-
291-
{/* 'More Information' table */}
292-
<div className="obj tbl-container more-info-actions">
293-
<header>
294-
{
295-
t(
296-
"EVENTS.EVENTS.DETAILS.WORKFLOWS.MORE_INFO",
297-
) /* More Information */
298-
}
299-
</header>
300-
<div className="obj-container">
301-
<ul>
302-
<li>
303-
<span>
304-
{
305-
t(
306-
"EVENTS.EVENTS.DETAILS.WORKFLOW_OPERATIONS.DETAILS_LINK",
307-
) /* Operations */
308-
}
309-
</span>
310-
<ButtonLikeAnchor extraClassName="details-link">
311-
{
312-
t(
313-
"EVENTS.EVENTS.DETAILS.WORKFLOWS.DETAILS",
314-
) /* Details */
315-
}
316-
</ButtonLikeAnchor>
317-
</li>
318-
</ul>
319-
</div>
320-
</div>
321261
</>
322262
)}
323263
</ModalContentTable>
324264
);
325265
};
326266

267+
const OperationsPreview = ({
268+
eventId,
269+
openSubTab,
270+
}: {
271+
eventId: string,
272+
openSubTab: (tab: WorkflowTabHierarchy) => void,
273+
}) => {
274+
const { t } = useTranslation();
275+
const dispatch = useAppDispatch();
276+
277+
const workflowId = useAppSelector(state => getModalWorkflowId(state));
278+
const operationsEntry = useAppSelector(state => getLatestWorkflowOperation(state));
279+
280+
const loadWorkflowOperations = async () => {
281+
// Fetching workflow operations from server
282+
dispatch(fetchWorkflowOperations({ eventId, workflowId }));
283+
};
284+
285+
useEffect(() => {
286+
// Fetch workflow operations initially
287+
loadWorkflowOperations().then();
288+
289+
// Fetch workflow operations every 5 seconds
290+
const fetchWorkflowOperationsInterval = setInterval(loadWorkflowOperations, 5000);
291+
292+
// Unmount interval
293+
return () => clearInterval(fetchWorkflowOperationsInterval);
294+
// eslint-disable-next-line react-hooks/exhaustive-deps
295+
}, []);
296+
297+
const openDetailsSubTab = (tabType: WorkflowTabHierarchy, operationId: number | undefined = undefined) => {
298+
dispatch(removeNotificationWizardForm());
299+
dispatch(setModalWorkflowTabHierarchy(tabType));
300+
if (tabType === "workflow-operation-details") {
301+
dispatch(fetchWorkflowOperationDetails({ eventId, workflowId, operationId })).then();
302+
}
303+
};
304+
305+
return (
306+
<div className="obj tbl-container more-info-actions">
307+
<header>
308+
{t("EVENTS.EVENTS.DETAILS.WORKFLOW_DETAILS.LATEST_OPERATION")}
309+
</header>
310+
311+
<table className="main-tbl">
312+
<thead>
313+
<tr>
314+
<th>
315+
{t("EVENTS.EVENTS.DETAILS.WORKFLOW_OPERATIONS.TABLE_HEADERS.STATUS") /* Status */}
316+
</th>
317+
<th>
318+
{t("EVENTS.EVENTS.DETAILS.WORKFLOW_OPERATIONS.TABLE_HEADERS.TITLE") /* Title */}
319+
</th>
320+
<th>
321+
{t("EVENTS.EVENTS.DETAILS.WORKFLOW_OPERATIONS.TABLE_HEADERS.DESCRIPTION") /* Description */}
322+
</th>
323+
<th className="medium" />
324+
</tr>
325+
</thead>
326+
<tbody>
327+
{ operationsEntry &&
328+
<Operation
329+
operationId={operationsEntry.index}
330+
item={operationsEntry.operation}
331+
openSubTab={openDetailsSubTab}
332+
/>
333+
}
334+
</tbody>
335+
</table>
336+
<hr style={{ height: "1px", border: 0, borderTop: "1px solid #ccc", margin: "0", padding: "0"}} />
337+
338+
{/* links to 'Operations' or 'Errors & Warnings' sub-Tabs */}
339+
<div className="obj-container">
340+
<ul>
341+
<li>
342+
<span>
343+
{
344+
t(
345+
"EVENTS.EVENTS.DETAILS.WORKFLOW_OPERATIONS.DETAILS_LINK",
346+
) /* Operations */
347+
}
348+
</span>
349+
<ButtonLikeAnchor
350+
extraClassName="details-link"
351+
onClick={() => openSubTab("workflow-operations")}
352+
>
353+
{
354+
t(
355+
"EVENTS.EVENTS.DETAILS.WORKFLOWS.DETAILS",
356+
) /* Details */
357+
}
358+
</ButtonLikeAnchor>
359+
</li>
360+
</ul>
361+
</div>
362+
</div>
363+
);
364+
}
365+
327366
export default EventDetailsWorkflowDetails;

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

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -111,27 +111,12 @@ const EventDetailsWorkflowOperations = ({
111111
<tbody>
112112
{/* workflow operation details */}
113113
{operations.entries.map((item, key) => (
114-
<tr key={key}>
115-
<td>{t(item.status as ParseKeys)}</td>
116-
<td>{item.title}</td>
117-
<td>{item.description}</td>
118-
119-
{/* link to 'Operation Details' sub-Tab */}
120-
<td>
121-
<ButtonLikeAnchor
122-
extraClassName="details-link"
123-
onClick={() =>
124-
openSubTab("workflow-operation-details", key)
125-
}
126-
>
127-
{
128-
t(
129-
"EVENTS.EVENTS.DETAILS.MEDIA.DETAILS",
130-
) /* Details */
131-
}
132-
</ButtonLikeAnchor>
133-
</td>
134-
</tr>
114+
<Operation
115+
key={key}
116+
operationId={key}
117+
item={item}
118+
openSubTab={openSubTab}
119+
/>
135120
))}
136121
</tbody>
137122
</table>
@@ -141,4 +126,48 @@ const EventDetailsWorkflowOperations = ({
141126
);
142127
};
143128

129+
export const Operation = ({
130+
operationId,
131+
item,
132+
openSubTab,
133+
}: {
134+
operationId: number,
135+
item: {
136+
configuration: {
137+
[key: string]: string;
138+
};
139+
description: string;
140+
id: number;
141+
status: string;
142+
title: string;
143+
},
144+
openSubTab: (tab: WorkflowTabHierarchy, operationId: number | undefined) => void,
145+
}) => {
146+
const { t } = useTranslation();
147+
148+
return (
149+
<tr>
150+
<td>{t(item.status as ParseKeys)}</td>
151+
<td>{item.title}</td>
152+
<td>{item.description}</td>
153+
154+
{/* link to 'Operation Details' sub-Tab */}
155+
<td>
156+
<ButtonLikeAnchor
157+
extraClassName="details-link"
158+
onClick={() =>
159+
openSubTab("workflow-operation-details", operationId)
160+
}
161+
>
162+
{
163+
t(
164+
"EVENTS.EVENTS.DETAILS.MEDIA.DETAILS",
165+
) /* Details */
166+
}
167+
</ButtonLikeAnchor>
168+
</td>
169+
</tr>
170+
);
171+
};
172+
144173
export default EventDetailsWorkflowOperations;

src/i18n/org/opencastproject/adminui/languages/lang-en_US.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,6 @@
991991
"ACTIONS": "Actions",
992992
"DETAILS": "Details",
993993
"DESCRIPTION": "Description",
994-
"MORE_INFO": "More Information",
995994
"ID": "ID",
996995
"TYPE": "Type",
997996
"TITLE": "Title",
@@ -1031,11 +1030,12 @@
10311030
},
10321031
"WORKFLOW_DETAILS": {
10331032
"TITLE": "Workflow details",
1034-
"CONFIGURATION": "Workflow configuration"
1033+
"CONFIGURATION": "Workflow configuration",
1034+
"LATEST_OPERATION": "Latest workflow operation"
10351035
},
10361036
"WORKFLOW_OPERATIONS": {
10371037
"TITLE": "Workflow operations",
1038-
"DETAILS_LINK": "Operations",
1038+
"DETAILS_LINK": "All operations",
10391039
"TABLE_HEADERS": {
10401040
"TITLE": "Title",
10411041
"STATUS": "Status",

src/selectors/eventDetailsSelectors.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,22 @@ export const deletingWorkflow = (state: RootState) =>
115115
state.eventDetails.statusDeleteWorkflow === "loading";
116116
export const getWorkflowOperations = (state: RootState) =>
117117
state.eventDetails.workflowOperations;
118+
// Get the workflow operation currently running (or the last one that was run)
119+
export const getLatestWorkflowOperation = createSelector(
120+
[getWorkflowOperations],
121+
operations => {
122+
const entries = operations.entries;
123+
124+
for (let i = entries.length - 1; i >= 0; i--) {
125+
const operation = entries[i];
126+
if (operation.id !== null) {
127+
return { operation, index: i };
128+
}
129+
}
130+
131+
return null; // if none found
132+
},
133+
);
118134
export const isFetchingWorkflowOperations = (state: RootState) =>
119135
state.eventDetails.statusWorkflowOperations === "loading";
120136
export const getWorkflowOperationDetails = (state: RootState) =>

0 commit comments

Comments
 (0)