@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.
Files changed (27) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +9 -9
  4. package/CHANGELOG.md +18 -0
  5. package/dist/index.d.ts +63 -45
  6. package/dist/index.js +776 -647
  7. package/dist/index.js.map +1 -1
  8. package/package.json +2 -2
  9. package/src/components/WfoBadges/WfoEngineStatusBadge/WfoEngineStatusBadge.tsx +3 -1
  10. package/src/components/WfoBadges/WfoWebsocketStatusBadge/WfoWebsocketStatusBadge.tsx +9 -3
  11. package/src/components/WfoPydanticForm/fields/WfoArrayField/WfoArrayField.tsx +1 -1
  12. package/src/components/WfoPydanticForm/fields/WfoInteger.tsx +8 -6
  13. package/src/components/WfoPydanticForm/fields/WfoObjectField/WfoObjectField.tsx +1 -1
  14. package/src/components/WfoSubscription/WfoSubscriptionGeneralSections/WfoSubscriptionDetailSection.tsx +34 -3
  15. package/src/components/WfoTitleWithWebsocketBadge/WfoTitleWithWebsocketBadge.tsx +3 -2
  16. package/src/components/WfoWorkflowSteps/WfoStep/WfoStep.tsx +57 -40
  17. package/src/components/WfoWorkflowSteps/WfoStep/useStepDetailOverride.ts +9 -0
  18. package/src/components/WfoWorkflowSteps/WfoWorkflowStepList/WfoStepListHeader.tsx +0 -1
  19. package/src/configuration/version.ts +1 -1
  20. package/src/messages/en-GB.json +2 -0
  21. package/src/messages/nl-NL.json +2 -0
  22. package/src/pages/metadata/WfoScheduleTaskFormPage.tsx +153 -35
  23. package/src/rtk/endpoints/forms.ts +1 -1
  24. package/src/rtk/endpoints/metadata/scheduledTasks.ts +9 -17
  25. package/src/rtk/endpoints/streamMessages.ts +10 -23
  26. package/src/rtk/slices/orchestratorComponentOverride.ts +5 -1
  27. 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": "7.6.0",
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": "^1.0.7",
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> = ({ hideWhenConnected = false }) => {
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
- const { data: websocketConnected } = useStreamMessagesQuery();
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?.componentMatcherExtender);
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 && newValue !== value) {
47
- onChange(parseInt(newValue));
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?.componentMatcherExtender);
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 { subscriptionId, product, description, startDate, endDate, status, customer, customerDescriptions } =
23
- subscriptionDetail;
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
- <EuiFlexItem grow={0}>
115
- <EuiText css={stepListContentBoldTextStyle}>{step.name}</EuiText>
116
- <EuiText>
117
- {step.status} {step.completed && `- ${formatDate(step.completed)}`}
118
- </EuiText>
119
- </EuiFlexItem>
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
- <EuiFlexGroup css={stepRowStyle}>
122
- {step.completed && (
123
- <>
124
- {isExpanded && <WfoCodeViewSelector codeView={codeView} handleCodeViewChange={handle} />}
125
- <EuiFlexItem grow={0} css={stepHeaderRightStyle}>
126
- <EuiText css={stepDurationStyle}>{t('duration')}</EuiText>
127
- <EuiText size="m">{calculateTimeDifference(startedAt, completedAt)}</EuiText>
128
- </EuiFlexItem>
129
- <EuiFlexItem grow={0} css={getStepToggleExpandStyle(hasStepContent)}>
130
- {(isExpanded && <WfoChevronUp color={theme.colors.textParagraph} />) || (
131
- <WfoChevronDown color={theme.colors.textParagraph} />
132
- )}
133
- </EuiFlexItem>
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
- {hasStepContent
139
- && !hasHtmlMail
140
- && isExpanded
141
- && (codeView === CodeView.TABLE ? <WfoTableCodeBlock stepState={stepContent} />
142
- : codeView === CodeView.RAW ? <WfoJsonCodeBlock data={stepContent} />
143
- : <WfoMonacoCodeBlock data={stepContent} />)}
144
- {isExpanded && hasHtmlMail && (
145
- <div css={stepEmailContainerStyle}>
146
- {displayMailConfirmation(step.stateDelta.confirmation_mail as EmailState)}
147
- </div>
148
- )}
149
- {step.status === StepStatus.SUSPEND && userInputForm && (
150
- <WfoStepForm
151
- userInputForm={userInputForm}
152
- isTask={isTask}
153
- processId={processId ?? ''}
154
- userPermissions={userPermissions}
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>
@@ -0,0 +1,9 @@
1
+ import { useAppSelector } from '@/rtk/hooks';
2
+
3
+ export const useStepDetailOverride = () => {
4
+ const overrideStepDetail = useAppSelector((state) => state.orchestratorComponentOverride?.stepDetail);
5
+
6
+ return {
7
+ overrideStepDetail,
8
+ };
9
+ };
@@ -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 = '7.6.0';
1
+ export const ORCHESTRATOR_UI_LIBRARY_VERSION = '8.0.0';
@@ -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",
@@ -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
- PydanticForm,
8
- PydanticFormApiResponseType,
9
- PydanticFormFieldFormat,
10
- PydanticFormFieldType,
11
- } from 'pydantic-forms';
12
- import type {
13
- PydanticFormApiProvider,
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 { useGetPydanticFormsConfig, useShowToastMessage } from '@/hooks';
22
- import type { CronKwargs, ScheduledTaskPostPayload } from '@/rtk';
23
- import { useCreateScheduledTaskMutation, useGetTasksQuery } from '@/rtk';
24
- import { Intervals, TaskDefinition, TaskType, ToastTypes } from '@/types';
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
- export const WfoScheduleTaskFormPage = () => {
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
- type: userInputStep1.taskType,
260
- workflowId: task.workflowId,
261
- workflowDescription: task.description,
262
- workflowName: task.name,
263
- kwargs: {
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
- type: userInputStep1.taskType,
277
- workflowId: task.workflowId,
278
- workflowDescription: task.description,
279
- workflowName: task.name,
280
- kwargs: {
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
- type: userInputStep1.taskType,
290
- workflowId: task.workflowId,
291
- workflowDescription: task.description,
292
- workflowName: task.name,
293
- kwargs: getCronKwargs(step2Input.cron, startDate),
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 = 'surf/forms/'; // It is still being used by example-wfo-ui
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) => ({