@orchestrator-ui/orchestrator-ui-components 4.2.0 → 5.1.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 +8 -8
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +6 -6
- package/CHANGELOG.md +26 -0
- package/dist/index.d.ts +2115 -336
- package/dist/index.js +2147 -1670
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/WfoBadges/WfoSubscriptionStatusBadge/WfoSubscriptionStatusBadge.tsx +6 -5
- package/src/components/WfoBadges/WfoWorkflowTargetBadge/WfoWorkflowTargetBadge.tsx +1 -0
- package/src/components/WfoButtonComboBox/WfoButtonComboBox.tsx +79 -0
- package/src/components/WfoButtonComboBox/index.ts +1 -0
- package/src/components/WfoButtonComboBox/styles.ts +28 -0
- package/src/components/WfoForms/formFields/SubscriptionSummaryField.tsx +1 -1
- package/src/components/WfoForms/formFields/commonStyles.ts +2 -0
- package/src/components/WfoForms/formFields/index.ts +1 -0
- package/src/components/WfoPydanticForm/Footer.tsx +75 -34
- package/src/components/WfoPydanticForm/Row.tsx +2 -2
- package/src/components/WfoPydanticForm/WfoPydanticForm.tsx +99 -8
- package/src/components/WfoPydanticForm/fields/Checkbox.tsx +22 -0
- package/src/components/WfoPydanticForm/fields/Divider.tsx +17 -0
- package/src/components/WfoPydanticForm/fields/Label.tsx +23 -0
- package/src/components/WfoPydanticForm/fields/Summary.tsx +125 -0
- package/src/components/WfoPydanticForm/fields/Text.tsx +28 -0
- package/src/components/WfoPydanticForm/fields/index.ts +5 -0
- package/src/components/WfoSubscription/WfoInSyncField.tsx +2 -2
- package/src/components/WfoSubscription/WfoSubscription.tsx +3 -2
- package/src/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActions.tsx +3 -3
- package/src/components/WfoSubscription/WfoSubscriptionDetailTree.tsx +64 -14
- package/src/components/WfoSubscription/utils/utils.spec.ts +121 -0
- package/src/components/WfoSubscription/utils/utils.ts +42 -6
- package/src/configuration/version.ts +1 -1
- package/src/contexts/TreeContext.tsx +16 -0
- package/src/icons/WfoExclamationTriangle.tsx +29 -0
- package/src/icons/index.ts +1 -0
- package/src/messages/en-GB.json +2 -0
- package/src/messages/nl-NL.json +2 -0
- package/src/pages/metadata/WfoTasksPage.tsx +11 -26
- package/src/pages/metadata/WfoWorkflowsPage.tsx +11 -12
- package/src/rtk/endpoints/index.ts +1 -0
- package/src/rtk/endpoints/metadata/tasks.ts +9 -4
- package/src/rtk/endpoints/metadata/workflows.ts +10 -5
- package/src/rtk/endpoints/startOptions.ts +9 -3
- package/src/types/types.ts +7 -3
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { PydanticFormElement } from 'pydantic-forms';
|
|
4
|
+
|
|
5
|
+
import { EuiFlexItem, EuiFormRow, EuiText } from '@elastic/eui';
|
|
6
|
+
import { tint } from '@elastic/eui';
|
|
7
|
+
import { css } from '@emotion/react';
|
|
8
|
+
import type { WfoTheme } from '@orchestrator-ui/orchestrator-ui-components';
|
|
9
|
+
|
|
10
|
+
import { getCommonFormFieldStyles } from '@/components/WfoForms/formFields/commonStyles';
|
|
11
|
+
import { useWithOrchestratorTheme } from '@/hooks';
|
|
12
|
+
|
|
13
|
+
export const getStyles = ({ theme }: WfoTheme) => {
|
|
14
|
+
const toShadeColor = (color: string) => tint(color, 0.9);
|
|
15
|
+
|
|
16
|
+
const summaryFieldStyle = css({
|
|
17
|
+
'div.emailMessage': {
|
|
18
|
+
td: {
|
|
19
|
+
color: theme.colors.text,
|
|
20
|
+
},
|
|
21
|
+
p: {
|
|
22
|
+
color: theme.colors.text,
|
|
23
|
+
},
|
|
24
|
+
html: {
|
|
25
|
+
marginLeft: '-10px',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
'section.table-summary': {
|
|
29
|
+
marginTop: '20px',
|
|
30
|
+
width: '100%',
|
|
31
|
+
td: {
|
|
32
|
+
padding: '10px',
|
|
33
|
+
verticalAlign: 'top',
|
|
34
|
+
},
|
|
35
|
+
'td:not(:first-child):not(:last-child)': {
|
|
36
|
+
borderRight: `1px solid ${theme.colors.lightestShade}`,
|
|
37
|
+
},
|
|
38
|
+
'.label': {
|
|
39
|
+
fontWeight: 'bold',
|
|
40
|
+
color: theme.colors.lightestShade,
|
|
41
|
+
backgroundColor: theme.colors.primary,
|
|
42
|
+
borderRight: `2px solid ${theme.colors.lightestShade}`,
|
|
43
|
+
borderBottom: `1px solid ${theme.colors.lightestShade}`,
|
|
44
|
+
},
|
|
45
|
+
'.value': {
|
|
46
|
+
backgroundColor: toShadeColor(theme.colors.primary),
|
|
47
|
+
borderBottom: `1px solid ${theme.colors.lightestShade}`,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
summaryFieldStyle: summaryFieldStyle,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const Summary: PydanticFormElement = ({ pydanticFormField }) => {
|
|
57
|
+
const { summaryFieldStyle } = useWithOrchestratorTheme(getStyles);
|
|
58
|
+
const { formRowStyle } = useWithOrchestratorTheme(getCommonFormFieldStyles);
|
|
59
|
+
|
|
60
|
+
const { id, title, description } = pydanticFormField;
|
|
61
|
+
const uniforms = pydanticFormField.schema.uniforms;
|
|
62
|
+
const summaryData = uniforms?.data as unknown as {
|
|
63
|
+
headers: string[][];
|
|
64
|
+
labels: string[];
|
|
65
|
+
columns: string[][];
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const headers = summaryData?.headers as string[][];
|
|
69
|
+
const labels = summaryData?.labels as string[];
|
|
70
|
+
const columns = summaryData?.columns || [];
|
|
71
|
+
|
|
72
|
+
const extraColumnsData = columns.filter((_, index) => index !== 0);
|
|
73
|
+
|
|
74
|
+
const rows = columns[0].map((row, index) => (
|
|
75
|
+
<tr key={index}>
|
|
76
|
+
{labels && <td className={`label`}>{labels[index]}</td>}
|
|
77
|
+
<td className={`value`}>
|
|
78
|
+
{typeof row === 'string' && row.includes('<!doctype html>') ? (
|
|
79
|
+
<div
|
|
80
|
+
className="emailMessage"
|
|
81
|
+
dangerouslySetInnerHTML={{ __html: row }}
|
|
82
|
+
></div>
|
|
83
|
+
) : (
|
|
84
|
+
row
|
|
85
|
+
)}
|
|
86
|
+
</td>
|
|
87
|
+
{extraColumnsData &&
|
|
88
|
+
extraColumnsData.map((_, idx) => (
|
|
89
|
+
<td className={`value`} key={idx}>
|
|
90
|
+
{extraColumnsData[idx][index]}
|
|
91
|
+
</td>
|
|
92
|
+
))}
|
|
93
|
+
</tr>
|
|
94
|
+
));
|
|
95
|
+
|
|
96
|
+
const tableHeader =
|
|
97
|
+
!headers || headers.length === 0 ? null : (
|
|
98
|
+
<tr>
|
|
99
|
+
{labels && <th />}
|
|
100
|
+
{headers.map((header, idx) => (
|
|
101
|
+
<th key={idx}>{header}</th>
|
|
102
|
+
))}
|
|
103
|
+
</tr>
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<EuiFlexItem css={[summaryFieldStyle, formRowStyle]}>
|
|
108
|
+
<section>
|
|
109
|
+
<EuiFormRow
|
|
110
|
+
label={description}
|
|
111
|
+
labelAppend={<EuiText size="m">{title}</EuiText>}
|
|
112
|
+
id={id}
|
|
113
|
+
fullWidth
|
|
114
|
+
>
|
|
115
|
+
<section className="table-summary">
|
|
116
|
+
<table id={`${id}-table`}>
|
|
117
|
+
<thead>{tableHeader}</thead>
|
|
118
|
+
<tbody>{rows}</tbody>
|
|
119
|
+
</table>
|
|
120
|
+
</section>
|
|
121
|
+
</EuiFormRow>
|
|
122
|
+
</section>
|
|
123
|
+
</EuiFlexItem>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { PydanticFormControlledElement } from 'pydantic-forms';
|
|
4
|
+
|
|
5
|
+
import { EuiFieldText } from '@elastic/eui';
|
|
6
|
+
|
|
7
|
+
import { useWithOrchestratorTheme } from '@/hooks';
|
|
8
|
+
import { getFormFieldsBaseStyle } from '@/theme';
|
|
9
|
+
|
|
10
|
+
export const Text: PydanticFormControlledElement = ({
|
|
11
|
+
onChange,
|
|
12
|
+
value,
|
|
13
|
+
disabled,
|
|
14
|
+
}) => {
|
|
15
|
+
const { formFieldBaseStyle } = useWithOrchestratorTheme(
|
|
16
|
+
getFormFieldsBaseStyle,
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<EuiFieldText
|
|
21
|
+
css={formFieldBaseStyle}
|
|
22
|
+
disabled={disabled}
|
|
23
|
+
onChange={(event) => onChange(event.target.value)}
|
|
24
|
+
value={value ?? ''}
|
|
25
|
+
fullWidth
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
@@ -26,10 +26,10 @@ export const WfoInSyncField = ({ subscriptionDetail }: WfoInSyncFieldProps) => {
|
|
|
26
26
|
const { theme } = useOrchestratorTheme();
|
|
27
27
|
const [inSync, setInSync] = useState<boolean>(subscriptionDetail.insync);
|
|
28
28
|
const lastTaskRunDate = getLatestTaskDate(
|
|
29
|
-
subscriptionDetail.processes
|
|
29
|
+
subscriptionDetail.processes?.page,
|
|
30
30
|
);
|
|
31
31
|
const lastUncompletedProcess = getLastUncompletedProcess(
|
|
32
|
-
subscriptionDetail
|
|
32
|
+
subscriptionDetail?.processes?.page,
|
|
33
33
|
);
|
|
34
34
|
const [setSubscriptionInSync, { isLoading }] =
|
|
35
35
|
useSetSubscriptionInSyncMutation();
|
|
@@ -102,10 +102,11 @@ export const WfoSubscription = ({ subscriptionId }: WfoSubscriptionProps) => {
|
|
|
102
102
|
/>
|
|
103
103
|
)}
|
|
104
104
|
{selectedTab === SubscriptionDetailTab.PROCESSES_TAB &&
|
|
105
|
-
data &&
|
|
105
|
+
data &&
|
|
106
|
+
subscriptionDetail.processes?.page && (
|
|
106
107
|
<WfoProcessesTimeline
|
|
107
108
|
subscriptionDetailProcesses={
|
|
108
|
-
subscriptionDetail.processes
|
|
109
|
+
subscriptionDetail.processes?.page
|
|
109
110
|
}
|
|
110
111
|
/>
|
|
111
112
|
)}
|
|
@@ -238,16 +238,16 @@ export const WfoSubscriptionActions: FC<WfoSubscriptionActionsProps> = ({
|
|
|
238
238
|
PolicyResource.SUBSCRIPTION_VALIDATE +
|
|
239
239
|
subscriptionId,
|
|
240
240
|
) &&
|
|
241
|
-
subscriptionActions.
|
|
241
|
+
subscriptionActions.validate && (
|
|
242
242
|
<>
|
|
243
243
|
<MenuBlock title={t('tasks')}></MenuBlock>
|
|
244
|
-
{subscriptionActions.
|
|
244
|
+
{subscriptionActions.validate.map(
|
|
245
245
|
(action, index) => (
|
|
246
246
|
<MenuItem
|
|
247
247
|
key={`s_${index}`}
|
|
248
248
|
action={action}
|
|
249
249
|
index={index}
|
|
250
|
-
target={WorkflowTarget.
|
|
250
|
+
target={WorkflowTarget.VALIDATE}
|
|
251
251
|
isTask={true}
|
|
252
252
|
/>
|
|
253
253
|
),
|
|
@@ -2,9 +2,22 @@ import React, { useState } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { useTranslations } from 'next-intl';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
EuiCallOut,
|
|
7
|
+
EuiFlexGroup,
|
|
8
|
+
EuiFlexItem,
|
|
9
|
+
EuiSelectableOption,
|
|
10
|
+
EuiText,
|
|
11
|
+
} from '@elastic/eui';
|
|
12
|
+
import { EuiSelectableOptionCheckedType } from '@elastic/eui/src/components/selectable/selectable_option';
|
|
6
13
|
|
|
7
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
PATH_SUBSCRIPTIONS,
|
|
16
|
+
WfoLoading,
|
|
17
|
+
WfoTextAnchor,
|
|
18
|
+
mapProductBlockInstancesToEuiSelectableOptions,
|
|
19
|
+
} from '@/components';
|
|
20
|
+
import { WfoButtonComboBox } from '@/components/WfoButtonComboBox';
|
|
8
21
|
import { TreeContext, TreeContextType } from '@/contexts';
|
|
9
22
|
import { useOrchestratorTheme, useWithOrchestratorTheme } from '@/hooks';
|
|
10
23
|
import {
|
|
@@ -21,6 +34,8 @@ import { WfoSubscriptionProductBlock } from './WfoSubscriptionProductBlock';
|
|
|
21
34
|
import { getSubscriptionDetailStyles } from './styles';
|
|
22
35
|
import { getProductBlockTitle } from './utils';
|
|
23
36
|
|
|
37
|
+
const EUI_OPTION_CHECKED_STATE_ON: EuiSelectableOptionCheckedType = 'on';
|
|
38
|
+
|
|
24
39
|
interface WfoSubscriptionDetailTreeProps {
|
|
25
40
|
productBlockInstances: ProductBlockInstance[];
|
|
26
41
|
subscriptionId: Subscription['subscriptionId'];
|
|
@@ -38,8 +53,14 @@ export const WfoSubscriptionDetailTree = ({
|
|
|
38
53
|
const { productBlockTreeWidth } = useWithOrchestratorTheme(
|
|
39
54
|
getSubscriptionDetailStyles,
|
|
40
55
|
);
|
|
41
|
-
const {
|
|
42
|
-
|
|
56
|
+
const {
|
|
57
|
+
selectedIds,
|
|
58
|
+
expandAll,
|
|
59
|
+
resetSelection,
|
|
60
|
+
selectAll,
|
|
61
|
+
selectIds,
|
|
62
|
+
deselectIds,
|
|
63
|
+
} = React.useContext(TreeContext) as TreeContextType;
|
|
43
64
|
|
|
44
65
|
let tree: TreeBlock | null = null;
|
|
45
66
|
const depthList: number[] = [];
|
|
@@ -99,7 +120,6 @@ export const WfoSubscriptionDetailTree = ({
|
|
|
99
120
|
const toggleShowAll = () => {
|
|
100
121
|
if (selectedIds.length === productBlockInstances.length) {
|
|
101
122
|
resetSelection();
|
|
102
|
-
collapseAll();
|
|
103
123
|
} else {
|
|
104
124
|
selectAll();
|
|
105
125
|
expandAll();
|
|
@@ -130,6 +150,18 @@ export const WfoSubscriptionDetailTree = ({
|
|
|
130
150
|
|
|
131
151
|
const headerHeight = 265; // The height of the header part of the page that needs to be subtracted from 100vh to fit the page
|
|
132
152
|
|
|
153
|
+
const handleOptionChange = (changedOption: EuiSelectableOption) => {
|
|
154
|
+
if (changedOption.data?.ids === undefined) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const { checked, data } = changedOption;
|
|
159
|
+
const shouldAddIds = checked === EUI_OPTION_CHECKED_STATE_ON;
|
|
160
|
+
|
|
161
|
+
expandAll();
|
|
162
|
+
return shouldAddIds ? selectIds(data.ids) : deselectIds(data.ids);
|
|
163
|
+
};
|
|
164
|
+
|
|
133
165
|
return (
|
|
134
166
|
<EuiFlexGroup
|
|
135
167
|
css={{
|
|
@@ -158,15 +190,33 @@ export const WfoSubscriptionDetailTree = ({
|
|
|
158
190
|
</EuiText>
|
|
159
191
|
</EuiFlexItem>
|
|
160
192
|
<EuiFlexItem grow={false}>
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
193
|
+
<EuiFlexGroup>
|
|
194
|
+
<WfoTextAnchor
|
|
195
|
+
text={t(
|
|
196
|
+
selectedIds.length ===
|
|
197
|
+
productBlockInstances.length
|
|
198
|
+
? 'hideAll'
|
|
199
|
+
: 'showAll',
|
|
200
|
+
)}
|
|
201
|
+
onClick={toggleShowAll}
|
|
202
|
+
/>
|
|
203
|
+
<WfoButtonComboBox
|
|
204
|
+
options={mapProductBlockInstancesToEuiSelectableOptions(
|
|
205
|
+
productBlockInstances,
|
|
206
|
+
)}
|
|
207
|
+
onOptionChange={handleOptionChange}
|
|
208
|
+
title={t('selectByNameTitle')}
|
|
209
|
+
>
|
|
210
|
+
{(togglePopover) => (
|
|
211
|
+
<WfoTextAnchor
|
|
212
|
+
text={t(
|
|
213
|
+
'selectByNameButtonText',
|
|
214
|
+
)}
|
|
215
|
+
onClick={togglePopover}
|
|
216
|
+
/>
|
|
217
|
+
)}
|
|
218
|
+
</WfoButtonComboBox>
|
|
219
|
+
</EuiFlexGroup>
|
|
170
220
|
</EuiFlexItem>
|
|
171
221
|
</EuiFlexGroup>
|
|
172
222
|
</EuiFlexItem>
|
|
@@ -3,6 +3,7 @@ import { EuiThemeComputed } from '@elastic/eui';
|
|
|
3
3
|
import {
|
|
4
4
|
FieldValue,
|
|
5
5
|
ProcessStatus,
|
|
6
|
+
ProductBlockInstance,
|
|
6
7
|
SubscriptionAction,
|
|
7
8
|
SubscriptionDetailProcess,
|
|
8
9
|
WorkflowTarget,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
getProductBlockTitle,
|
|
16
17
|
getWorkflowTargetColor,
|
|
17
18
|
getWorkflowTargetIconContent,
|
|
19
|
+
mapProductBlockInstancesToEuiSelectableOptions,
|
|
18
20
|
} from './utils';
|
|
19
21
|
|
|
20
22
|
describe('getFieldFromProductBlockInstanceValues()', () => {
|
|
@@ -347,3 +349,122 @@ describe('getLatestTaskDate', () => {
|
|
|
347
349
|
expect(lastTaskDate).toBe('2021-04-01T00:00:00Z');
|
|
348
350
|
});
|
|
349
351
|
});
|
|
352
|
+
|
|
353
|
+
describe('mapProductBlockInstancesToEuiSelectableOptions', () => {
|
|
354
|
+
const baseProductBlockInstance: Omit<
|
|
355
|
+
ProductBlockInstance,
|
|
356
|
+
'id' | 'productBlockInstanceValues'
|
|
357
|
+
> = {
|
|
358
|
+
subscriptionInstanceId: '',
|
|
359
|
+
parent: null,
|
|
360
|
+
inUseByRelations: [],
|
|
361
|
+
subscription: {
|
|
362
|
+
subscriptionId: '',
|
|
363
|
+
description: '',
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
it('maps product block instances to EuiSelectableOptions', () => {
|
|
368
|
+
// Given
|
|
369
|
+
const productBlockInstances: ProductBlockInstance[] = [
|
|
370
|
+
{
|
|
371
|
+
...baseProductBlockInstance,
|
|
372
|
+
id: 0,
|
|
373
|
+
productBlockInstanceValues: [
|
|
374
|
+
{
|
|
375
|
+
field: 'name',
|
|
376
|
+
value: 'test1',
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
...baseProductBlockInstance,
|
|
382
|
+
id: 1,
|
|
383
|
+
productBlockInstanceValues: [
|
|
384
|
+
{
|
|
385
|
+
field: 'name',
|
|
386
|
+
value: 'test2',
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
},
|
|
390
|
+
];
|
|
391
|
+
|
|
392
|
+
// When
|
|
393
|
+
const result = mapProductBlockInstancesToEuiSelectableOptions(
|
|
394
|
+
productBlockInstances,
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
// Then
|
|
398
|
+
expect(result).toEqual([
|
|
399
|
+
{
|
|
400
|
+
label: 'test1',
|
|
401
|
+
data: {
|
|
402
|
+
ids: [0],
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
label: 'test2',
|
|
407
|
+
data: {
|
|
408
|
+
ids: [1],
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
]);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('maps product block instances to EuiSelectableOptions and groups ids by name', () => {
|
|
415
|
+
// Given
|
|
416
|
+
const productBlockInstances: ProductBlockInstance[] = [
|
|
417
|
+
{
|
|
418
|
+
...baseProductBlockInstance,
|
|
419
|
+
id: 0,
|
|
420
|
+
productBlockInstanceValues: [
|
|
421
|
+
{
|
|
422
|
+
field: 'name',
|
|
423
|
+
value: 'test1',
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
...baseProductBlockInstance,
|
|
429
|
+
id: 1,
|
|
430
|
+
productBlockInstanceValues: [
|
|
431
|
+
{
|
|
432
|
+
field: 'name',
|
|
433
|
+
value: 'test2',
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
...baseProductBlockInstance,
|
|
439
|
+
id: 2,
|
|
440
|
+
productBlockInstanceValues: [
|
|
441
|
+
{
|
|
442
|
+
field: 'name',
|
|
443
|
+
value: 'test1',
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
},
|
|
447
|
+
];
|
|
448
|
+
|
|
449
|
+
// When
|
|
450
|
+
const result = mapProductBlockInstancesToEuiSelectableOptions(
|
|
451
|
+
productBlockInstances,
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
// Then
|
|
455
|
+
expect(result).toEqual([
|
|
456
|
+
{
|
|
457
|
+
label: 'test1',
|
|
458
|
+
data: {
|
|
459
|
+
ids: [0, 2],
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
label: 'test2',
|
|
464
|
+
data: {
|
|
465
|
+
ids: [1],
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
]);
|
|
469
|
+
});
|
|
470
|
+
});
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { TranslationValues } from 'next-intl';
|
|
2
2
|
|
|
3
|
-
import { EuiThemeComputed } from '@elastic/eui';
|
|
3
|
+
import { EuiSelectableOption, EuiThemeComputed } from '@elastic/eui';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
FieldValue,
|
|
7
7
|
ProcessStatus,
|
|
8
|
+
ProductBlockDefinition,
|
|
9
|
+
ProductBlockInstance,
|
|
8
10
|
SortOrder,
|
|
9
11
|
SubscriptionAction,
|
|
10
12
|
SubscriptionDetailProcess,
|
|
11
13
|
WorkflowTarget,
|
|
12
14
|
} from '@/types';
|
|
13
15
|
|
|
16
|
+
const PRODUCT_BLOCK_NAME_FIELD: keyof ProductBlockDefinition = 'name';
|
|
17
|
+
|
|
14
18
|
export enum SubscriptionDetailTab {
|
|
15
19
|
GENERAL_TAB = 'general',
|
|
16
20
|
SERVICE_CONFIGURATION_TAB = 'service-configuration',
|
|
@@ -68,6 +72,7 @@ export const getWorkflowTargetColor = (
|
|
|
68
72
|
case WorkflowTarget.MODIFY:
|
|
69
73
|
return theme.colors.primaryText;
|
|
70
74
|
case WorkflowTarget.SYSTEM:
|
|
75
|
+
case WorkflowTarget.VALIDATE:
|
|
71
76
|
return theme.colors.warning;
|
|
72
77
|
case WorkflowTarget.TERMINATE:
|
|
73
78
|
return theme.colors.danger;
|
|
@@ -84,6 +89,7 @@ export const getWorkflowTargetIconContent = (
|
|
|
84
89
|
case WorkflowTarget.CREATE:
|
|
85
90
|
return 'C';
|
|
86
91
|
case WorkflowTarget.SYSTEM:
|
|
92
|
+
case WorkflowTarget.VALIDATE:
|
|
87
93
|
return 'T';
|
|
88
94
|
case WorkflowTarget.TERMINATE:
|
|
89
95
|
return 'X';
|
|
@@ -93,9 +99,9 @@ export const getWorkflowTargetIconContent = (
|
|
|
93
99
|
};
|
|
94
100
|
|
|
95
101
|
export const getLastUncompletedProcess = (
|
|
96
|
-
processes
|
|
102
|
+
processes?: SubscriptionDetailProcess[],
|
|
97
103
|
): SubscriptionDetailProcess | undefined => {
|
|
98
|
-
if (processes.length === 0) {
|
|
104
|
+
if (!processes || processes.length === 0) {
|
|
99
105
|
return;
|
|
100
106
|
}
|
|
101
107
|
|
|
@@ -107,13 +113,13 @@ export const getLastUncompletedProcess = (
|
|
|
107
113
|
return dateB.getTime() - dateA.getTime();
|
|
108
114
|
});
|
|
109
115
|
|
|
110
|
-
return uncompletedProcesses.length > 0
|
|
116
|
+
return (uncompletedProcesses && uncompletedProcesses.length) > 0
|
|
111
117
|
? uncompletedProcesses[0]
|
|
112
118
|
: undefined;
|
|
113
119
|
};
|
|
114
120
|
|
|
115
|
-
export const getLatestTaskDate = (processes
|
|
116
|
-
if (processes.length === 0) {
|
|
121
|
+
export const getLatestTaskDate = (processes?: SubscriptionDetailProcess[]) => {
|
|
122
|
+
if (!processes || processes.length === 0) {
|
|
117
123
|
return '';
|
|
118
124
|
}
|
|
119
125
|
|
|
@@ -143,3 +149,33 @@ export const sortProcessesByDate = (
|
|
|
143
149
|
}
|
|
144
150
|
});
|
|
145
151
|
};
|
|
152
|
+
|
|
153
|
+
export const mapProductBlockInstancesToEuiSelectableOptions = (
|
|
154
|
+
productBlockInstances: ProductBlockInstance[],
|
|
155
|
+
): EuiSelectableOption[] => {
|
|
156
|
+
const items2Map = productBlockInstances.reduce((acc, curr) => {
|
|
157
|
+
const name = getFieldFromProductBlockInstanceValues(
|
|
158
|
+
curr.productBlockInstanceValues,
|
|
159
|
+
PRODUCT_BLOCK_NAME_FIELD,
|
|
160
|
+
).toString();
|
|
161
|
+
|
|
162
|
+
if (!name) {
|
|
163
|
+
console.error('Name field is missing', curr);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (acc.has(name)) {
|
|
167
|
+
acc.get(name)?.push(curr.id);
|
|
168
|
+
} else {
|
|
169
|
+
acc.set(name, [curr.id]);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return acc;
|
|
173
|
+
}, new Map<string, number[]>());
|
|
174
|
+
|
|
175
|
+
return Array.from(items2Map).map(([label, ids]) => ({
|
|
176
|
+
label,
|
|
177
|
+
data: {
|
|
178
|
+
ids,
|
|
179
|
+
},
|
|
180
|
+
}));
|
|
181
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const ORCHESTRATOR_UI_LIBRARY_VERSION = '
|
|
1
|
+
export const ORCHESTRATOR_UI_LIBRARY_VERSION = '5.1.0';
|
|
@@ -12,6 +12,8 @@ export type TreeContextType = {
|
|
|
12
12
|
collapseAll: () => void;
|
|
13
13
|
resetSelection: () => void;
|
|
14
14
|
selectAll: () => void;
|
|
15
|
+
selectIds: (ids: number[]) => void;
|
|
16
|
+
deselectIds: (ids: number[]) => void;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
export const TreeContext = React.createContext<TreeContextType | null>(null);
|
|
@@ -39,6 +41,18 @@ export const TreeProvider: React.FC<TreeProviderProps> = ({ children }) => {
|
|
|
39
41
|
setSelectedIds(Array.from(Array(depths.length).keys()));
|
|
40
42
|
};
|
|
41
43
|
|
|
44
|
+
const selectIds = (ids: number[]) => {
|
|
45
|
+
setSelectedIds((prevSelectedIds) =>
|
|
46
|
+
Array.from(new Set([...prevSelectedIds, ...ids])),
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const deselectIds = (ids: number[]) => {
|
|
51
|
+
setSelectedIds((prevSelectedIds) =>
|
|
52
|
+
prevSelectedIds.filter((id) => !ids.includes(id)),
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
42
56
|
const expandAll = () => {
|
|
43
57
|
setExpandedIds(Array.from(Array(depths.length).keys()));
|
|
44
58
|
};
|
|
@@ -96,6 +110,8 @@ export const TreeProvider: React.FC<TreeProviderProps> = ({ children }) => {
|
|
|
96
110
|
collapseAll,
|
|
97
111
|
resetSelection,
|
|
98
112
|
selectAll,
|
|
113
|
+
selectIds,
|
|
114
|
+
deselectIds,
|
|
99
115
|
}}
|
|
100
116
|
>
|
|
101
117
|
{children}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { WfoIconProps } from './WfoIconProps';
|
|
4
|
+
|
|
5
|
+
export const WfoExclamationTriangle: FC<WfoIconProps> = ({
|
|
6
|
+
width = 24,
|
|
7
|
+
height = 24,
|
|
8
|
+
color = '#000000',
|
|
9
|
+
}) => (
|
|
10
|
+
<svg
|
|
11
|
+
width={width}
|
|
12
|
+
height={height}
|
|
13
|
+
viewBox="0 0 24 24"
|
|
14
|
+
version="1.1"
|
|
15
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
16
|
+
color={color}
|
|
17
|
+
>
|
|
18
|
+
<title>icon/exclamation-circle</title>
|
|
19
|
+
<g id="Symbols" strokeWidth="1.5" fill="currentColor">
|
|
20
|
+
<g id="icon/exclamation-triangle" fill="currentColor">
|
|
21
|
+
<path
|
|
22
|
+
fillRule="evenodd"
|
|
23
|
+
d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495ZM10 5a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 10 5Zm0 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
|
|
24
|
+
clipRule="evenodd"
|
|
25
|
+
/>
|
|
26
|
+
</g>
|
|
27
|
+
</g>
|
|
28
|
+
</svg>
|
|
29
|
+
);
|
package/src/icons/index.ts
CHANGED
package/src/messages/en-GB.json
CHANGED
|
@@ -363,6 +363,8 @@
|
|
|
363
363
|
},
|
|
364
364
|
"showAll": "Show all",
|
|
365
365
|
"hideAll": "Hide all",
|
|
366
|
+
"selectByNameTitle": "Select product blocks by name",
|
|
367
|
+
"selectByNameButtonText": "Select by name",
|
|
366
368
|
"see": "See",
|
|
367
369
|
"setInSync": "Set in Sync",
|
|
368
370
|
"setInSyncQuestion": "Are you sure you want to do this? You're about to force a subscription in sync. When it's clear why the subscription is out of sync this could enable you to start or finish a change on this subscription. When you're in doubt, please consult the network automators first as running workflows on subscriptions that are not in sync can potentially do great harm to the network.",
|
package/src/messages/nl-NL.json
CHANGED
|
@@ -360,6 +360,8 @@
|
|
|
360
360
|
},
|
|
361
361
|
"showAll": "Toon alles",
|
|
362
362
|
"hideAll": "Verberg alles",
|
|
363
|
+
"selectByNameTitle": "Selecteer product blocks op naam",
|
|
364
|
+
"selectByNameButtonText": "Selecteer op naam",
|
|
363
365
|
"see": "Bekijk",
|
|
364
366
|
"setInSync": "Set in Sync",
|
|
365
367
|
"setInSyncQuestion": "Weet je zeker dat je de subscription in-sync wilt zetten? Je gaat een subscription geforceerd in-sync zetten. Alleen als je zeker weet wat de reden is van out-of-sync kun je de actie uitvoeren, zodat je daarna wijzigingen kunt doorvoeren op deze subscription. Bij twijfel - check eerst bij de network automators of het in sync zetten van de subscription mogelijk schadelijke gevolgen kan hebben op het netwerk.",
|