@orchestrator-ui/orchestrator-ui-components 7.6.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +7 -7
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +9 -9
- package/CHANGELOG.md +18 -0
- package/dist/index.d.ts +63 -45
- package/dist/index.js +776 -647
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/WfoBadges/WfoEngineStatusBadge/WfoEngineStatusBadge.tsx +3 -1
- package/src/components/WfoBadges/WfoWebsocketStatusBadge/WfoWebsocketStatusBadge.tsx +9 -3
- package/src/components/WfoPydanticForm/fields/WfoArrayField/WfoArrayField.tsx +1 -1
- package/src/components/WfoPydanticForm/fields/WfoInteger.tsx +8 -6
- package/src/components/WfoPydanticForm/fields/WfoObjectField/WfoObjectField.tsx +1 -1
- package/src/components/WfoSubscription/WfoSubscriptionGeneralSections/WfoSubscriptionDetailSection.tsx +34 -3
- package/src/components/WfoTitleWithWebsocketBadge/WfoTitleWithWebsocketBadge.tsx +3 -2
- package/src/components/WfoWorkflowSteps/WfoStep/WfoStep.tsx +57 -40
- package/src/components/WfoWorkflowSteps/WfoStep/useStepDetailOverride.ts +9 -0
- package/src/components/WfoWorkflowSteps/WfoWorkflowStepList/WfoStepListHeader.tsx +0 -1
- package/src/configuration/version.ts +1 -1
- package/src/messages/en-GB.json +2 -0
- package/src/messages/nl-NL.json +2 -0
- package/src/pages/metadata/WfoScheduleTaskFormPage.tsx +153 -35
- package/src/rtk/endpoints/forms.ts +1 -1
- package/src/rtk/endpoints/metadata/scheduledTasks.ts +9 -17
- package/src/rtk/endpoints/streamMessages.ts +10 -23
- package/src/rtk/slices/orchestratorComponentOverride.ts +5 -1
- package/src/utils/compareVersions.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orchestrator-ui/orchestrator-ui-components",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Library of UI Components used to display the workflow orchestrator frontend",
|
|
6
6
|
"author": {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"next-query-params": "^5.0.0",
|
|
55
55
|
"object-hash": "^3.0.0",
|
|
56
56
|
"prism-themes": "^1.9.0",
|
|
57
|
-
"pydantic-forms": "^
|
|
57
|
+
"pydantic-forms": "^2.0.0",
|
|
58
58
|
"react-diff-view": "^3.2.0",
|
|
59
59
|
"react-draggable": "^4.4.6",
|
|
60
60
|
"react-redux": "^9.1.2",
|
|
@@ -13,10 +13,12 @@ export const WfoEngineStatusBadge = () => {
|
|
|
13
13
|
const { data } = useGetEngineStatusQuery();
|
|
14
14
|
const { engineStatus } = data || {};
|
|
15
15
|
const { useWebSockets } = useGetOrchestratorConfig();
|
|
16
|
+
|
|
17
|
+
const { orchestratorWebsocketUrl } = useGetOrchestratorConfig();
|
|
16
18
|
const [websocketTrigger, { isUninitialized }] = useLazyStreamMessagesQuery();
|
|
17
19
|
|
|
18
20
|
if (useWebSockets && isUninitialized) {
|
|
19
|
-
websocketTrigger();
|
|
21
|
+
websocketTrigger(orchestratorWebsocketUrl);
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
const engineStatusText: string = engineStatus ? `Engine is ${engineStatus}` : 'Engine status is unavailable';
|
|
@@ -6,7 +6,7 @@ import { useTranslations } from 'next-intl';
|
|
|
6
6
|
import { EuiToolTip } from '@elastic/eui';
|
|
7
7
|
|
|
8
8
|
import { WfoHeaderBadge } from '@/components';
|
|
9
|
-
import { useWithOrchestratorTheme } from '@/hooks';
|
|
9
|
+
import { useGetOrchestratorConfig, useWithOrchestratorTheme } from '@/hooks';
|
|
10
10
|
import { useOrchestratorTheme } from '@/hooks/useOrchestratorTheme';
|
|
11
11
|
import { WfoBoltFill, WfoBoltSlashFill } from '@/icons';
|
|
12
12
|
import { orchestratorApi } from '@/rtk';
|
|
@@ -15,16 +15,22 @@ import { useStreamMessagesQuery } from '@/rtk/endpoints/streamMessages';
|
|
|
15
15
|
import { getStyles } from './styles';
|
|
16
16
|
|
|
17
17
|
interface WfoWebsocketStatusBadgeProps {
|
|
18
|
+
wsUrl?: string;
|
|
18
19
|
hideWhenConnected?: boolean;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export const WfoWebsocketStatusBadge: FC<WfoWebsocketStatusBadgeProps> = ({
|
|
22
|
+
export const WfoWebsocketStatusBadge: FC<WfoWebsocketStatusBadgeProps> = ({
|
|
23
|
+
hideWhenConnected = false,
|
|
24
|
+
wsUrl = undefined,
|
|
25
|
+
}) => {
|
|
22
26
|
const dispatch = useDispatch();
|
|
23
27
|
const { connectedStyle, disconnectedStyle } = useWithOrchestratorTheme(getStyles);
|
|
24
28
|
|
|
25
29
|
const t = useTranslations('main');
|
|
26
30
|
const { theme } = useOrchestratorTheme();
|
|
27
|
-
|
|
31
|
+
|
|
32
|
+
const { orchestratorWebsocketUrl } = useGetOrchestratorConfig();
|
|
33
|
+
const { data: websocketConnected } = useStreamMessagesQuery(wsUrl || orchestratorWebsocketUrl);
|
|
28
34
|
|
|
29
35
|
const showBadge = !(websocketConnected && hideWhenConnected);
|
|
30
36
|
|
|
@@ -81,7 +81,7 @@ export const WfoArrayField = ({ pydanticFormField }: PydanticFormElementProps) =
|
|
|
81
81
|
|
|
82
82
|
if (!arrayItem) return null;
|
|
83
83
|
|
|
84
|
-
const component = fieldToComponentMatcher(arrayItem, config?.
|
|
84
|
+
const component = fieldToComponentMatcher(arrayItem, config?.componentMatcher);
|
|
85
85
|
|
|
86
86
|
const renderField = (field: Record<'id', string>, index: number) => {
|
|
87
87
|
const itemizedField = itemizeArrayItem(index, arrayItem, arrayName);
|
|
@@ -31,20 +31,22 @@ export const WfoInteger: PydanticFormControlledElement = ({ pydanticFormField, o
|
|
|
31
31
|
// this is imposed by react-hook-form. We try to detect this and extract the actual value
|
|
32
32
|
const fieldName = getFormFieldIdWithPath(pydanticFormField.id);
|
|
33
33
|
const fieldValue = _.isObject(value) && _.has(value, fieldName) ? _.get(value, fieldName) : value;
|
|
34
|
-
const [userInput, setUserInput] = React.useState<string>();
|
|
34
|
+
const [userInput, setUserInput] = React.useState<string>('');
|
|
35
35
|
|
|
36
36
|
useEffect(() => {
|
|
37
37
|
if (fieldValue !== undefined && fieldValue !== null) {
|
|
38
38
|
setUserInput(fieldValue);
|
|
39
|
-
} else {
|
|
40
|
-
setUserInput('');
|
|
41
39
|
}
|
|
42
40
|
}, [fieldValue, value]);
|
|
43
41
|
|
|
44
42
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
45
|
-
const newValue = event.target.value;
|
|
46
|
-
if (value === null
|
|
47
|
-
|
|
43
|
+
const newValue = event.target.value || '';
|
|
44
|
+
if (value === null || newValue !== value) {
|
|
45
|
+
if (!isNaN(parseInt(newValue))) {
|
|
46
|
+
onChange(parseInt(newValue));
|
|
47
|
+
} else {
|
|
48
|
+
onChange('');
|
|
49
|
+
}
|
|
48
50
|
}
|
|
49
51
|
setUserInput(newValue);
|
|
50
52
|
};
|
|
@@ -16,7 +16,7 @@ export const WfoObjectField = ({ pydanticFormField }: PydanticFormElementProps)
|
|
|
16
16
|
const config = useGetConfig();
|
|
17
17
|
const disabled = pydanticFormField.attributes?.disabled || false;
|
|
18
18
|
const { wfoObjectFieldStyles } = getWfoObjectFieldStyles();
|
|
19
|
-
const components = getPydanticFormComponents(pydanticFormField.properties || {}, config?.
|
|
19
|
+
const components = getPydanticFormComponents(pydanticFormField.properties || {}, config?.componentMatcher);
|
|
20
20
|
|
|
21
21
|
// We have decided - for now - on the convention that all descendants of disabled fields will be disabled as well
|
|
22
22
|
// so we will not displaying any interactive elements inside a disabled element
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
import { useTranslations } from 'next-intl';
|
|
4
|
+
import Link from 'next/link';
|
|
4
5
|
|
|
5
6
|
import {
|
|
7
|
+
PATH_TASKS,
|
|
6
8
|
SubscriptionKeyValueBlock,
|
|
7
9
|
WfoCustomerDescriptionsField,
|
|
8
10
|
WfoInSyncField,
|
|
9
11
|
WfoSubscriptionDetailNoteEdit,
|
|
10
12
|
WfoSubscriptionStatusBadge,
|
|
11
13
|
} from '@/components';
|
|
12
|
-
import { SubscriptionDetail } from '@/types';
|
|
14
|
+
import { SubscriptionDetail, WorkflowTarget } from '@/types';
|
|
13
15
|
import { formatDate } from '@/utils';
|
|
14
16
|
|
|
15
17
|
interface WfoSubscriptionDetailSectionProps {
|
|
@@ -19,8 +21,17 @@ interface WfoSubscriptionDetailSectionProps {
|
|
|
19
21
|
export const WfoSubscriptionDetailSection = ({ subscriptionDetail }: WfoSubscriptionDetailSectionProps) => {
|
|
20
22
|
const t = useTranslations('subscriptions.detail');
|
|
21
23
|
|
|
22
|
-
const {
|
|
23
|
-
|
|
24
|
+
const {
|
|
25
|
+
subscriptionId,
|
|
26
|
+
product,
|
|
27
|
+
description,
|
|
28
|
+
startDate,
|
|
29
|
+
endDate,
|
|
30
|
+
status,
|
|
31
|
+
customer,
|
|
32
|
+
customerDescriptions,
|
|
33
|
+
processes,
|
|
34
|
+
} = subscriptionDetail;
|
|
24
35
|
|
|
25
36
|
const subscriptionDetailBlockData = [
|
|
26
37
|
{
|
|
@@ -52,6 +63,26 @@ export const WfoSubscriptionDetailSection = ({ subscriptionDetail }: WfoSubscrip
|
|
|
52
63
|
key: t('insync'),
|
|
53
64
|
value: <WfoInSyncField subscriptionDetail={subscriptionDetail} />,
|
|
54
65
|
},
|
|
66
|
+
{
|
|
67
|
+
key: t('lastRunValidation'),
|
|
68
|
+
value: (() => {
|
|
69
|
+
const lastValidate = processes?.page
|
|
70
|
+
?.filter((process) => process.workflowTarget.toLowerCase() === WorkflowTarget.VALIDATE.toLowerCase())
|
|
71
|
+
.slice(-1)[0];
|
|
72
|
+
|
|
73
|
+
if (!lastValidate) {
|
|
74
|
+
return t('noValidateWorkflows');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const processUrl = `${PATH_TASKS}/${lastValidate.processId}`;
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Link href={processUrl}>
|
|
81
|
+
{lastValidate.lastStatus} ({formatDate(lastValidate.startedAt)})
|
|
82
|
+
</Link>
|
|
83
|
+
);
|
|
84
|
+
})(),
|
|
85
|
+
},
|
|
55
86
|
{
|
|
56
87
|
key: t('customer'),
|
|
57
88
|
value: subscriptionDetail && subscriptionDetail.customer ? `${customer?.fullname}` : '-',
|
|
@@ -7,15 +7,16 @@ import { useGetOrchestratorConfig } from '@/hooks';
|
|
|
7
7
|
|
|
8
8
|
interface WfoTitleWithWebsocketBadgeProps {
|
|
9
9
|
title: string;
|
|
10
|
+
wsUrl?: string;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export const WfoTitleWithWebsocketBadge = ({ title }: WfoTitleWithWebsocketBadgeProps) => {
|
|
13
|
+
export const WfoTitleWithWebsocketBadge = ({ title, wsUrl = undefined }: WfoTitleWithWebsocketBadgeProps) => {
|
|
13
14
|
const { useWebSockets } = useGetOrchestratorConfig();
|
|
14
15
|
|
|
15
16
|
const pageTitle =
|
|
16
17
|
useWebSockets ?
|
|
17
18
|
<>
|
|
18
|
-
{title} <WfoWebsocketStatusBadge />
|
|
19
|
+
{title} <WfoWebsocketStatusBadge wsUrl={wsUrl} />
|
|
19
20
|
</>
|
|
20
21
|
: title;
|
|
21
22
|
|
|
@@ -17,6 +17,7 @@ import { getStepContent } from '../stepListUtils';
|
|
|
17
17
|
import { getWorkflowStepsStyles } from '../styles';
|
|
18
18
|
import { CodeView, WfoCodeViewSelector } from './WfoCodeViewSelector';
|
|
19
19
|
import { WfoStepForm } from './WfoStepForm';
|
|
20
|
+
import { useStepDetailOverride } from './useStepDetailOverride';
|
|
20
21
|
|
|
21
22
|
export interface WfoStepProps {
|
|
22
23
|
stepListItem: StepListItem;
|
|
@@ -58,6 +59,7 @@ export const WfoStep = React.forwardRef(
|
|
|
58
59
|
stepRowStyle,
|
|
59
60
|
getStepToggleExpandStyle,
|
|
60
61
|
} = useWithOrchestratorTheme(getWorkflowStepsStyles);
|
|
62
|
+
const { overrideStepDetail } = useStepDetailOverride();
|
|
61
63
|
const t = useTranslations('processes.steps');
|
|
62
64
|
const hasHtmlMail = Object.prototype.hasOwnProperty.call(step?.stateDelta || {}, 'confirmation_mail');
|
|
63
65
|
|
|
@@ -104,55 +106,70 @@ export const WfoStep = React.forwardRef(
|
|
|
104
106
|
},
|
|
105
107
|
[setCodeView],
|
|
106
108
|
);
|
|
107
|
-
|
|
109
|
+
const shouldExpand: boolean = isExpanded && hasStepContent;
|
|
108
110
|
return (
|
|
109
111
|
<div ref={ref}>
|
|
110
112
|
<EuiPanel>
|
|
111
113
|
<EuiFlexGroup css={getStepHeaderStyle(hasStepContent)} onClick={() => hasStepContent && onToggleStepDetail()}>
|
|
112
114
|
<WfoStepStatusIcon stepStatus={step.status} isStartStep={isStartStep} />
|
|
113
115
|
|
|
114
|
-
<
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
<EuiFlexGroup direction="column" gutterSize="none">
|
|
117
|
+
<EuiFlexItem css={{ flexDirection: 'row' }}>
|
|
118
|
+
<EuiFlexItem grow={0}>
|
|
119
|
+
<EuiText css={stepListContentBoldTextStyle}>{step.name}</EuiText>
|
|
120
|
+
<EuiText>
|
|
121
|
+
{step.status} {step.completed && `- ${formatDate(step.completed)}`}
|
|
122
|
+
</EuiText>
|
|
123
|
+
</EuiFlexItem>
|
|
120
124
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
125
|
+
<EuiFlexGroup css={stepRowStyle}>
|
|
126
|
+
{step.completed && (
|
|
127
|
+
<>
|
|
128
|
+
{isExpanded && <WfoCodeViewSelector codeView={codeView} handleCodeViewChange={handle} />}
|
|
129
|
+
<EuiFlexItem grow={0} css={stepHeaderRightStyle}>
|
|
130
|
+
<EuiText css={stepDurationStyle}>{t('duration')}</EuiText>
|
|
131
|
+
<EuiText size="m">{calculateTimeDifference(startedAt, completedAt)}</EuiText>
|
|
132
|
+
</EuiFlexItem>
|
|
133
|
+
<EuiFlexItem grow={0} css={getStepToggleExpandStyle(hasStepContent)}>
|
|
134
|
+
{(isExpanded && <WfoChevronUp color={theme.colors.textParagraph} />) || (
|
|
135
|
+
<WfoChevronDown color={theme.colors.textParagraph} />
|
|
136
|
+
)}
|
|
137
|
+
</EuiFlexItem>
|
|
138
|
+
</>
|
|
139
|
+
)}
|
|
140
|
+
</EuiFlexGroup>
|
|
141
|
+
</EuiFlexItem>
|
|
142
|
+
<EuiFlexItem>
|
|
143
|
+
{overrideStepDetail?.stepHeader && <overrideStepDetail.stepHeader step={step} />}
|
|
144
|
+
</EuiFlexItem>
|
|
136
145
|
</EuiFlexGroup>
|
|
137
146
|
</EuiFlexGroup>
|
|
138
|
-
{
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
147
|
+
{shouldExpand && (
|
|
148
|
+
<EuiFlexGroup direction="column" gutterSize="none">
|
|
149
|
+
<EuiFlexItem grow={1}>
|
|
150
|
+
{overrideStepDetail?.stepBody && <overrideStepDetail.stepBody step={step} />}
|
|
151
|
+
</EuiFlexItem>
|
|
152
|
+
<EuiFlexItem>
|
|
153
|
+
{shouldExpand
|
|
154
|
+
&& !hasHtmlMail
|
|
155
|
+
&& (codeView === CodeView.TABLE ? <WfoTableCodeBlock stepState={stepContent} />
|
|
156
|
+
: codeView === CodeView.RAW ? <WfoJsonCodeBlock data={stepContent} />
|
|
157
|
+
: <WfoMonacoCodeBlock data={stepContent} />)}
|
|
158
|
+
{isExpanded && hasHtmlMail && (
|
|
159
|
+
<div css={stepEmailContainerStyle}>
|
|
160
|
+
{displayMailConfirmation(step.stateDelta.confirmation_mail as EmailState)}
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
{step.status === StepStatus.SUSPEND && userInputForm && (
|
|
164
|
+
<WfoStepForm
|
|
165
|
+
userInputForm={userInputForm}
|
|
166
|
+
isTask={isTask}
|
|
167
|
+
processId={processId ?? ''}
|
|
168
|
+
userPermissions={userPermissions}
|
|
169
|
+
/>
|
|
170
|
+
)}
|
|
171
|
+
</EuiFlexItem>
|
|
172
|
+
</EuiFlexGroup>
|
|
156
173
|
)}
|
|
157
174
|
</EuiPanel>
|
|
158
175
|
</div>
|
|
@@ -69,7 +69,6 @@ export const WfoStepListHeader: FC<WfoStepListHeaderProps> = ({
|
|
|
69
69
|
<EuiText css={stepListContentBoldTextStyle}>{t(isTask ? 'taskSteps' : 'workflowSteps')}</EuiText>
|
|
70
70
|
{!showRaw && <WfoTextAnchor text={allDetailToggleText} onClick={onToggleAllDetailsIsOpen} />}
|
|
71
71
|
</EuiFlexGroup>
|
|
72
|
-
|
|
73
72
|
{/* Right side: view options */}
|
|
74
73
|
<EuiFlexGroup justifyContent="flexEnd" direction="row" css={stepListOptionsContainerStyle} gutterSize="s">
|
|
75
74
|
{showTracebackButton && (
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const ORCHESTRATOR_UI_LIBRARY_VERSION = '
|
|
1
|
+
export const ORCHESTRATOR_UI_LIBRARY_VERSION = '8.0.0';
|
package/src/messages/en-GB.json
CHANGED
|
@@ -390,6 +390,8 @@
|
|
|
390
390
|
"subscriptionId": "Subscription ID",
|
|
391
391
|
"description": "Description",
|
|
392
392
|
"startDate": "Start date",
|
|
393
|
+
"lastRunValidation": "Most recent validate",
|
|
394
|
+
"noValidateWorkflows": "No validate workflows have been run recently",
|
|
393
395
|
"insync": "In sync",
|
|
394
396
|
"customer": "Customer",
|
|
395
397
|
"customerUuid": "Customer UUID",
|
package/src/messages/nl-NL.json
CHANGED
|
@@ -389,6 +389,8 @@
|
|
|
389
389
|
"subscriptionId": "Subscription ID",
|
|
390
390
|
"description": "Beschrijving",
|
|
391
391
|
"startDate": "Startdatum",
|
|
392
|
+
"lastRunValidation": "Meest recente validate",
|
|
393
|
+
"noValidateWorkflows": "Er zijn recentelijk geen validate workflows uitgevoerd",
|
|
392
394
|
"insync": "In sync",
|
|
393
395
|
"customer": "Klant",
|
|
394
396
|
"customerUuid": "Klant UUID",
|
|
@@ -1,27 +1,33 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useCallback, useMemo } from 'react';
|
|
2
2
|
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
import { useTranslations } from 'next-intl';
|
|
5
5
|
import { useRouter } from 'next/router';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from '
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
PydanticFormDefinitionResponse,
|
|
15
|
-
PydanticFormSuccessResponse,
|
|
16
|
-
RawJsonProperties,
|
|
17
|
-
} from 'pydantic-forms';
|
|
18
|
-
|
|
19
|
-
import { Footer, PATH_METADATA_SCHEDULED_TASKS, WfoContentHeader, WfoLoading } from '@/components';
|
|
6
|
+
import { PydanticForm } from 'pydantic-forms';
|
|
7
|
+
import type { PydanticFormApiProvider } from 'pydantic-forms';
|
|
8
|
+
import { PydanticFormApiResponseType, PydanticFormFieldFormat, PydanticFormFieldType } from 'pydantic-forms';
|
|
9
|
+
import type { PydanticFormDefinitionResponse, PydanticFormSuccessResponse, RawJsonProperties } from 'pydantic-forms';
|
|
10
|
+
|
|
11
|
+
import { PATH_METADATA_SCHEDULED_TASKS } from '@/components';
|
|
12
|
+
import { WfoContentHeader, WfoLoading } from '@/components';
|
|
13
|
+
import { Footer } from '@/components/WfoPydanticForm/Footer';
|
|
20
14
|
import { NUMBER_OF_ITEMS_REPRESENTING_ALL_ITEMS } from '@/configuration';
|
|
21
|
-
import {
|
|
22
|
-
import
|
|
23
|
-
import {
|
|
24
|
-
|
|
15
|
+
import { useShowToastMessage } from '@/hooks';
|
|
16
|
+
import { useGetPydanticFormsConfig } from '@/hooks/useGetPydanticFormsConfig';
|
|
17
|
+
import {
|
|
18
|
+
HttpStatus,
|
|
19
|
+
ScheduledTaskPostPayload,
|
|
20
|
+
isFetchBaseQueryError,
|
|
21
|
+
isRecord,
|
|
22
|
+
useCreateScheduledTaskMutation,
|
|
23
|
+
} from '@/rtk';
|
|
24
|
+
import type { CronKwargs } from '@/rtk';
|
|
25
|
+
import { useGetTasksQuery } from '@/rtk';
|
|
26
|
+
import { useStartFormMutation } from '@/rtk/endpoints/forms';
|
|
27
|
+
import { useGetVersionsQuery } from '@/rtk/endpoints/versions';
|
|
28
|
+
import { ToastTypes } from '@/types';
|
|
29
|
+
import { Intervals, TaskDefinition, TaskType } from '@/types';
|
|
30
|
+
import { compareVersions } from '@/utils/compareVersions';
|
|
25
31
|
|
|
26
32
|
type CreateScheduleFormStep1 = {
|
|
27
33
|
workflowId: TaskDefinition['workflowId'];
|
|
@@ -50,7 +56,8 @@ type CreateScheduleFormStep2 =
|
|
|
50
56
|
|
|
51
57
|
type CreateScheduleFormInput = [CreateScheduleFormStep1, CreateScheduleFormStep2];
|
|
52
58
|
|
|
53
|
-
|
|
59
|
+
// TODO: remove after 5.0.0 releases and rename WfoScheduleTaskFormPageBackend to WfoScheduleTaskFormPage
|
|
60
|
+
export const WfoScheduleTaskFormPageHardCoded = () => {
|
|
54
61
|
const t = useTranslations('metadata.scheduleTaskForm');
|
|
55
62
|
const { showToastMessage } = useShowToastMessage();
|
|
56
63
|
|
|
@@ -256,13 +263,15 @@ export const WfoScheduleTaskFormPage = () => {
|
|
|
256
263
|
|
|
257
264
|
if (userInputStep1.taskType === TaskType.DATE) {
|
|
258
265
|
return {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
266
|
+
scheduled_type: 'create',
|
|
267
|
+
workflow_id: task.workflowId,
|
|
268
|
+
workflow_name: task.name,
|
|
269
|
+
name: task.description,
|
|
270
|
+
trigger: userInputStep1.taskType,
|
|
271
|
+
trigger_kwargs: {
|
|
264
272
|
run_date: startDate,
|
|
265
273
|
},
|
|
274
|
+
user_inputs: [],
|
|
266
275
|
};
|
|
267
276
|
} else if (userInputStep1.taskType === TaskType.INTERVAL) {
|
|
268
277
|
const step2Input = userInputStep2 as CreateScheduleFormStep2Interval;
|
|
@@ -273,24 +282,28 @@ export const WfoScheduleTaskFormPage = () => {
|
|
|
273
282
|
}
|
|
274
283
|
|
|
275
284
|
return {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
285
|
+
scheduled_type: 'create',
|
|
286
|
+
workflow_id: task.workflowId,
|
|
287
|
+
workflow_name: task.name,
|
|
288
|
+
name: task.description,
|
|
289
|
+
trigger: userInputStep1.taskType,
|
|
290
|
+
trigger_kwargs: {
|
|
281
291
|
start_date: startDate,
|
|
282
292
|
...intervalArg,
|
|
283
293
|
},
|
|
294
|
+
user_inputs: [],
|
|
284
295
|
};
|
|
285
296
|
} else if (userInputStep1.taskType === TaskType.CRON) {
|
|
286
297
|
const step2Input = userInputStep2 as CreateScheduleFormStep2Cron;
|
|
287
298
|
// minute hour day month weekday
|
|
288
299
|
return {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
300
|
+
scheduled_type: 'create',
|
|
301
|
+
workflow_id: task.workflowId,
|
|
302
|
+
workflow_name: task.name,
|
|
303
|
+
name: task.description,
|
|
304
|
+
trigger: userInputStep1.taskType,
|
|
305
|
+
trigger_kwargs: getCronKwargs(step2Input.cron, startDate),
|
|
306
|
+
user_inputs: [],
|
|
294
307
|
};
|
|
295
308
|
}
|
|
296
309
|
throw new Error('Unknown or missing task type');
|
|
@@ -420,3 +433,108 @@ export const WfoScheduleTaskFormPage = () => {
|
|
|
420
433
|
</>
|
|
421
434
|
);
|
|
422
435
|
};
|
|
436
|
+
|
|
437
|
+
const START_SCHEDULE_PAYLOAD = {};
|
|
438
|
+
|
|
439
|
+
export const WfoScheduleTaskFormPageBackend = () => {
|
|
440
|
+
const { showToastMessage } = useShowToastMessage();
|
|
441
|
+
|
|
442
|
+
const generateFormId = useMemo(() => {
|
|
443
|
+
return `${JSON.stringify(START_SCHEDULE_PAYLOAD)}`;
|
|
444
|
+
}, []);
|
|
445
|
+
|
|
446
|
+
const [startForm] = useStartFormMutation();
|
|
447
|
+
const [createScheduledTask, mutationState] = useCreateScheduledTaskMutation();
|
|
448
|
+
const router = useRouter();
|
|
449
|
+
|
|
450
|
+
const onSuccess = useCallback(
|
|
451
|
+
async (_fieldValues: object, req: object) => {
|
|
452
|
+
const request = req as {
|
|
453
|
+
status: HttpStatus;
|
|
454
|
+
data: ScheduledTaskPostPayload;
|
|
455
|
+
};
|
|
456
|
+
if (request?.data?.workflow_id) {
|
|
457
|
+
const resp = await createScheduledTask(request.data);
|
|
458
|
+
if (!resp?.error) {
|
|
459
|
+
router.replace(PATH_METADATA_SCHEDULED_TASKS);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
[router, createScheduledTask],
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
const getPydanticFormProvider = useCallback(() => {
|
|
467
|
+
const pydanticFormProvider: PydanticFormApiProvider = async ({ requestBody = [], formKey }) => {
|
|
468
|
+
const userInputs =
|
|
469
|
+
_.isEmpty(START_SCHEDULE_PAYLOAD) ? [...requestBody] : [{ ...START_SCHEDULE_PAYLOAD }, ...requestBody];
|
|
470
|
+
|
|
471
|
+
const response = startForm({
|
|
472
|
+
formKey,
|
|
473
|
+
userInputs,
|
|
474
|
+
});
|
|
475
|
+
return response
|
|
476
|
+
.then(({ error, data }) => {
|
|
477
|
+
return new Promise<Record<string, unknown>>((resolve) => {
|
|
478
|
+
if (isFetchBaseQueryError(error) && isRecord(error.data)) {
|
|
479
|
+
if (error.status === HttpStatus.FormNotComplete) {
|
|
480
|
+
resolve(error.data);
|
|
481
|
+
} else if (error.status === HttpStatus.BadRequest) {
|
|
482
|
+
resolve({
|
|
483
|
+
...error.data,
|
|
484
|
+
status: error.status,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
} else if (data) {
|
|
488
|
+
resolve({
|
|
489
|
+
data,
|
|
490
|
+
status: HttpStatus.Created,
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
resolve({});
|
|
495
|
+
});
|
|
496
|
+
})
|
|
497
|
+
.catch((error) => {
|
|
498
|
+
return new Promise<Record<string, object>>((resolve, reject) => {
|
|
499
|
+
if (error.status === HttpStatus.FormNotComplete) {
|
|
500
|
+
resolve(error.data);
|
|
501
|
+
}
|
|
502
|
+
reject(error);
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
return pydanticFormProvider;
|
|
508
|
+
}, [startForm]);
|
|
509
|
+
|
|
510
|
+
const config = useGetPydanticFormsConfig(getPydanticFormProvider, (props) => <Footer {...props} />);
|
|
511
|
+
|
|
512
|
+
const handleCancel = useCallback(() => {
|
|
513
|
+
const pfBasePath = PATH_METADATA_SCHEDULED_TASKS;
|
|
514
|
+
router.replace(pfBasePath);
|
|
515
|
+
}, [router]);
|
|
516
|
+
|
|
517
|
+
if (mutationState.isError) {
|
|
518
|
+
showToastMessage(ToastTypes.ERROR, '', 'Error while saving scheduled task');
|
|
519
|
+
console.error('Error saving scheduled task', mutationState);
|
|
520
|
+
return undefined;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return (
|
|
524
|
+
<PydanticForm
|
|
525
|
+
formKey="configure_schedule"
|
|
526
|
+
formId={generateFormId}
|
|
527
|
+
onSuccess={onSuccess}
|
|
528
|
+
onCancel={handleCancel}
|
|
529
|
+
config={config}
|
|
530
|
+
/>
|
|
531
|
+
);
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
export const WfoScheduleTaskFormPage = () => {
|
|
535
|
+
const { data } = useGetVersionsQuery();
|
|
536
|
+
const coreVersion = data?.version.applicationVersions[0].split(' ')[1] ?? '';
|
|
537
|
+
|
|
538
|
+
const isCompatible = compareVersions(coreVersion, '5.0.0a7') !== -1;
|
|
539
|
+
return isCompatible ? <WfoScheduleTaskFormPageBackend /> : <WfoScheduleTaskFormPageHardCoded />;
|
|
540
|
+
};
|
|
@@ -2,7 +2,7 @@ import { BaseQueryTypes, orchestratorApi } from '@/rtk';
|
|
|
2
2
|
|
|
3
3
|
const PROCESS_ENDPOINT = 'processes';
|
|
4
4
|
const RESUME_ENDPOINT = 'resume';
|
|
5
|
-
const FORMS_ENDPOINT = '
|
|
5
|
+
const FORMS_ENDPOINT = 'forms/'; // It is still being used by example-wfo-ui
|
|
6
6
|
|
|
7
7
|
const formsApi = orchestratorApi.injectEndpoints({
|
|
8
8
|
endpoints: (build) => ({
|