@orchestrator-ui/orchestrator-ui-components 8.6.0 → 8.7.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 +16 -13
- package/CHANGELOG.md +11 -0
- package/dist/index.d.ts +106 -8
- package/dist/index.js +2540 -2078
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/src/components/WfoPydanticForm/fields/WfoCron.spec.ts +88 -0
- package/src/components/WfoPydanticForm/fields/WfoCron.tsx +233 -0
- package/src/components/WfoPydanticForm/fields/index.ts +1 -0
- package/src/components/WfoPydanticForm/fields/styles.ts +92 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchResultItem.tsx +2 -1
- package/src/components/WfoSearchPage/utils.ts +37 -1
- package/src/components/WfoSubscription/WfoInUseByRelations.tsx +6 -2
- package/src/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActionExpandableMenuItem.tsx +19 -14
- package/src/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActions.tsx +13 -1
- package/src/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActionsMenuItem.tsx +3 -0
- package/src/components/WfoSubscription/WfoSubscriptionProductBlock/WfoSubscriptionProductBlock.tsx +1 -1
- package/src/components/WfoSubscriptionsList/WfoSubscriptionsList.tsx +5 -0
- package/src/components/WfoSubscriptionsList/subscriptionResultMappers.ts +3 -1
- package/src/components/WfoTable/WfoAdvancedTable/WfoAdvancedTable.tsx +2 -12
- package/src/components/WfoTable/WfoStructuredSearchTable/WfoExpandingSearchRow.tsx +21 -14
- package/src/components/WfoTable/WfoStructuredSearchTable/WfoFieldSelector.tsx +27 -1
- package/src/components/WfoTable/WfoStructuredSearchTable/WfoFilterBuilder.tsx +97 -91
- package/src/components/WfoTable/WfoStructuredSearchTable/WfoOperatorSelector.tsx +32 -7
- package/src/components/WfoTable/WfoStructuredSearchTable/WfoRuleGroup.tsx +5 -4
- package/src/components/WfoTable/WfoStructuredSearchTable/WfoStructuredSearchTable.tsx +32 -30
- package/src/components/WfoTable/WfoStructuredSearchTable/WfoValueEditor.tsx +41 -11
- package/src/components/WfoTable/WfoStructuredSearchTable/styles.ts +22 -5
- package/src/components/WfoTable/WfoTable/WfoTable.tsx +1 -0
- package/src/components/WfoTable/WfoTable/WfoTableDataRows.tsx +16 -4
- package/src/components/WfoTable/WfoTable/styles.ts +8 -0
- package/src/components/WfoTable/WfoTable/utils.spec.ts +62 -1
- package/src/components/WfoTable/WfoTable/utils.ts +4 -1
- package/src/components/WfoTable/WfoTableSettingsModal/WfoTableSettingsModal.spec.ts +45 -0
- package/src/components/WfoTable/WfoTableSettingsModal/index.ts +1 -0
- package/src/components/WfoTable/WfoTableSettingsModal/utils.ts +17 -0
- package/src/configuration/version.ts +1 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useGetPydanticFormsConfig.tsx +12 -0
- package/src/hooks/useLanguageCode.ts +11 -0
- package/src/messages/en-GB.json +19 -0
- package/src/messages/nl-NL.json +19 -0
- package/src/pages/WfoSearchPocPage.tsx +14 -5
- package/src/rtk/endpoints/subscriptionList.ts +1 -0
- package/src/theme/baseStyles/formFieldsBaseStyle.ts +0 -1
- package/src/types/search.ts +1 -1
- package/src/types/types.ts +1 -1
- package/src/utils/getDefaultTableConfig.ts +6 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/integer.spec.ts +20 -0
- package/src/utils/integer.ts +3 -0
|
@@ -137,23 +137,53 @@ const DatePicker = ({ handleOnChange, rangeIndex, value: currentValue }: WfoRang
|
|
|
137
137
|
);
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
-
export const WfoValueEditor = ({
|
|
140
|
+
export const WfoValueEditor = ({
|
|
141
|
+
field: fieldName,
|
|
142
|
+
context,
|
|
143
|
+
handleOnChange,
|
|
144
|
+
operator,
|
|
145
|
+
value,
|
|
146
|
+
className,
|
|
147
|
+
}: ValueEditorProps) => {
|
|
141
148
|
const fieldPathInfoMap = context?.fieldPathInfoMap;
|
|
142
149
|
|
|
143
150
|
const fieldInfo = fieldPathInfoMap && fieldPathInfoMap.has(fieldName) ? fieldPathInfoMap.get(fieldName) : undefined;
|
|
144
151
|
const uiFieldType = fieldInfo?.ui_types[0] || UiFieldType.text;
|
|
145
152
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
const getEditor = () => {
|
|
154
|
+
if (uiFieldType === UiFieldType.boolean) {
|
|
155
|
+
return <BooleanEditor handleOnChange={handleOnChange} value={value} />;
|
|
156
|
+
}
|
|
149
157
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
if (uiFieldType === UiFieldType.datetime) {
|
|
159
|
+
return <WfoRangeEditor handleOnChange={handleOnChange} operator={operator} Element={DatePicker} value={value} />;
|
|
160
|
+
}
|
|
153
161
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
162
|
+
if (uiFieldType === UiFieldType.number) {
|
|
163
|
+
return (
|
|
164
|
+
<WfoRangeEditor handleOnChange={handleOnChange} operator={operator} Element={NumberEditor} value={value} />
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return <TextEditor handleOnChange={handleOnChange} value={value} />;
|
|
169
|
+
};
|
|
157
170
|
|
|
158
|
-
|
|
171
|
+
const handleWrapperKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {
|
|
172
|
+
if (event.key !== 'Enter') return;
|
|
173
|
+
// Restrict to the editor inputs: the editors' own Enter handlers have already run
|
|
174
|
+
// (bubble phase) and committed the value via blur, so the search sees it. Enter on
|
|
175
|
+
// the boolean buttons means "select" — its click fires only after this keydown, so
|
|
176
|
+
// searching there would use the pre-toggle value.
|
|
177
|
+
if (!(event.target instanceof HTMLInputElement)) return;
|
|
178
|
+
context?.onValueEditorEnter?.();
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// react-querybuilder delivers the standard `rule-value` class via this prop; the wrapper
|
|
182
|
+
// makes it queryable in the DOM (WfoFieldSelector relies on it to move focus here).
|
|
183
|
+
// `display: contents` keeps the children direct participants in the rule's flex row.
|
|
184
|
+
return (
|
|
185
|
+
<div className={className} style={{ display: 'contents' }} onKeyDown={handleWrapperKeyDown}>
|
|
186
|
+
{getEditor()}
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
159
189
|
};
|
|
@@ -107,11 +107,12 @@ export const getWfoStructuredSearchTableStyles = ({ theme }: WfoThemeHelpers) =>
|
|
|
107
107
|
});
|
|
108
108
|
const expandingRowBodyStyles = css({
|
|
109
109
|
display: 'flex',
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
justifyContent: 'space-between',
|
|
111
|
+
// Size to the visible scroll viewport (minus the td padding) instead of
|
|
112
|
+
// the full table width, and keep it pinned there on horizontal scroll
|
|
113
|
+
position: 'sticky',
|
|
114
|
+
left: theme.base / 2,
|
|
115
|
+
width: `calc(100cqw - ${theme.base}px)`,
|
|
115
116
|
});
|
|
116
117
|
|
|
117
118
|
const addRulePlusStyles = css({
|
|
@@ -161,6 +162,19 @@ export const getWfoStructuredSearchTableStyles = ({ theme }: WfoThemeHelpers) =>
|
|
|
161
162
|
},
|
|
162
163
|
});
|
|
163
164
|
|
|
165
|
+
const hideExpandedRowStyle = css({
|
|
166
|
+
display: 'none',
|
|
167
|
+
});
|
|
168
|
+
const dotStyles = css({
|
|
169
|
+
padding: theme.base / 4,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const expandingRowFieldStyles = css({
|
|
173
|
+
display: 'flex',
|
|
174
|
+
padding: theme.base / 4,
|
|
175
|
+
alignItems: 'center',
|
|
176
|
+
});
|
|
177
|
+
|
|
164
178
|
return {
|
|
165
179
|
toggleButtonStyles,
|
|
166
180
|
queryBuilderContainerStyles,
|
|
@@ -178,5 +192,8 @@ export const getWfoStructuredSearchTableStyles = ({ theme }: WfoThemeHelpers) =>
|
|
|
178
192
|
addGroupStyles,
|
|
179
193
|
inlineCombinatorStyles,
|
|
180
194
|
ruleGroupBodyGridStyles,
|
|
195
|
+
hideExpandedRowStyle,
|
|
196
|
+
expandingRowFieldStyles,
|
|
197
|
+
dotStyles,
|
|
181
198
|
};
|
|
182
199
|
};
|
|
@@ -82,6 +82,7 @@ export type WfoTableProps<T extends object> = {
|
|
|
82
82
|
rowExpandingConfiguration?: {
|
|
83
83
|
uniqueRowId: keyof WfoTableColumnConfig<T>;
|
|
84
84
|
uniqueRowIdToExpandedRowMap: Record<string, ReactNode>;
|
|
85
|
+
shouldOnlyShowOnHover?: boolean;
|
|
85
86
|
};
|
|
86
87
|
pagination?: Pagination;
|
|
87
88
|
overrideHeader?: (
|
|
@@ -27,8 +27,15 @@ export const WfoTableDataRows = <T extends object>({
|
|
|
27
27
|
onRowClick,
|
|
28
28
|
className,
|
|
29
29
|
}: WfoTableDataRowsProps<T>) => {
|
|
30
|
-
const {
|
|
31
|
-
|
|
30
|
+
const {
|
|
31
|
+
cellStyle,
|
|
32
|
+
cellContentStyle,
|
|
33
|
+
rowStyle,
|
|
34
|
+
dataRowStyle,
|
|
35
|
+
clickableStyle,
|
|
36
|
+
setWidth,
|
|
37
|
+
toggleExpandedRowOnHoverStyle,
|
|
38
|
+
} = useWithOrchestratorTheme(getWfoTableStyles);
|
|
32
39
|
|
|
33
40
|
const sortedVisibleColumns = getSortedVisibleColumns(columnConfig, columnOrder, hiddenColumns);
|
|
34
41
|
|
|
@@ -37,8 +44,13 @@ export const WfoTableDataRows = <T extends object>({
|
|
|
37
44
|
{data.map((row, index) => (
|
|
38
45
|
<Fragment key={`table-data-row-${index}`}>
|
|
39
46
|
<tr
|
|
40
|
-
className={`${className} ${DATA_ROW_CLASS}`}
|
|
41
|
-
css={[
|
|
47
|
+
className={className ? `${className} ${DATA_ROW_CLASS}` : DATA_ROW_CLASS}
|
|
48
|
+
css={[
|
|
49
|
+
rowStyle,
|
|
50
|
+
dataRowStyle,
|
|
51
|
+
onRowClick && clickableStyle,
|
|
52
|
+
rowExpandingConfiguration?.shouldOnlyShowOnHover && toggleExpandedRowOnHoverStyle,
|
|
53
|
+
]}
|
|
42
54
|
onClick={() => onRowClick?.(row)}
|
|
43
55
|
>
|
|
44
56
|
{sortedVisibleColumns.map(([key, columnConfig]) => {
|
|
@@ -31,6 +31,9 @@ export const getWfoTableStyles = ({ theme, isDarkModeActive }: WfoThemeHelpers)
|
|
|
31
31
|
|
|
32
32
|
const tableContainerStyle = css({
|
|
33
33
|
overflowX: 'auto',
|
|
34
|
+
// Query container so descendants (e.g. expanded rows) can size to the
|
|
35
|
+
// visible table width with cqw units instead of the full table width
|
|
36
|
+
containerType: 'inline-size',
|
|
34
37
|
});
|
|
35
38
|
|
|
36
39
|
const tableStyle = css({
|
|
@@ -157,6 +160,10 @@ export const getWfoTableStyles = ({ theme, isDarkModeActive }: WfoThemeHelpers)
|
|
|
157
160
|
'.eui-xScroll': { display: 'flex', justifyContent: 'flex-start' },
|
|
158
161
|
});
|
|
159
162
|
|
|
163
|
+
const toggleExpandedRowOnHoverStyle = css({
|
|
164
|
+
'&:hover + tr, &:focus-within + tr': { display: 'table-row' },
|
|
165
|
+
});
|
|
166
|
+
|
|
160
167
|
return {
|
|
161
168
|
tableContainerStyle,
|
|
162
169
|
tableStyle,
|
|
@@ -174,5 +181,6 @@ export const getWfoTableStyles = ({ theme, isDarkModeActive }: WfoThemeHelpers)
|
|
|
174
181
|
dragAndDropStyle,
|
|
175
182
|
paginationStyle,
|
|
176
183
|
setWidth,
|
|
184
|
+
toggleExpandedRowOnHoverStyle,
|
|
177
185
|
};
|
|
178
186
|
};
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { TableColumnKeys } from '@/components';
|
|
1
2
|
import { ColumnType, WfoTableColumnConfig } from '@/components/WfoTable/WfoTable/WfoTable';
|
|
2
3
|
import { SortOrder } from '@/types';
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getSortedVisibleColumns,
|
|
7
|
+
getUpdatedSortOrder,
|
|
8
|
+
mapSortableAndFilterableValuesToTableColumnConfig,
|
|
9
|
+
} from './utils';
|
|
5
10
|
|
|
6
11
|
type TestObject = {
|
|
7
12
|
name: string;
|
|
@@ -19,6 +24,21 @@ const tableColumnConfig: WfoTableColumnConfig<TestObject> = {
|
|
|
19
24
|
},
|
|
20
25
|
};
|
|
21
26
|
|
|
27
|
+
const tableColumnConfigWithControlColumn: WfoTableColumnConfig<TestObject> = {
|
|
28
|
+
actions: {
|
|
29
|
+
columnType: ColumnType.CONTROL,
|
|
30
|
+
renderControl: () => null,
|
|
31
|
+
},
|
|
32
|
+
name: {
|
|
33
|
+
columnType: ColumnType.DATA,
|
|
34
|
+
label: 'testName',
|
|
35
|
+
},
|
|
36
|
+
age: {
|
|
37
|
+
columnType: ColumnType.DATA,
|
|
38
|
+
label: 'testAge',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
22
42
|
describe('utils', () => {
|
|
23
43
|
describe('getUpdatedSortOrder()', () => {
|
|
24
44
|
it('returns SortOrder.DESC if the currentSortOrder is SortOrder.ASC', () => {
|
|
@@ -38,6 +58,47 @@ describe('utils', () => {
|
|
|
38
58
|
});
|
|
39
59
|
});
|
|
40
60
|
|
|
61
|
+
describe('getSortedVisibleColumns()', () => {
|
|
62
|
+
it('keeps a control column visible even when its key is present in hiddenColumns', () => {
|
|
63
|
+
// Given a stale hiddenColumns still listing the control column (e.g. from localStorage)
|
|
64
|
+
const hiddenColumns = ['actions'] as unknown as TableColumnKeys<TestObject>;
|
|
65
|
+
|
|
66
|
+
// When
|
|
67
|
+
const result = getSortedVisibleColumns(tableColumnConfigWithControlColumn, [], hiddenColumns);
|
|
68
|
+
|
|
69
|
+
// Then the control column is still rendered
|
|
70
|
+
const visibleKeys = result.map(([key]) => key);
|
|
71
|
+
expect(visibleKeys).toContain('actions');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('hides data columns that are listed in hiddenColumns', () => {
|
|
75
|
+
// Given
|
|
76
|
+
const hiddenColumns: TableColumnKeys<TestObject> = ['age'];
|
|
77
|
+
|
|
78
|
+
// When
|
|
79
|
+
const result = getSortedVisibleColumns(tableColumnConfigWithControlColumn, [], hiddenColumns);
|
|
80
|
+
|
|
81
|
+
// Then
|
|
82
|
+
const visibleKeys = result.map(([key]) => key);
|
|
83
|
+
expect(visibleKeys).not.toContain('age');
|
|
84
|
+
expect(visibleKeys).toContain('name');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('keeps the control column visible while still hiding a hidden data column', () => {
|
|
88
|
+
// Given both a control key and a data key are marked hidden
|
|
89
|
+
const hiddenColumns = ['actions', 'age'] as unknown as TableColumnKeys<TestObject>;
|
|
90
|
+
|
|
91
|
+
// When
|
|
92
|
+
const result = getSortedVisibleColumns(tableColumnConfigWithControlColumn, [], hiddenColumns);
|
|
93
|
+
|
|
94
|
+
// Then only the data column is hidden; the control column stays visible
|
|
95
|
+
const visibleKeys = result.map(([key]) => key);
|
|
96
|
+
expect(visibleKeys).toContain('actions');
|
|
97
|
+
expect(visibleKeys).toContain('name');
|
|
98
|
+
expect(visibleKeys).not.toContain('age');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
41
102
|
describe('mapSortableAndFilterableValuesToTableColumnConfig()', () => {
|
|
42
103
|
it('sets the sortable and filterable properties for the columnConfig object to true when the colum name is specified in the list', () => {
|
|
43
104
|
// Given
|
|
@@ -25,7 +25,10 @@ export const getSortedVisibleColumns = <T extends object>(
|
|
|
25
25
|
),
|
|
26
26
|
);
|
|
27
27
|
|
|
28
|
-
return tableHeadersEntries.filter(
|
|
28
|
+
return tableHeadersEntries.filter(
|
|
29
|
+
([columnId, columnConfig]) =>
|
|
30
|
+
columnConfig.columnType === ColumnType.CONTROL || !hiddenColumns.includes(columnId as keyof T),
|
|
31
|
+
);
|
|
29
32
|
};
|
|
30
33
|
|
|
31
34
|
export const getUpdatedSortOrder = (currentSortOrder?: SortOrder) =>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { TableColumnKeys } from '@/components';
|
|
2
|
+
import { ColumnType, WfoTableColumnConfig } from '@/components/WfoTable/WfoTable/WfoTable';
|
|
3
|
+
import { getTableSettingsColumns } from '@/components/WfoTable/WfoTableSettingsModal/utils';
|
|
4
|
+
|
|
5
|
+
type TestObject = {
|
|
6
|
+
name: string;
|
|
7
|
+
age: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const columnConfig: WfoTableColumnConfig<TestObject> = {
|
|
11
|
+
actions: {
|
|
12
|
+
columnType: ColumnType.CONTROL,
|
|
13
|
+
renderControl: () => null,
|
|
14
|
+
},
|
|
15
|
+
name: {
|
|
16
|
+
columnType: ColumnType.DATA,
|
|
17
|
+
label: 'Name',
|
|
18
|
+
},
|
|
19
|
+
age: {
|
|
20
|
+
columnType: ColumnType.DATA,
|
|
21
|
+
label: 'Age',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
describe('getTableSettingsColumns()', () => {
|
|
26
|
+
it('excludes control columns and keeps only data columns as toggles', () => {
|
|
27
|
+
const result = getTableSettingsColumns(columnConfig, []);
|
|
28
|
+
expect(result.map((column) => column.field)).toEqual(['name', 'age']);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('maps each data column label to name and marks all visible when nothing is hidden', () => {
|
|
32
|
+
const result = getTableSettingsColumns(columnConfig, []);
|
|
33
|
+
expect(result).toEqual([
|
|
34
|
+
{ field: 'name', name: 'Name', isVisible: true },
|
|
35
|
+
{ field: 'age', name: 'Age', isVisible: true },
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('marks a data column as not visible when it is present in hiddenColumns', () => {
|
|
40
|
+
const hiddenColumns: TableColumnKeys<TestObject> = ['age'];
|
|
41
|
+
const result = getTableSettingsColumns(columnConfig, hiddenColumns);
|
|
42
|
+
expect(result.find((column) => column.field === 'age')?.isVisible).toBe(false);
|
|
43
|
+
expect(result.find((column) => column.field === 'name')?.isVisible).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ColumnType, TableColumnKeys, TableSettingsColumnConfig, WfoTableColumnConfig } from '@/components';
|
|
2
|
+
|
|
3
|
+
export const getTableSettingsColumns = <T extends object>(
|
|
4
|
+
columnConfig: WfoTableColumnConfig<T>,
|
|
5
|
+
hiddenColumns: TableColumnKeys<T>,
|
|
6
|
+
): TableSettingsColumnConfig<T>[] =>
|
|
7
|
+
Object.entries(columnConfig)
|
|
8
|
+
.filter(([, columnItemConfig]) => columnItemConfig.columnType === ColumnType.DATA)
|
|
9
|
+
.map(([key, { label }]): TableSettingsColumnConfig<T> => {
|
|
10
|
+
const field = key as keyof T;
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
field,
|
|
14
|
+
name: label,
|
|
15
|
+
isVisible: hiddenColumns.indexOf(field) === -1,
|
|
16
|
+
};
|
|
17
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const ORCHESTRATOR_UI_LIBRARY_VERSION = '8.
|
|
1
|
+
export const ORCHESTRATOR_UI_LIBRARY_VERSION = '8.7.0';
|
package/src/hooks/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
WfoArrayField,
|
|
24
24
|
WfoCallout,
|
|
25
25
|
WfoCheckbox,
|
|
26
|
+
WfoCron,
|
|
26
27
|
WfoDivider,
|
|
27
28
|
WfoDropdown,
|
|
28
29
|
WfoInteger,
|
|
@@ -181,6 +182,17 @@ const useGetComponentMatcherExtender = (): ComponentMatcherExtender => {
|
|
|
181
182
|
return type === PydanticFormFieldType.STRING && format === ('callout' as PydanticFormFieldFormat);
|
|
182
183
|
},
|
|
183
184
|
},
|
|
185
|
+
{
|
|
186
|
+
id: 'cron',
|
|
187
|
+
ElementMatch: {
|
|
188
|
+
isControlledElement: true,
|
|
189
|
+
Element: WfoCron,
|
|
190
|
+
},
|
|
191
|
+
matcher: (field) => {
|
|
192
|
+
const { type, schema } = field;
|
|
193
|
+
return type === PydanticFormFieldType.STRING && schema.title === 'Cron';
|
|
194
|
+
},
|
|
195
|
+
},
|
|
184
196
|
{
|
|
185
197
|
id: 'markdownField',
|
|
186
198
|
ElementMatch: {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useLocale } from 'next-intl';
|
|
2
|
+
|
|
3
|
+
export const useLanguageCode = () => {
|
|
4
|
+
const locale = useLocale();
|
|
5
|
+
if (!locale) return 'en';
|
|
6
|
+
try {
|
|
7
|
+
return typeof Intl !== 'undefined' && 'Locale' in Intl ? new Intl.Locale(locale).language : locale.split(/[-_]/)[0];
|
|
8
|
+
} catch {
|
|
9
|
+
return locale.split(/[-_]/)[0] || 'en';
|
|
10
|
+
}
|
|
11
|
+
};
|
package/src/messages/en-GB.json
CHANGED
|
@@ -107,6 +107,24 @@
|
|
|
107
107
|
"contactPersonName": {
|
|
108
108
|
"placeholder": "Search and add contact persons..."
|
|
109
109
|
},
|
|
110
|
+
"cron": {
|
|
111
|
+
"second": "second",
|
|
112
|
+
"minute": "minute",
|
|
113
|
+
"hour": "hour",
|
|
114
|
+
"dayOfMonth": "day (month)",
|
|
115
|
+
"month": "month",
|
|
116
|
+
"dayOfWeek": "day (week)",
|
|
117
|
+
"anyValue": "any value",
|
|
118
|
+
"valueListSeparator": "value list separator",
|
|
119
|
+
"rangeOfValues": "range of values",
|
|
120
|
+
"stepValues": "step values",
|
|
121
|
+
"allowedValues": "allowed values",
|
|
122
|
+
"alternativeSingleValues": "alternative single values",
|
|
123
|
+
"sundayNonStandard": "sunday (non-standard)",
|
|
124
|
+
"invalidExpression": "Invalid cron expression: {error}",
|
|
125
|
+
"nextOccurrences": "Next: {dates}",
|
|
126
|
+
"possibleValues": "Possible values for this field"
|
|
127
|
+
},
|
|
110
128
|
"ipvAnyNetworkField": {
|
|
111
129
|
"manuallySelectedPrefix": "Manually selected prefix"
|
|
112
130
|
},
|
|
@@ -347,6 +365,7 @@
|
|
|
347
365
|
"endDate": "End date",
|
|
348
366
|
"metadata": "Metadata",
|
|
349
367
|
"note": "Note",
|
|
368
|
+
"customerId": "Customer ID",
|
|
350
369
|
"customerFullname": "Customer",
|
|
351
370
|
"customerShortcode": "Customer abbr",
|
|
352
371
|
"actions": "Actions"
|
package/src/messages/nl-NL.json
CHANGED
|
@@ -108,6 +108,24 @@
|
|
|
108
108
|
"contactPersonName": {
|
|
109
109
|
"placeholder": "Zoek en voeg contactpersonen toe..."
|
|
110
110
|
},
|
|
111
|
+
"cron": {
|
|
112
|
+
"second": "seconde",
|
|
113
|
+
"minute": "minuut",
|
|
114
|
+
"hour": "uur",
|
|
115
|
+
"dayOfMonth": "dag (maand)",
|
|
116
|
+
"month": "maand",
|
|
117
|
+
"dayOfWeek": "dag (week)",
|
|
118
|
+
"anyValue": "elke waarde",
|
|
119
|
+
"valueListSeparator": "scheidingsteken voor waardenlijst",
|
|
120
|
+
"rangeOfValues": "bereik van waarden",
|
|
121
|
+
"stepValues": "stapwaarden",
|
|
122
|
+
"allowedValues": "toegestane waarden",
|
|
123
|
+
"alternativeSingleValues": "alternatieve losse waarden",
|
|
124
|
+
"sundayNonStandard": "zondag (niet-standaard)",
|
|
125
|
+
"invalidExpression": "Ongeldige cron-expressie: {error}",
|
|
126
|
+
"nextOccurrences": "Volgende: {dates}",
|
|
127
|
+
"possibleValues": "Mogelijke waarden voor dit veld"
|
|
128
|
+
},
|
|
111
129
|
"ipvAnyNetworkField": {
|
|
112
130
|
"manuallySelectedPrefix": "Manually selected prefix"
|
|
113
131
|
},
|
|
@@ -347,6 +365,7 @@
|
|
|
347
365
|
"endDate": "Einddatum",
|
|
348
366
|
"note": "Notitie",
|
|
349
367
|
"metadata": "Metadata",
|
|
368
|
+
"customerId": "Klant ID",
|
|
350
369
|
"customerFullname": "Klant",
|
|
351
370
|
"customerShortcode": "Klantafkorting",
|
|
352
371
|
"actions": "Acties"
|
|
@@ -9,7 +9,7 @@ import { StringParam, useQueryParam, withDefault } from 'use-query-params';
|
|
|
9
9
|
|
|
10
10
|
import { EuiSpacer } from '@elastic/eui';
|
|
11
11
|
|
|
12
|
-
import { SearchParams,
|
|
12
|
+
import { SearchParams, addStatusFilterFromTab, removeTabStatusMatchingFields } from '@/components';
|
|
13
13
|
import {
|
|
14
14
|
DEFAULT_PAGE_SIZE,
|
|
15
15
|
StoredTableConfig,
|
|
@@ -56,6 +56,7 @@ const getDataFromResponse = <T extends object>(
|
|
|
56
56
|
data: PaginatedSearchResults,
|
|
57
57
|
resultColumToPropertyMap: ResultColumToPropertyMap<T>,
|
|
58
58
|
uniqueRowId: keyof T,
|
|
59
|
+
selectedTab: WfoSubscriptionListTab,
|
|
59
60
|
): {
|
|
60
61
|
items: T[];
|
|
61
62
|
rowExpandingConfiguration?: WfoTableProps<T>['rowExpandingConfiguration'];
|
|
@@ -72,18 +73,23 @@ const getDataFromResponse = <T extends object>(
|
|
|
72
73
|
const rowExpandingConfiguration: WfoTableProps<T>['rowExpandingConfiguration'] = {
|
|
73
74
|
uniqueRowId: uniqueRowId as keyof WfoTableColumnConfig<T>,
|
|
74
75
|
uniqueRowIdToExpandedRowMap: searchResult.reduce(
|
|
75
|
-
(rowMap, { response_columns, score, perfect_match,
|
|
76
|
+
(rowMap, { response_columns, score, perfect_match, matching_fields }) => {
|
|
76
77
|
const idColumnInResponseColumn = getKeyByValueFromMap<T>(resultColumToPropertyMap, uniqueRowId);
|
|
77
78
|
const rowId = response_columns[idColumnInResponseColumn];
|
|
78
79
|
if (rowId) {
|
|
79
80
|
rowMap[rowId] = (
|
|
80
|
-
<WfoExpandingSearchRow
|
|
81
|
+
<WfoExpandingSearchRow
|
|
82
|
+
score={score}
|
|
83
|
+
matchingFields={removeTabStatusMatchingFields(matching_fields, selectedTab, EntityKind.SUBSCRIPTION)}
|
|
84
|
+
perfectMatch={perfect_match}
|
|
85
|
+
/>
|
|
81
86
|
);
|
|
82
87
|
}
|
|
83
88
|
return rowMap;
|
|
84
89
|
},
|
|
85
90
|
{} as Record<string, ReactNode>,
|
|
86
91
|
),
|
|
92
|
+
shouldOnlyShowOnHover: true,
|
|
87
93
|
};
|
|
88
94
|
|
|
89
95
|
const items: T[] = responseColumns.map((responseColumn) => {
|
|
@@ -203,7 +209,7 @@ export const WfoSearchPocPage = () => {
|
|
|
203
209
|
// Cursor is stripped from the RTK cache key, so a cursor change appends to the accumulated
|
|
204
210
|
// result set instead of creating a new cache entry — see search endpoint's merge() logic.
|
|
205
211
|
const searchPayload: SearchPayload = useMemo(() => {
|
|
206
|
-
const filters =
|
|
212
|
+
const filters = addStatusFilterFromTab(committedRuleGroup, selectedTab);
|
|
207
213
|
const order_by = {
|
|
208
214
|
element: getKeyByValueFromMap(resultColumToPropertyMap, dataSorting.field),
|
|
209
215
|
direction: dataSorting.sortOrder.toLowerCase(),
|
|
@@ -453,7 +459,9 @@ export const WfoSearchPocPage = () => {
|
|
|
453
459
|
};
|
|
454
460
|
|
|
455
461
|
const { items: subscriptionListItems, rowExpandingConfiguration } =
|
|
456
|
-
data ?
|
|
462
|
+
data ?
|
|
463
|
+
getDataFromResponse<SubscriptionListItem>(data, resultColumToPropertyMap, 'subscriptionId', selectedTab)
|
|
464
|
+
: { items: [] };
|
|
457
465
|
|
|
458
466
|
const totalItems = getTotalItemsFromResponse(data);
|
|
459
467
|
const hasNextPage = data?.page_info?.has_next_page ?? false;
|
|
@@ -465,6 +473,7 @@ export const WfoSearchPocPage = () => {
|
|
|
465
473
|
exportResult,
|
|
466
474
|
resultColumToPropertyMap,
|
|
467
475
|
'subscriptionId',
|
|
476
|
+
selectedTab,
|
|
468
477
|
);
|
|
469
478
|
if (!exportItems.length) {
|
|
470
479
|
return;
|
|
@@ -10,7 +10,6 @@ export const getFormFieldsBaseStyle = ({ theme }: WfoThemeHelpers) => {
|
|
|
10
10
|
backgroundColor: theme.colors.backgroundBaseNeutral,
|
|
11
11
|
boxShadow: `0 0 0 1px ${theme.colors.primary}`,
|
|
12
12
|
},
|
|
13
|
-
// boxShadow: `0 0 0 1px ${theme.colors.borderBaseSubdued} !important`,
|
|
14
13
|
});
|
|
15
14
|
|
|
16
15
|
return {
|
package/src/types/search.ts
CHANGED
package/src/types/types.ts
CHANGED
|
@@ -471,7 +471,7 @@ export type Subscription = {
|
|
|
471
471
|
status: SubscriptionStatus;
|
|
472
472
|
product: Pick<ProductDefinition, 'name' | 'tag' | 'productType'>;
|
|
473
473
|
productBlockInstances: ProductBlockInstance[];
|
|
474
|
-
customer: Pick<Customer, 'fullname' | 'shortcode'>;
|
|
474
|
+
customer: Pick<Customer, 'customerId' | 'fullname' | 'shortcode'>;
|
|
475
475
|
metadata: object;
|
|
476
476
|
};
|
|
477
477
|
|
|
@@ -95,7 +95,12 @@ export const getDefaultTableConfig = <T>(storageKey: string) => {
|
|
|
95
95
|
return getTableConfig<T>(completedTasksColumns as (keyof T)[]);
|
|
96
96
|
}
|
|
97
97
|
case SUBSCRIPTIONS_TABLE_LOCAL_STORAGE_KEY: {
|
|
98
|
-
const subscriptionColumns: (keyof SubscriptionListItem)[] = [
|
|
98
|
+
const subscriptionColumns: (keyof SubscriptionListItem)[] = [
|
|
99
|
+
'productName',
|
|
100
|
+
'customerId',
|
|
101
|
+
'customerFullname',
|
|
102
|
+
'metadata',
|
|
103
|
+
];
|
|
99
104
|
return getTableConfig<T>(subscriptionColumns as (keyof T)[]);
|
|
100
105
|
}
|
|
101
106
|
default:
|
package/src/utils/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './getProductNamesFromProcess';
|
|
|
10
10
|
export * from './getQueryVariablesForExport';
|
|
11
11
|
export * from './getStatusBadgeColor';
|
|
12
12
|
export * from './getTypedFieldFromObject';
|
|
13
|
+
export * from './integer';
|
|
13
14
|
export * from './onlyUnique';
|
|
14
15
|
export * from './optionalArray';
|
|
15
16
|
export * from './resultFlattener';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { toPercentage } from './integer';
|
|
2
|
+
|
|
3
|
+
describe('toPercentage()', () => {
|
|
4
|
+
it('converts a fraction to a percentage string with one decimal', () => {
|
|
5
|
+
expect(toPercentage(0.5)).toEqual('50.0%');
|
|
6
|
+
});
|
|
7
|
+
it('converts 1 to 100.0%', () => {
|
|
8
|
+
expect(toPercentage(1)).toEqual('100.0%');
|
|
9
|
+
});
|
|
10
|
+
it('converts 0 to 0.0%', () => {
|
|
11
|
+
expect(toPercentage(0)).toEqual('0.0%');
|
|
12
|
+
});
|
|
13
|
+
it('rounds to one decimal place', () => {
|
|
14
|
+
expect(toPercentage(0.12345)).toEqual('12.3%');
|
|
15
|
+
expect(toPercentage(0.6789)).toEqual('67.9%');
|
|
16
|
+
});
|
|
17
|
+
it('handles fractions greater than 1', () => {
|
|
18
|
+
expect(toPercentage(1.5)).toEqual('150.0%');
|
|
19
|
+
});
|
|
20
|
+
});
|