@orchestrator-ui/orchestrator-ui-components 1.8.0 → 1.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchestrator-ui/orchestrator-ui-components",
3
- "version": "1.8.0",
3
+ "version": "1.10.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Library of UI Components used to display the workflow orchestrator frontend",
6
6
  "author": {
@@ -16,7 +16,7 @@ import React, { useCallback, useEffect, useState } from 'react';
16
16
 
17
17
  import { Form, FormNotCompleteResponse } from '@/types/forms';
18
18
 
19
- import UserInputFormWizard from './UserInputFormWizard';
19
+ import UserInputFormWizardDeprecated from './UserInputFormWizardDeprecated';
20
20
  import { useAxiosApiClient } from './useAxiosApiClient';
21
21
 
22
22
  interface IProps {
@@ -60,7 +60,7 @@ export function CreateForm(props: IProps) {
60
60
  return (
61
61
  <div>
62
62
  {stepUserInput && (
63
- <UserInputFormWizard
63
+ <UserInputFormWizardDeprecated
64
64
  stepUserInput={stepUserInput}
65
65
  validSubmit={submit}
66
66
  cancel={handleCancel}
@@ -18,10 +18,10 @@ import { useRouter } from 'next/router';
18
18
  import hash from 'object-hash';
19
19
 
20
20
  import { ConfirmDialogActions } from '@/contexts';
21
+ import { HttpStatus, handlePromiseErrorWithCallback } from '@/rtk';
21
22
  import { FormNotCompleteResponse, InputForm } from '@/types/forms';
22
23
 
23
24
  import UserInputForm from './UserInputForm';
24
- import { useAxiosApiClient } from './useAxiosApiClient';
25
25
 
26
26
  interface Form {
27
27
  form: InputForm;
@@ -30,7 +30,7 @@ interface Form {
30
30
 
31
31
  interface UserInputFormWizardProps {
32
32
  stepUserInput: InputForm;
33
- validSubmit: (processInput: object[]) => Promise<unknown>;
33
+ stepSubmit: (processInput: object[]) => Promise<unknown>;
34
34
  cancel?: () => void;
35
35
  isTask: boolean;
36
36
  hasNext?: boolean;
@@ -47,13 +47,12 @@ function stop(e: React.SyntheticEvent) {
47
47
  export function UserInputFormWizard({
48
48
  hasNext = false,
49
49
  stepUserInput,
50
- validSubmit,
50
+ stepSubmit,
51
51
  cancel,
52
52
  isTask,
53
53
  isResuming = false,
54
54
  }: UserInputFormWizardProps) {
55
55
  const router = useRouter();
56
- const apiClient = useAxiosApiClient();
57
56
  const [forms, setForms] = useState<Form[]>([
58
57
  { form: stepUserInput, hasNext: hasNext },
59
58
  ]);
@@ -75,20 +74,17 @@ export function UserInputFormWizard({
75
74
  const newUserInputs = userInputs.slice(0, forms.length - 1);
76
75
  newUserInputs.push(currentFormData);
77
76
 
78
- const result = validSubmit(newUserInputs);
79
- return apiClient.catchErrorStatus<FormNotCompleteResponse>(
80
- result,
81
- 510,
82
- (json) => {
83
- // Scroll to top when navigating to next screen of wizard
84
- window.scrollTo(0, 0);
85
- // setFlash(intl.formatMessage({ id: "process.flash.wizard_next_step" }));
86
- setForms([
87
- ...forms,
88
- { form: json.form, hasNext: json.hasNext },
89
- ]);
90
- setUserInputs(newUserInputs);
91
- },
77
+ const promise = stepSubmit(newUserInputs);
78
+ const callback = (data: FormNotCompleteResponse) => {
79
+ window.scrollTo(0, 0);
80
+ setForms([...forms, { form: data.form, hasNext: data.hasNext }]);
81
+ setUserInputs(newUserInputs);
82
+ };
83
+
84
+ return handlePromiseErrorWithCallback<FormNotCompleteResponse>(
85
+ promise,
86
+ HttpStatus.FormNotComplete,
87
+ callback,
92
88
  );
93
89
  };
94
90
 
@@ -0,0 +1,125 @@
1
+ /*
2
+ * Copyright 2019-2023 SURF.
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ * http://www.apache.org/licenses/LICENSE-2.0
7
+ *
8
+ * Unless required by applicable law or agreed to in writing, software
9
+ * distributed under the License is distributed on an "AS IS" BASIS,
10
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ * See the License for the specific language governing permissions and
12
+ * limitations under the License.
13
+ *
14
+ */
15
+ import React, { useEffect, useState } from 'react';
16
+
17
+ import { useRouter } from 'next/router';
18
+ import hash from 'object-hash';
19
+
20
+ import { ConfirmDialogActions } from '@/contexts';
21
+ import { FormNotCompleteResponse, InputForm } from '@/types/forms';
22
+
23
+ import UserInputForm from './UserInputForm';
24
+ import { useAxiosApiClient } from './useAxiosApiClient';
25
+
26
+ interface Form {
27
+ form: InputForm;
28
+ hasNext?: boolean;
29
+ }
30
+
31
+ interface UserInputFormWizardProps {
32
+ stepUserInput: InputForm;
33
+ validSubmit: (processInput: object[]) => Promise<unknown>;
34
+ cancel?: () => void;
35
+ isTask: boolean;
36
+ hasNext?: boolean;
37
+ isResuming?: boolean;
38
+ }
39
+
40
+ function stop(e: React.SyntheticEvent) {
41
+ if (e !== undefined && e !== null) {
42
+ e.preventDefault();
43
+ e.stopPropagation();
44
+ }
45
+ }
46
+
47
+ export function UserInputFormWizardDeprecated({
48
+ hasNext = false,
49
+ stepUserInput,
50
+ validSubmit,
51
+ cancel,
52
+ isTask,
53
+ isResuming = false,
54
+ }: UserInputFormWizardProps) {
55
+ const router = useRouter();
56
+ const apiClient = useAxiosApiClient();
57
+ const [forms, setForms] = useState<Form[]>([
58
+ { form: stepUserInput, hasNext: hasNext },
59
+ ]);
60
+ const [userInputs, setUserInputs] = useState<object[]>([]);
61
+
62
+ useEffect(() => {
63
+ setForms([{ form: stepUserInput, hasNext: hasNext }]);
64
+ }, [hasNext, stepUserInput]);
65
+
66
+ const previous: ConfirmDialogActions['closeConfirmDialog'] = (e) => {
67
+ if (e) {
68
+ stop(e);
69
+ }
70
+ const current = forms.pop();
71
+ setForms(forms.filter((item) => item !== current));
72
+ };
73
+
74
+ const submit = (currentFormData: object) => {
75
+ const newUserInputs = userInputs.slice(0, forms.length - 1);
76
+ newUserInputs.push(currentFormData);
77
+
78
+ const result = validSubmit(newUserInputs);
79
+ return apiClient.catchErrorStatus<FormNotCompleteResponse>(
80
+ result,
81
+ 510,
82
+ (json) => {
83
+ window.scrollTo(0, 0);
84
+ setForms([
85
+ ...forms,
86
+ { form: json.form, hasNext: json.hasNext },
87
+ ]);
88
+ setUserInputs(newUserInputs);
89
+ },
90
+ );
91
+ };
92
+
93
+ const currentForm = forms[forms.length - 1];
94
+ const currentUserInput = userInputs[forms.length - 1];
95
+ if (!currentForm || !currentForm.form.properties) {
96
+ return null;
97
+ }
98
+
99
+ /* Generate a key based on input widget names that results in a new
100
+ * clean instance + rerender of UserInputForm if the form changes. Without this, state of previous,
101
+ * wizard step can cause wrong/weird default values for forms inputs.
102
+ *
103
+ * Note: to ensure a new form for multiple form wizard steps with exactly the same fields and labels on
104
+ * the form the hash is calculated on the form object itself + length, which generates a unique hash as it
105
+ * has a changing ".length" attribute.
106
+ * */
107
+ const key = hash.sha1({ form: currentForm.form, length: forms.length });
108
+ return (
109
+ <UserInputForm
110
+ key={key}
111
+ router={router}
112
+ stepUserInput={currentForm.form}
113
+ validSubmit={submit}
114
+ previous={previous}
115
+ hasNext={currentForm.hasNext}
116
+ hasPrev={forms.length > 1}
117
+ cancel={cancel}
118
+ userInput={currentUserInput}
119
+ isTask={isTask}
120
+ isResuming={isResuming}
121
+ />
122
+ );
123
+ }
124
+
125
+ export default UserInputFormWizardDeprecated;
@@ -1,5 +1,6 @@
1
1
  export * from './AutoFields';
2
2
  export * from './UserInputForm';
3
3
  export * from './UserInputFormWizard';
4
+ export * from './UserInputFormWizardDeprecated';
4
5
  export * from './formFields';
5
6
  export * from './CreateForm';
@@ -0,0 +1,50 @@
1
+ import React, { FC } from 'react';
2
+
3
+ import { useTranslations } from 'next-intl';
4
+ import Link from 'next/link';
5
+
6
+ import { useWithOrchestratorTheme } from '@/hooks';
7
+
8
+ import { getMenuItemStyles } from './styles';
9
+
10
+ type WfoMenuItemLinkProps = {
11
+ path: string;
12
+ translationString: string;
13
+ isSelected: boolean;
14
+ isSubItem?: boolean;
15
+ };
16
+
17
+ export const WfoMenuItemLink: FC<WfoMenuItemLinkProps> = ({
18
+ path,
19
+ translationString,
20
+ isSelected,
21
+ isSubItem,
22
+ }) => {
23
+ const {
24
+ menuItemStyle,
25
+ selectedMenuItem,
26
+ selectedSubMenuItem,
27
+ subMenuItemStyle,
28
+ } = useWithOrchestratorTheme(getMenuItemStyles);
29
+
30
+ const getMenuItemStyle = () => {
31
+ if (isSubItem) {
32
+ return isSelected ? selectedSubMenuItem : subMenuItemStyle;
33
+ } else {
34
+ return isSelected ? selectedMenuItem : menuItemStyle;
35
+ }
36
+ };
37
+
38
+ const t = useTranslations('main');
39
+
40
+ // This is a workaround to use the translation string as the link text if it's not found in the translation file.
41
+ const linkText =
42
+ t(translationString) === `main.${translationString}`
43
+ ? translationString
44
+ : t(translationString);
45
+ return (
46
+ <Link css={getMenuItemStyle()} href={path}>
47
+ {linkText}
48
+ </Link>
49
+ );
50
+ };
@@ -25,6 +25,7 @@ import {
25
25
  PATH_WORKFLOWS,
26
26
  } from '../paths';
27
27
  import { WfoCopyright } from './WfoCopyright';
28
+ import { WfoMenuItemLink } from './WfoMenuLink';
28
29
 
29
30
  export const renderEmptyElementWhenNotAllowedByPolicy = (isAllowed: boolean) =>
30
31
  isAllowed ? undefined : () => <></>;
@@ -46,6 +47,7 @@ export type WfoSidebarProps = {
46
47
  export const WfoSidebar: FC<WfoSidebarProps> = ({ overrideMenuItems }) => {
47
48
  const t = useTranslations('main');
48
49
  const router = useRouter();
50
+
49
51
  const [isSideNavOpenOnMobile, setIsSideNavOpenOnMobile] = useState(false);
50
52
  const { isAllowed } = usePolicy();
51
53
 
@@ -53,90 +55,144 @@ export const WfoSidebar: FC<WfoSidebarProps> = ({ overrideMenuItems }) => {
53
55
  setIsSideNavOpenOnMobile((openState) => !openState);
54
56
  };
55
57
 
58
+ // Note: href is used to determine if the user has access to the page in
59
+ // defaultMenuItemsFilteredByPolicy so we need to keep it in the item although we don't use it in the render.
56
60
  const defaultMenuItems: EuiSideNavItemType<object>[] = [
57
61
  {
58
62
  name: t('start'),
59
63
  id: '2',
60
64
  isSelected: router.pathname === PATH_START,
61
- onClick: (e) => {
62
- e.preventDefault();
63
- router.push(PATH_START);
64
- },
65
+ href: PATH_START,
66
+ renderItem: () => (
67
+ <WfoMenuItemLink
68
+ path={PATH_START}
69
+ translationString="start"
70
+ isSelected={router.pathname === PATH_START}
71
+ />
72
+ ),
65
73
  },
66
74
  {
67
75
  name: t('workflows'),
68
76
  id: '3',
69
77
  isSelected: router.pathname === PATH_WORKFLOWS,
70
78
  href: PATH_WORKFLOWS,
71
- onClick: (e) => {
72
- e.preventDefault();
73
- router.push(PATH_WORKFLOWS);
74
- },
79
+ renderItem: () => (
80
+ <WfoMenuItemLink
81
+ path={PATH_WORKFLOWS}
82
+ translationString="workflows"
83
+ isSelected={router.pathname === PATH_WORKFLOWS}
84
+ />
85
+ ),
75
86
  },
76
87
  {
77
88
  name: t('subscriptions'),
78
89
  id: '4',
79
90
  isSelected: router.pathname === PATH_SUBSCRIPTIONS,
80
91
  href: PATH_SUBSCRIPTIONS,
81
- onClick: (e) => {
82
- e.preventDefault();
83
- router.push(PATH_SUBSCRIPTIONS);
84
- },
92
+ renderItem: () => (
93
+ <WfoMenuItemLink
94
+ path={PATH_SUBSCRIPTIONS}
95
+ translationString="subscriptions"
96
+ isSelected={router.pathname === PATH_SUBSCRIPTIONS}
97
+ />
98
+ ),
85
99
  },
86
100
  {
87
101
  name: t('metadata'),
88
102
  id: '5',
89
103
  href: PATH_METADATA,
90
- onClick: () => {
91
- router.push(PATH_METADATA);
92
- },
104
+ isSelected:
105
+ router.pathname.substring(0, PATH_METADATA.length) ===
106
+ PATH_METADATA,
107
+ renderItem: () => (
108
+ <WfoMenuItemLink
109
+ path={PATH_METADATA_PRODUCTS}
110
+ translationString="metadata"
111
+ isSelected={
112
+ router.pathname.substring(0, PATH_METADATA.length) ===
113
+ PATH_METADATA
114
+ }
115
+ />
116
+ ),
93
117
  items: [
94
118
  {
95
119
  name: t('metadataProducts'),
96
120
  id: '5.1',
97
- isSelected: router.pathname === PATH_METADATA_PRODUCTS,
98
- onClick: (e) => {
99
- e.preventDefault();
100
- router.push(PATH_METADATA_PRODUCTS);
101
- },
121
+ href: PATH_METADATA_PRODUCTS,
122
+ renderItem: () => (
123
+ <WfoMenuItemLink
124
+ path={PATH_METADATA_PRODUCTS}
125
+ translationString="metadataProducts"
126
+ isSelected={
127
+ router.pathname === PATH_METADATA_PRODUCTS
128
+ }
129
+ isSubItem={true}
130
+ />
131
+ ),
102
132
  },
103
133
  {
104
134
  name: t('metadataProductblocks'),
105
135
  id: '5.2',
106
136
  isSelected:
107
137
  router.pathname === PATH_METADATA_PRODUCT_BLOCKS,
108
- onClick: (e) => {
109
- e.preventDefault();
110
- router.push(PATH_METADATA_PRODUCT_BLOCKS);
111
- },
138
+ href: PATH_METADATA_PRODUCT_BLOCKS,
139
+ renderItem: () => (
140
+ <WfoMenuItemLink
141
+ path={PATH_METADATA_PRODUCT_BLOCKS}
142
+ translationString="metadataProductblocks"
143
+ isSelected={
144
+ router.pathname === PATH_METADATA_PRODUCT_BLOCKS
145
+ }
146
+ isSubItem={true}
147
+ />
148
+ ),
112
149
  },
113
150
  {
114
151
  name: t('metadataResourceTypes'),
115
152
  id: '5.3',
153
+ href: PATH_METADATA_RESOURCE_TYPES,
116
154
  isSelected:
117
155
  router.pathname === PATH_METADATA_RESOURCE_TYPES,
118
- onClick: (e) => {
119
- e.preventDefault();
120
- router.push(PATH_METADATA_RESOURCE_TYPES);
121
- },
156
+ renderItem: () => (
157
+ <WfoMenuItemLink
158
+ path={PATH_METADATA_RESOURCE_TYPES}
159
+ translationString="metadataResourceTypes"
160
+ isSelected={
161
+ router.pathname === PATH_METADATA_RESOURCE_TYPES
162
+ }
163
+ isSubItem={true}
164
+ />
165
+ ),
122
166
  },
123
167
  {
124
168
  name: t('metadataWorkflows'),
125
169
  id: '5.4',
126
170
  isSelected: router.pathname === PATH_METADATA_WORKFLOWS,
127
- onClick: (e) => {
128
- e.preventDefault();
129
- router.push(PATH_METADATA_WORKFLOWS);
130
- },
171
+ href: PATH_METADATA_WORKFLOWS,
172
+ renderItem: () => (
173
+ <WfoMenuItemLink
174
+ path={PATH_METADATA_WORKFLOWS}
175
+ translationString="metadataWorkflows"
176
+ isSelected={
177
+ router.pathname === PATH_METADATA_WORKFLOWS
178
+ }
179
+ isSubItem={true}
180
+ />
181
+ ),
131
182
  },
132
183
  {
133
184
  name: t('metadataTasks'),
134
185
  id: '5.5',
135
186
  isSelected: router.pathname === PATH_METADATA_TASKS,
136
- onClick: (e) => {
137
- e.preventDefault();
138
- router.push(PATH_METADATA_TASKS);
139
- },
187
+ href: PATH_METADATA_TASKS,
188
+ renderItem: () => (
189
+ <WfoMenuItemLink
190
+ path={PATH_METADATA_TASKS}
191
+ translationString="metadataTasks"
192
+ isSelected={router.pathname === PATH_METADATA_TASKS}
193
+ isSubItem={true}
194
+ />
195
+ ),
140
196
  },
141
197
  ],
142
198
  },
@@ -144,21 +200,27 @@ export const WfoSidebar: FC<WfoSidebarProps> = ({ overrideMenuItems }) => {
144
200
  name: t('tasks'),
145
201
  isSelected: router.pathname === PATH_TASKS,
146
202
  id: '6',
147
- onClick: (e) => {
148
- e.preventDefault();
149
- router.push(PATH_TASKS);
150
- },
151
203
  href: PATH_TASKS,
204
+ renderItem: () => (
205
+ <WfoMenuItemLink
206
+ path={PATH_TASKS}
207
+ translationString="tasks"
208
+ isSelected={router.pathname === PATH_TASKS}
209
+ />
210
+ ),
152
211
  },
153
212
  {
154
213
  name: t('settings'),
155
214
  isSelected: router.pathname === PATH_SETTINGS,
156
215
  id: '7',
157
- onClick: (e) => {
158
- e.preventDefault();
159
- router.push(PATH_SETTINGS);
160
- },
161
216
  href: PATH_SETTINGS,
217
+ renderItem: () => (
218
+ <WfoMenuItemLink
219
+ path={PATH_SETTINGS}
220
+ translationString="settings"
221
+ isSelected={router.pathname === PATH_SETTINGS}
222
+ />
223
+ ),
162
224
  },
163
225
  ];
164
226
 
@@ -1 +1,2 @@
1
1
  export * from './WfoSidebar';
2
+ export * from './WfoMenuLink';
@@ -1,5 +1,5 @@
1
1
  import { EuiThemeComputed } from '@elastic/eui';
2
- import { css } from '@emotion/react';
2
+ import { CSSObject, css } from '@emotion/react';
3
3
 
4
4
  export const getCopyrightStyles = (theme: EuiThemeComputed) => {
5
5
  const copyrightStyle = css({
@@ -15,3 +15,64 @@ export const getCopyrightStyles = (theme: EuiThemeComputed) => {
15
15
  copyrightStyle,
16
16
  };
17
17
  };
18
+
19
+ export const getMenuItemStyles = (theme: EuiThemeComputed) => {
20
+ const baseStyles: CSSObject = {
21
+ lineHeight: `${theme.base * 1.25}px`,
22
+ display: 'flex',
23
+ alignItems: 'center',
24
+ ':hover': {
25
+ textDecoration: 'underline',
26
+ },
27
+ color: theme.colors.text,
28
+ padding: `${theme.base * 0.5}px ${theme.base * 0.75}px`,
29
+ };
30
+
31
+ const baseSubMenuStyles: CSSObject = {
32
+ ...baseStyles,
33
+ ':after': {
34
+ content: "''",
35
+ top: '16px',
36
+ left: 0,
37
+ width: '4px',
38
+ height: '1px',
39
+ backgroundColor: '#d3dae6',
40
+ position: 'absolute',
41
+ },
42
+ padding: '8px 12px',
43
+ ':last-child': {
44
+ top: '-4px',
45
+ position: 'relative',
46
+ },
47
+ };
48
+
49
+ const menuItemStyle = css({
50
+ ...baseStyles,
51
+ color: theme.colors.title,
52
+ });
53
+
54
+ const selectedMenuItem = css({
55
+ ...baseStyles,
56
+ height: `${theme.base * 2.25}px`,
57
+ backgroundColor: theme.colors.lightShade,
58
+ borderRadius: theme.border.radius.medium,
59
+ fontWeight: theme.font.weight.semiBold,
60
+ color: theme.colors.primaryText,
61
+ });
62
+
63
+ const selectedSubMenuItem = css({
64
+ ...baseSubMenuStyles,
65
+ fontWeight: theme.font.weight.semiBold,
66
+ });
67
+
68
+ const subMenuItemStyle = css({
69
+ ...baseSubMenuStyles,
70
+ });
71
+
72
+ return {
73
+ menuItemStyle,
74
+ selectedMenuItem,
75
+ selectedSubMenuItem,
76
+ subMenuItemStyle,
77
+ };
78
+ };
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
2
2
 
3
3
  import { EuiFlexItem } from '@elastic/eui';
4
4
 
5
- import { UserInputFormWizard, WfoLoading } from '@/components';
5
+ import { UserInputFormWizardDeprecated, WfoLoading } from '@/components';
6
6
  import { useAxiosApiClient } from '@/components/WfoForms/useAxiosApiClient';
7
7
  import { useOrchestratorTheme } from '@/hooks';
8
8
  import { InputForm } from '@/types/forms';
@@ -35,7 +35,7 @@ export const WfoStepForm = ({
35
35
  return (
36
36
  <EuiFlexItem css={{ margin: theme.size.m }}>
37
37
  {(isProcessing && <WfoLoading />) || (
38
- <UserInputFormWizard
38
+ <UserInputFormWizardDeprecated
39
39
  stepUserInput={userInputForm}
40
40
  validSubmit={submitForm}
41
41
  hasNext={false}
@@ -42,6 +42,10 @@ export const filterDataByCriteria = <Type>(
42
42
  });
43
43
  };
44
44
 
45
+ /**
46
+ * @deprecated
47
+ * Currently not in use in the SURF app - switched to RTK Query
48
+ */
45
49
  export const useFilterQueryWithRest = <Type>(
46
50
  url: string,
47
51
  queryKey: string[],
@@ -28,7 +28,7 @@ import {
28
28
  } from '@/types';
29
29
  import { FormNotCompleteResponse } from '@/types/forms';
30
30
 
31
- import UserInputFormWizard from '../../components/WfoForms/UserInputFormWizard';
31
+ import UserInputFormWizardDeprecated from '../../components/WfoForms/UserInputFormWizardDeprecated';
32
32
  import { WfoProcessDetail } from './WfoProcessDetail';
33
33
 
34
34
  type StartCreateWorkflowPayload = {
@@ -215,7 +215,7 @@ export const WfoStartProcessPage = ({
215
215
  <EuiHorizontalRule />
216
216
  {(hasError && <WfoError />) ||
217
217
  (stepUserInput && (
218
- <UserInputFormWizard
218
+ <UserInputFormWizardDeprecated
219
219
  stepUserInput={stepUserInput}
220
220
  validSubmit={submit}
221
221
  cancel={() =>
package/src/rtk/api.ts CHANGED
@@ -21,6 +21,12 @@ export enum CacheTags {
21
21
  subscriptionList = 'subscriptionList',
22
22
  }
23
23
 
24
+ export enum HttpStatus {
25
+ FormNotComplete = 510,
26
+ BadGateway = 502,
27
+ BadRequest = 400,
28
+ }
29
+
24
30
  type ExtraOptions = {
25
31
  baseQueryType?: BaseQueryTypes;
26
32
  apiName?: string;
@@ -34,6 +40,20 @@ export const prepareHeaders = async (headers: Headers) => {
34
40
  return headers;
35
41
  };
36
42
 
43
+ export const handlePromiseErrorWithCallback = <T>(
44
+ promise: Promise<unknown>,
45
+ status: number,
46
+ callbackAction: (json: T) => void,
47
+ ) => {
48
+ return promise.catch((err) => {
49
+ if (err.data && err.status === status) {
50
+ callbackAction(err.data);
51
+ } else {
52
+ throw err;
53
+ }
54
+ });
55
+ };
56
+
37
57
  export const orchestratorApi = createApi({
38
58
  reducerPath: 'orchestratorApi',
39
59
  baseQuery: (args, api, extraOptions: ExtraOptions) => {