@antscorp/antsomi-ui 1.3.5-beta.490 → 1.3.5-beta.491
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/es/components/molecules/MatchAnySelect/MatchesAnySelect.js +42 -19
- package/es/components/molecules/MatchAnySelect/types.d.ts +4 -0
- package/es/components/organism/DataTable/components/Filter/AddFilterButton.js +2 -2
- package/es/components/organism/DataTable/components/Filter/FilterCondition/index.d.ts +1 -0
- package/es/components/organism/DataTable/components/Filter/FilterCondition/index.js +2 -1
- package/es/components/organism/DataTable/components/Filter/FilterConditionList.js +2 -2
- package/es/components/organism/DataTable/hooks/useAddFilterButton.d.ts +1 -0
- package/es/components/organism/DataTable/hooks/useAddFilterButton.js +3 -2
- package/es/components/organism/DataTable/hooks/useDataTable.js +2 -1
- package/es/components/organism/DataTable/hooks/useDataTableListing/types.d.ts +1 -0
- package/es/components/organism/DataTable/hooks/useDataTableListing/useDataTableListing.js +48 -16
- package/es/components/organism/DataTable/types/filter.d.ts +2 -0
- package/es/components/organism/DataTable/utils/filter.d.ts +2 -1
- package/es/components/organism/DataTable/utils/filter.js +2 -2
- package/es/components/template/TemplateListing/hooks/useTemplateListing.js +1 -0
- package/es/constants/queries.d.ts +1 -0
- package/es/constants/queries.js +1 -0
- package/es/queries/DataTable/index.d.ts +6 -1
- package/es/queries/DataTable/index.js +6 -2
- package/es/tests/DataTableTest.js +18 -19
- package/package.json +1 -1
|
@@ -13,7 +13,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
13
13
|
// Libraries
|
|
14
14
|
import { ConfigProvider } from 'antd';
|
|
15
15
|
import { uniqBy } from 'lodash';
|
|
16
|
-
import React, { useMemo, useState } from 'react';
|
|
16
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
17
17
|
// Components
|
|
18
18
|
import { EmptyData, Select } from '@antscorp/antsomi-ui/es/components/molecules';
|
|
19
19
|
import { Button, Flex, Icon, Input, Popover, Scrollbars, Spin, Typography, Tooltip, } from '@antscorp/antsomi-ui/es/components/atoms';
|
|
@@ -27,7 +27,7 @@ import { translations } from '@antscorp/antsomi-ui/es/locales/translations';
|
|
|
27
27
|
import { MATCHES_ANY_THEME } from './constants';
|
|
28
28
|
import { globalToken } from '@antscorp/antsomi-ui/es/constants';
|
|
29
29
|
// Hooks
|
|
30
|
-
import { useDeepCompareEffect, useDeepCompareMemo } from '@antscorp/antsomi-ui/es/hooks';
|
|
30
|
+
import { useDeepCompareEffect, useDeepCompareMemo, useIntersectionObserver, } from '@antscorp/antsomi-ui/es/hooks';
|
|
31
31
|
// Utils
|
|
32
32
|
import { flatTree, recursiveSearchItems } from '@antscorp/antsomi-ui/es/utils';
|
|
33
33
|
import { DataIcon } from '../../icons';
|
|
@@ -37,16 +37,27 @@ const initialState = {
|
|
|
37
37
|
const matchesAnyInitialState = {
|
|
38
38
|
searchValue: '',
|
|
39
39
|
selectedItems: [],
|
|
40
|
+
isShowLoadMoreEl: false,
|
|
40
41
|
};
|
|
41
42
|
const { t } = i18nInstance;
|
|
42
43
|
const { Text } = Typography;
|
|
43
44
|
export const MatchesAny = props => {
|
|
44
45
|
// Props
|
|
45
|
-
const { objectName, loading = false, showExtendValue = true, items, onApply = () => { }, onCancel = () => { } } = props, restOfProps = __rest(props, ["objectName", "loading", "showExtendValue", "items", "onApply", "onCancel"]);
|
|
46
|
+
const { objectName, loading = false, showExtendValue = true, items, onApply = () => { }, onCancel = () => { }, onLoadMore = () => { } } = props, restOfProps = __rest(props, ["objectName", "loading", "showExtendValue", "items", "onApply", "onCancel", "onLoadMore"]);
|
|
46
47
|
// State
|
|
47
48
|
const [state, setState] = useState(matchesAnyInitialState);
|
|
48
49
|
// Variables
|
|
49
|
-
const { searchValue, selectedItems } = state;
|
|
50
|
+
const { searchValue, selectedItems, isShowLoadMoreEl } = state;
|
|
51
|
+
// Refs
|
|
52
|
+
const { ref: loadMoreRef } = useIntersectionObserver({
|
|
53
|
+
threshold: 0,
|
|
54
|
+
initialIsIntersecting: false,
|
|
55
|
+
onChange(isIntersecting) {
|
|
56
|
+
if (isIntersecting) {
|
|
57
|
+
onLoadMore();
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
});
|
|
50
61
|
// Effects
|
|
51
62
|
/**
|
|
52
63
|
* Updates the `selectedItems` state when the `selectedItems` prop changes.
|
|
@@ -54,6 +65,12 @@ export const MatchesAny = props => {
|
|
|
54
65
|
useDeepCompareEffect(() => {
|
|
55
66
|
setState(prev => (Object.assign(Object.assign({}, prev), { selectedItems: props.selectedItems })));
|
|
56
67
|
}, [props.selectedItems]);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
// Delay for show load more el
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
setState(prev => (Object.assign(Object.assign({}, prev), { isShowLoadMoreEl: true })));
|
|
72
|
+
}, 500);
|
|
73
|
+
}, []);
|
|
57
74
|
// Handlers
|
|
58
75
|
/**
|
|
59
76
|
* Adds an item and, if applicable, its leaf descendants to the list of selected items.
|
|
@@ -147,7 +164,10 @@ export const MatchesAny = props => {
|
|
|
147
164
|
(selectedItem.isExtendValue && selectedItem.title === item.title)))
|
|
148
165
|
: selectedItems === null || selectedItems === void 0 ? void 0 : selectedItems.some(selectedItem => selectedItem.key === item.key ||
|
|
149
166
|
(selectedItem.isExtendValue && selectedItem.title === item.title));
|
|
150
|
-
return Object.assign(Object.assign(Object.assign({}, item), { title: renderItemNodeTitle({
|
|
167
|
+
return Object.assign(Object.assign(Object.assign({}, item), { title: renderItemNodeTitle({
|
|
168
|
+
item,
|
|
169
|
+
onSelectItem,
|
|
170
|
+
}), disabled: isSelected }), (item.children && {
|
|
151
171
|
children: serializeTreeData(item.children),
|
|
152
172
|
}));
|
|
153
173
|
});
|
|
@@ -155,8 +175,10 @@ export const MatchesAny = props => {
|
|
|
155
175
|
return { treeData, matchedParents };
|
|
156
176
|
}, [items, searchValue, selectedItems]);
|
|
157
177
|
const selectedTreeData = useMemo(() => {
|
|
158
|
-
|
|
159
|
-
const
|
|
178
|
+
var _a;
|
|
179
|
+
const notExtendValueSelectedItems = (selectedItems === null || selectedItems === void 0 ? void 0 : selectedItems.filter(item => !item.isExtendValue)) || [];
|
|
180
|
+
const extendValueSelectedItems = (selectedItems === null || selectedItems === void 0 ? void 0 : selectedItems.filter(item => item.isExtendValue)) || [];
|
|
181
|
+
const lazyLoadSelectedItems = (selectedItems === null || selectedItems === void 0 ? void 0 : selectedItems.filter(selectedItem => !flatTree(items || [], 'children').some(item => item.key === selectedItem.key))) || [];
|
|
160
182
|
const serializeTreeData = (list) => {
|
|
161
183
|
var _a;
|
|
162
184
|
return ((_a = list
|
|
@@ -176,7 +198,7 @@ export const MatchesAny = props => {
|
|
|
176
198
|
children: serializeTreeData(item.children),
|
|
177
199
|
}))))) || [];
|
|
178
200
|
};
|
|
179
|
-
return serializeTreeData(items || []).concat((extendValueSelectedItems === null ||
|
|
201
|
+
return serializeTreeData(items || []).concat(((_a = [...lazyLoadSelectedItems, ...extendValueSelectedItems]) === null || _a === void 0 ? void 0 : _a.map(item => (Object.assign(Object.assign({}, item), { title: renderItemNodeTitle({ item, onRemoveItem }) })))) || []);
|
|
180
202
|
}, [items, selectedItems]);
|
|
181
203
|
const isDisableRemoveAll = useDeepCompareMemo(() => !(selectedTreeData === null || selectedTreeData === void 0 ? void 0 : selectedTreeData.length), [selectedTreeData]);
|
|
182
204
|
const isDisableSelectAll = useDeepCompareMemo(() => {
|
|
@@ -195,21 +217,22 @@ export const MatchesAny = props => {
|
|
|
195
217
|
React.createElement(Text, { strong: true }, `${objectName} (${flatTree(items, 'children').filter(item => !item.children).length})`),
|
|
196
218
|
React.createElement(TextButton, { disabled: isDisableSelectAll, onClick: onSelectAll }, t(translations.global.selectAll).toString())),
|
|
197
219
|
React.createElement(Spin, { spinning: loading },
|
|
198
|
-
React.createElement(Scrollbars, { style: { height: '100%' } }, (treeData === null || treeData === void 0 ? void 0 : treeData.length) ? (React.createElement(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
220
|
+
React.createElement(Scrollbars, { style: { height: '100%' } }, (treeData === null || treeData === void 0 ? void 0 : treeData.length) ? (React.createElement(React.Fragment, null,
|
|
221
|
+
React.createElement(StyledTree, { key: searchValue, defaultExpandedKeys: matchedParents.map(item => item.key), selectable: false, treeData: treeData, switcherIcon: props => (React.createElement(Icon, { type: "icon-ants-caret-down", style: {
|
|
222
|
+
transform: props.expanded ? 'rotate(0)' : 'rotate(-90deg)',
|
|
223
|
+
transition: 'transform 0.3s ease',
|
|
224
|
+
}, color: globalToken === null || globalToken === void 0 ? void 0 : globalToken.bw8 })) }),
|
|
225
|
+
isShowLoadMoreEl && React.createElement("div", { ref: loadMoreRef }))) : (React.createElement(EmptyData, { showIcon: false, description: t(translations.global.noResultsMatchesKeyWord).toString() })))))));
|
|
202
226
|
};
|
|
203
227
|
const renderSelectedList = () => (React.createElement("div", { className: "matches-any__section" },
|
|
204
228
|
React.createElement(Flex, { className: "matches-any__header", justify: "space-between" },
|
|
205
229
|
React.createElement(Text, { strong: true }, `${t(translations.global.selected)} (${(selectedItems === null || selectedItems === void 0 ? void 0 : selectedItems.length) || 0})`),
|
|
206
230
|
React.createElement(TextButton, { disabled: isDisableRemoveAll, onClick: onRemoveAll }, t(translations.global.removeAll).toString())),
|
|
207
231
|
React.createElement("div", { className: "matches-any__body" },
|
|
208
|
-
React.createElement(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}, color: globalToken === null || globalToken === void 0 ? void 0 : globalToken.bw8 })) })))))));
|
|
232
|
+
React.createElement(Scrollbars, { style: { height: '100%' } }, !(selectedItems === null || selectedItems === void 0 ? void 0 : selectedItems.length) ? (React.createElement(EmptyData, { icon: React.createElement(DataIcon, { color: globalToken === null || globalToken === void 0 ? void 0 : globalToken.bw5, width: 48, height: 48 }), description: t(translations.global.selectItemsFromList).toString() })) : (React.createElement(StyledTree, { selectable: false, treeData: selectedTreeData, switcherIcon: props => (React.createElement(Icon, { type: "icon-ants-caret-down", style: {
|
|
233
|
+
transform: props.expanded ? 'rotate(0)' : 'rotate(-90deg)',
|
|
234
|
+
transition: 'transform 0.3s ease',
|
|
235
|
+
}, color: globalToken === null || globalToken === void 0 ? void 0 : globalToken.bw8 })) }))))));
|
|
213
236
|
// Handlers
|
|
214
237
|
const onApplyExtendValue = (extendValues) => {
|
|
215
238
|
// Filter out values that are already present in selectedItems
|
|
@@ -239,7 +262,7 @@ export const MatchesAny = props => {
|
|
|
239
262
|
};
|
|
240
263
|
export const MatchesAnySelect = props => {
|
|
241
264
|
var _a, _b, _c;
|
|
242
|
-
const { placeholder = 'Select an item', dropdownStyle, objectName, selectedItems, items, loading, popupClassName, onChange = () => { } } = props, restProps = __rest(props, ["placeholder", "dropdownStyle", "objectName", "selectedItems", "items", "loading", "popupClassName", "onChange"]);
|
|
265
|
+
const { placeholder = 'Select an item', dropdownStyle, objectName, selectedItems, items, loading, popupClassName, onChange = () => { }, onLoadMore } = props, restProps = __rest(props, ["placeholder", "dropdownStyle", "objectName", "selectedItems", "items", "loading", "popupClassName", "onChange", "onLoadMore"]);
|
|
243
266
|
// State
|
|
244
267
|
const [state, setState] = useState(initialState);
|
|
245
268
|
// Variables
|
|
@@ -252,7 +275,7 @@ export const MatchesAnySelect = props => {
|
|
|
252
275
|
onChange(selectedItems);
|
|
253
276
|
setState(prev => (Object.assign(Object.assign({}, prev), { isOpenPopover: false })));
|
|
254
277
|
};
|
|
255
|
-
return (React.createElement(Popover, { open: isOpenPopover, arrow: false, placement: "bottomLeft", content: React.createElement(MatchesAny, { className: popupClassName, items: items, selectedItems: selectedItems, loading: loading, objectName: objectName, onApply: onApplyMatchesAny, onCancel: () => setState(prev => (Object.assign(Object.assign({}, prev), { isOpenPopover: false }))) }), overlayStyle: { width: 700 }, overlayInnerStyle: {
|
|
278
|
+
return (React.createElement(Popover, { open: isOpenPopover, arrow: false, placement: "bottomLeft", content: React.createElement(MatchesAny, { className: popupClassName, items: items, selectedItems: selectedItems, loading: loading, objectName: objectName, onApply: onApplyMatchesAny, onCancel: () => setState(prev => (Object.assign(Object.assign({}, prev), { isOpenPopover: false }))), onLoadMore: onLoadMore }), overlayStyle: { width: 700 }, overlayInnerStyle: {
|
|
256
279
|
padding: 0,
|
|
257
280
|
}, trigger: ['click'], destroyTooltipOnHide: true, onOpenChange: () => setState(prev => (Object.assign(Object.assign({}, prev), { isOpenPopover: !isOpenPopover }))) },
|
|
258
281
|
React.createElement(Tooltip, { mouseEnterDelay: 0.5, title: `${selectedItems ? selectedItems === null || selectedItems === void 0 ? void 0 : selectedItems.map(item => item.title).join(', ') : ''}` },
|
|
@@ -20,6 +20,8 @@ export interface MatchesAnySelectProps extends SelectProps {
|
|
|
20
20
|
selectedItems?: MatchesAnyItem[];
|
|
21
21
|
/** Callback function that is called when the selected items change. */
|
|
22
22
|
onChange?: (selectedItems: MatchesAnyItem[]) => void;
|
|
23
|
+
/** Callback function that is called when the user scroll */
|
|
24
|
+
onLoadMore?: () => void;
|
|
23
25
|
}
|
|
24
26
|
export interface MatchesAnyProps extends Omit<FlexProps, 'children'> {
|
|
25
27
|
/** The name of the object, corresponding to the `objectName` property in `MatchesAnySelectProps`. */
|
|
@@ -40,4 +42,6 @@ export interface MatchesAnyProps extends Omit<FlexProps, 'children'> {
|
|
|
40
42
|
onApply?: (selectedItems: MatchesAnyItem[]) => void;
|
|
41
43
|
/** Callback function that is called when the cancel action is triggered. */
|
|
42
44
|
onCancel?: () => void;
|
|
45
|
+
/** Callback function that is called when the user scroll */
|
|
46
|
+
onLoadMore?: MatchesAnySelectProps['onLoadMore'];
|
|
43
47
|
}
|
|
@@ -13,7 +13,7 @@ import { translations } from '@antscorp/antsomi-ui/es/locales/translations';
|
|
|
13
13
|
import { EXCEPTION_OFF_FILTER_CLASS } from '../../constants/filter';
|
|
14
14
|
export const AddFilterButton = memo(() => {
|
|
15
15
|
const { t } = i18nInstance;
|
|
16
|
-
const { openPopover, selectedFilterMetric, matchesAny, onSelectFilterMetric, onApplyFilterMetricCondition, onClickAddFilter, onChangeFilterCondition, setState: setAddFilterConditionState, } = useAddFilterCondition();
|
|
16
|
+
const { openPopover, selectedFilterMetric, matchesAny, onSelectFilterMetric, onApplyFilterMetricCondition, onClickAddFilter, onChangeFilterCondition, onMatchesAnyLoadMore, setState: setAddFilterConditionState, } = useAddFilterCondition();
|
|
17
17
|
return (React.createElement(Popover, { open: openPopover, content: React.createElement("div", { className: EXCEPTION_OFF_FILTER_CLASS }, selectedFilterMetric ? (React.createElement(FilterCondition, { filterMetric: selectedFilterMetric, matchesAny: matchesAny, onCancel: () => onSelectFilterMetric(undefined), onApply: ({ operator, value }) => {
|
|
18
18
|
onApplyFilterMetricCondition({
|
|
19
19
|
filterMetric: selectedFilterMetric,
|
|
@@ -22,7 +22,7 @@ export const AddFilterButton = memo(() => {
|
|
|
22
22
|
});
|
|
23
23
|
onSelectFilterMetric(undefined);
|
|
24
24
|
setAddFilterConditionState(prev => (Object.assign(Object.assign({}, prev), { openPopover: false })));
|
|
25
|
-
}, onChange: condition => {
|
|
25
|
+
}, onLoadMore: () => onMatchesAnyLoadMore(selectedFilterMetric.id), onChange: condition => {
|
|
26
26
|
const { id, dataType } = selectedFilterMetric;
|
|
27
27
|
const { operator, value } = condition || {};
|
|
28
28
|
onChangeFilterCondition({
|
|
@@ -11,6 +11,7 @@ interface FilterConditionProps {
|
|
|
11
11
|
onCancel?: () => void;
|
|
12
12
|
onApply?: (values: TFormType) => void;
|
|
13
13
|
onChange?: (values: TFormType) => void;
|
|
14
|
+
onLoadMore?: () => void;
|
|
14
15
|
}
|
|
15
16
|
export declare const FilterCondition: React.FC<FilterConditionProps>;
|
|
16
17
|
export {};
|
|
@@ -11,7 +11,7 @@ import { renderValueField } from '../../../utils';
|
|
|
11
11
|
const { BETWEEN, EXISTS, NOT_EXISTS } = OPERATORS_OPTION;
|
|
12
12
|
export const FilterCondition = props => {
|
|
13
13
|
// Props
|
|
14
|
-
const { filterMetric, filterCondition, matchesAny, onChange, onCancel, onApply } = props;
|
|
14
|
+
const { filterMetric, filterCondition, matchesAny, onChange, onCancel, onApply, onLoadMore } = props;
|
|
15
15
|
// Hooks
|
|
16
16
|
const [form] = Form.useForm();
|
|
17
17
|
// Form
|
|
@@ -96,6 +96,7 @@ export const FilterCondition = props => {
|
|
|
96
96
|
operator,
|
|
97
97
|
value,
|
|
98
98
|
matchesAny,
|
|
99
|
+
onLoadMore,
|
|
99
100
|
onChangeValue: value => {
|
|
100
101
|
form.setFieldsValue({
|
|
101
102
|
value,
|
|
@@ -22,7 +22,7 @@ import { translations } from '@antscorp/antsomi-ui/es/locales/translations';
|
|
|
22
22
|
const { t } = i18nInstance;
|
|
23
23
|
export const FilterConditionList = memo(() => {
|
|
24
24
|
// Store
|
|
25
|
-
const { filterMetrics, filters, isFilterActive, matchesAny, onChangeFilters = () => { }, onChangeFilterCondition = () => { }, } = useDataTableContext(store => store.filter);
|
|
25
|
+
const { filterMetrics, filters, isFilterActive, matchesAny, onChangeFilters = () => { }, onChangeFilterCondition = () => { }, onMatchesAnyLoadMore = () => { }, } = useDataTableContext(store => store.filter);
|
|
26
26
|
const setFilter = useDataTableContext(store => store.setFilter);
|
|
27
27
|
// State
|
|
28
28
|
/* Reserve for handle popover open per filter condition */
|
|
@@ -80,7 +80,7 @@ export const FilterConditionList = memo(() => {
|
|
|
80
80
|
value: condition.value,
|
|
81
81
|
dataType: metric.dataType,
|
|
82
82
|
});
|
|
83
|
-
}, onCancel: () => handleTogglePopover(index, false) })), trigger: ['click'], overlayInnerStyle: { width: 300, padding: 0, overflow: 'visible' }, arrow: false, placement: "bottomLeft", onOpenChange: open => handleTogglePopover(index, open) },
|
|
83
|
+
}, onLoadMore: () => onMatchesAnyLoadMore(metric.id), onCancel: () => handleTogglePopover(index, false) })), trigger: ['click'], overlayInnerStyle: { width: 300, padding: 0, overflow: 'visible' }, arrow: false, placement: "bottomLeft", onOpenChange: open => handleTogglePopover(index, open) },
|
|
84
84
|
React.createElement(FilterItem, { gap: 5, onClick: () => setFilter({
|
|
85
85
|
isFilterActive: true,
|
|
86
86
|
}), name: (metric === null || metric === void 0 ? void 0 : metric.name) || column.toString() || '', operator: operator, value: value, onClickRemove: () => onClickRemoveFilter(index) })));
|
|
@@ -21,6 +21,7 @@ export declare const useAddFilterCondition: () => {
|
|
|
21
21
|
onApplyFilterMetricCondition: (values: OnApplyFilterMetricConditionValue) => void;
|
|
22
22
|
onClickAddFilter: () => void;
|
|
23
23
|
onChangeFilterCondition: (filterItem: import("../types").FilterItem) => void;
|
|
24
|
+
onMatchesAnyLoadMore: (metricId?: string | number | undefined) => void;
|
|
24
25
|
openPopover?: boolean | undefined;
|
|
25
26
|
selectedFilterMetric?: FilterMetricItem | undefined;
|
|
26
27
|
};
|
|
@@ -10,7 +10,7 @@ export const useAddFilterCondition = () => {
|
|
|
10
10
|
selectedFilterMetric: undefined,
|
|
11
11
|
});
|
|
12
12
|
// Store
|
|
13
|
-
const { filters, matchesAny, onChangeFilters = () => { }, onChangeFilterCondition = () => { }, } = useDataTableContext(store => store.filter);
|
|
13
|
+
const { filters, matchesAny, onChangeFilters = () => { }, onChangeFilterCondition = () => { }, onMatchesAnyLoadMore = () => { }, } = useDataTableContext(store => store.filter);
|
|
14
14
|
const setFilter = useDataTableContext(store => store.setFilter);
|
|
15
15
|
// Handlers
|
|
16
16
|
const onSelectFilterMetric = useCallback((filterMetric) => {
|
|
@@ -54,5 +54,6 @@ export const useAddFilterCondition = () => {
|
|
|
54
54
|
onSelectFilterMetric,
|
|
55
55
|
onApplyFilterMetricCondition,
|
|
56
56
|
onClickAddFilter,
|
|
57
|
-
onChangeFilterCondition
|
|
57
|
+
onChangeFilterCondition,
|
|
58
|
+
onMatchesAnyLoadMore });
|
|
58
59
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Context
|
|
2
2
|
import { useDataTableContext } from '../contexts/DataTableContext';
|
|
3
3
|
import { useDeepCompareEffect } from '@antscorp/antsomi-ui/es/hooks';
|
|
4
|
+
import { omit } from 'lodash';
|
|
4
5
|
export const useDataTable = (props) => {
|
|
5
6
|
// Props
|
|
6
7
|
const { table, toolbar, filter, pagination } = props || {};
|
|
@@ -25,7 +26,7 @@ export const useDataTable = (props) => {
|
|
|
25
26
|
/* Handle update filter */
|
|
26
27
|
useDeepCompareEffect(() => {
|
|
27
28
|
if (filter) {
|
|
28
|
-
setFilter(filter);
|
|
29
|
+
setFilter(omit(filter, 'onMatchesAnyLoadMore'));
|
|
29
30
|
}
|
|
30
31
|
}, [setFilter, filter]);
|
|
31
32
|
/* Handle update pagination */
|
|
@@ -14,6 +14,7 @@ export type TApiGlobal = AxiosRequestConfig<any> & {
|
|
|
14
14
|
type TMatchesAnyParams = (filterItem?: FilterItem) => any | any;
|
|
15
15
|
type TMatchesAnyProps = {
|
|
16
16
|
formatData: (response: any) => MatchesAnyItem[];
|
|
17
|
+
total?: (response: any) => number | number;
|
|
17
18
|
};
|
|
18
19
|
export type TConfig<TTableType = any> = {
|
|
19
20
|
env?: TEnv;
|
|
@@ -21,7 +21,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
21
21
|
// Libraries
|
|
22
22
|
import React, { useCallback, useState } from 'react';
|
|
23
23
|
import { Link } from 'react-router-dom';
|
|
24
|
-
import { isEmpty, pick } from 'lodash';
|
|
24
|
+
import { flatMap, isEmpty, pick } from 'lodash';
|
|
25
25
|
import { useQueryClient } from '@tanstack/react-query';
|
|
26
26
|
// Types
|
|
27
27
|
import { useAppConfigContext } from '@antscorp/antsomi-ui/es/providers';
|
|
@@ -33,7 +33,7 @@ import { MODIFY_COLUMN_DISABLE_EDITABLE, MODIFY_COLUMN_DISABLE_REMOVE, SAVED_FIL
|
|
|
33
33
|
import { DEFAULT_CELL_EMPTY, DEFAULT_TOGGLE_WIDTH, SORT_MAP } from '../../constants';
|
|
34
34
|
// Hooks
|
|
35
35
|
import { useDeepCompareEffect, useDeepCompareMemo } from '@antscorp/antsomi-ui/es/hooks';
|
|
36
|
-
import { useCreateModifyColumn, useDeleteModifyColumn, useDeleteSavedFilter, useGetColumnMetrics, useGetFilterMetricList,
|
|
36
|
+
import { useCreateModifyColumn, useDeleteModifyColumn, useDeleteSavedFilter, useGetColumnMetrics, useGetFilterMetricList, useGetInfiniteMatchesAnyList, useGetModifyColumnList, useGetSavedFilterList, useGetSearchListing, useGetTableListing, useSaveFilter, useUpdateFilter, useUpdateModifyColumn, } from '@antscorp/antsomi-ui/es/queries';
|
|
37
37
|
// Utils
|
|
38
38
|
import { flatTree, mapResponseSearchToGeneral, parseJSONFromLocalStorage, } from '@antscorp/antsomi-ui/es/utils';
|
|
39
39
|
import { METRIC_MAP_NUMBER_TYPE } from '../../constants/filter';
|
|
@@ -91,6 +91,14 @@ export function useDataTableListing(props) {
|
|
|
91
91
|
const matchesAnyRequest = (apiMatchesAny === null || apiMatchesAny === void 0 ? void 0 : apiMatchesAny[`${selectedFilterItem === null || selectedFilterItem === void 0 ? void 0 : selectedFilterItem.column}`.toLowerCase()])
|
|
92
92
|
? apiMatchesAny === null || apiMatchesAny === void 0 ? void 0 : apiMatchesAny[`${selectedFilterItem === null || selectedFilterItem === void 0 ? void 0 : selectedFilterItem.column}`.toLowerCase()]
|
|
93
93
|
: apiMatchesAny === null || apiMatchesAny === void 0 ? void 0 : apiMatchesAny.general;
|
|
94
|
+
/**
|
|
95
|
+
* If `selectedFilterItem` has a column, the function will attempt to find a matching property in the `matchesAny`
|
|
96
|
+
* object by converting the column name to lowercase. If such a property does not exist, it will default to using
|
|
97
|
+
* the `general` property from the `matchesAny` object.
|
|
98
|
+
*/
|
|
99
|
+
const matchesAnyProps = (matchesAny === null || matchesAny === void 0 ? void 0 : matchesAny[`${selectedFilterItem === null || selectedFilterItem === void 0 ? void 0 : selectedFilterItem.column}`.toLowerCase()])
|
|
100
|
+
? matchesAny === null || matchesAny === void 0 ? void 0 : matchesAny[`${selectedFilterItem === null || selectedFilterItem === void 0 ? void 0 : selectedFilterItem.column}`.toLowerCase()]
|
|
101
|
+
: matchesAny === null || matchesAny === void 0 ? void 0 : matchesAny.general;
|
|
94
102
|
// Memos
|
|
95
103
|
const mapObject = useDeepCompareMemo(() => ({
|
|
96
104
|
objType: object === null || object === void 0 ? void 0 : object.type,
|
|
@@ -131,7 +139,7 @@ export function useDataTableListing(props) {
|
|
|
131
139
|
},
|
|
132
140
|
options: Object.assign({ enabled: enabledApiFilter }, queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.getFilterMetricList),
|
|
133
141
|
});
|
|
134
|
-
const
|
|
142
|
+
const getInfiniteMatchesAnyList = useGetInfiniteMatchesAnyList({
|
|
135
143
|
args: {
|
|
136
144
|
auth,
|
|
137
145
|
params: (_a = matchesAnyRequest === null || matchesAnyRequest === void 0 ? void 0 : matchesAnyRequest.params) === null || _a === void 0 ? void 0 : _a.call(matchesAnyRequest, selectedFilterItem),
|
|
@@ -140,9 +148,27 @@ export function useDataTableListing(props) {
|
|
|
140
148
|
options: {
|
|
141
149
|
enabled: !!selectedFilterItem &&
|
|
142
150
|
[MATCHES_ANY, NOT_MATCHES, MATCHES].includes(selectedFilterItem.operator) &&
|
|
143
|
-
!!matchesAnyRequest
|
|
151
|
+
!!matchesAnyRequest &&
|
|
152
|
+
(typeof matchesAnyRequest.enabled === 'boolean' ? matchesAnyRequest.enabled : true),
|
|
144
153
|
keepPreviousData: true,
|
|
145
154
|
retry: false,
|
|
155
|
+
getNextPageParam(lastPage, allPages) {
|
|
156
|
+
// Extract the total number of items from the last fetched page
|
|
157
|
+
const total = (typeof (matchesAnyProps === null || matchesAnyProps === void 0 ? void 0 : matchesAnyProps.total) === 'function'
|
|
158
|
+
? matchesAnyProps === null || matchesAnyProps === void 0 ? void 0 : matchesAnyProps.total(lastPage)
|
|
159
|
+
: matchesAnyProps === null || matchesAnyProps === void 0 ? void 0 : matchesAnyProps.total) || lastPage.total;
|
|
160
|
+
// Check if the total number of items is defined
|
|
161
|
+
if (total) {
|
|
162
|
+
// Calculate the current total number of items retrieved so far
|
|
163
|
+
// Process all pages and their data using formatData function and flatten the results
|
|
164
|
+
const currentTotalItems = flatMap(allPages === null || allPages === void 0 ? void 0 : allPages.map(page => { var _a; return ((_a = matchesAnyProps === null || matchesAnyProps === void 0 ? void 0 : matchesAnyProps.formatData) === null || _a === void 0 ? void 0 : _a.call(matchesAnyProps, page)) || []; }));
|
|
165
|
+
// If the total number of items fetched is less than the total available,
|
|
166
|
+
// return the next page number (allPages.length + 1). Otherwise, return undefined.
|
|
167
|
+
return ((currentTotalItems === null || currentTotalItems === void 0 ? void 0 : currentTotalItems.length) || 0) < total ? (allPages === null || allPages === void 0 ? void 0 : allPages.length) + 1 : undefined;
|
|
168
|
+
}
|
|
169
|
+
// If the total number of items is not defined, return undefined
|
|
170
|
+
return undefined;
|
|
171
|
+
},
|
|
146
172
|
},
|
|
147
173
|
});
|
|
148
174
|
// Variables
|
|
@@ -177,7 +203,7 @@ export function useDataTableListing(props) {
|
|
|
177
203
|
const { data: tableListing, isLoading: isTableListingLoading, isRefetching: isTableListingRefetching, } = getTableListing || {};
|
|
178
204
|
const { body: tableBody, header: tableHeader, total } = tableListing || {};
|
|
179
205
|
const { data: searchListingData, isLoading: isSearchListingLoading, isFetching: isSearchListingFetching, } = getSearchListing || {};
|
|
180
|
-
const { data:
|
|
206
|
+
const { data: infiniteMatchesAnyData, isFetchingNextPage, isLoading: isMatchesAnyListLoading = false, isFetching: isMatchesAnyListFetching = false, fetchNextPage, } = getInfiniteMatchesAnyList || {};
|
|
181
207
|
// Conditionally format search listing data based on the presence of a formatData function in searchProps.
|
|
182
208
|
// If formatData is defined, use it to format searchListingData.
|
|
183
209
|
// Otherwise, use the mapResponseSearchToGeneral function to format searchListingData.
|
|
@@ -299,16 +325,16 @@ export function useDataTableListing(props) {
|
|
|
299
325
|
return ((searchListingBody === null || searchListingBody === void 0 ? void 0 : searchListingBody.map(searchItem => (Object.assign(Object.assign({}, Object.entries(itemMapKeys || {}).reduce((acc, [key, value]) => (Object.assign(Object.assign({}, acc), { [key]: searchItem[value] })), {})), { icon: typeof icon === 'function' ? icon(searchItem) : icon, link: typeof link === 'function' ? link(searchItem) : link })))) || []);
|
|
300
326
|
}, [searchListingData, searchProps]);
|
|
301
327
|
const matchesAnyList = useDeepCompareMemo(() => {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return
|
|
311
|
-
}, [matchesAny,
|
|
328
|
+
var _a;
|
|
329
|
+
const { formatData } = matchesAnyProps || {};
|
|
330
|
+
let matchesAnyList = [];
|
|
331
|
+
if (formatData) {
|
|
332
|
+
(_a = infiniteMatchesAnyData === null || infiniteMatchesAnyData === void 0 ? void 0 : infiniteMatchesAnyData.pages) === null || _a === void 0 ? void 0 : _a.forEach(page => {
|
|
333
|
+
matchesAnyList = matchesAnyList.concat((formatData === null || formatData === void 0 ? void 0 : formatData(page)) || []);
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
return matchesAnyList;
|
|
337
|
+
}, [matchesAny, infiniteMatchesAnyData, selectedFilterItem === null || selectedFilterItem === void 0 ? void 0 : selectedFilterItem.column]);
|
|
312
338
|
// Effects
|
|
313
339
|
/**
|
|
314
340
|
* Update values from local storage to state
|
|
@@ -501,6 +527,11 @@ export function useDataTableListing(props) {
|
|
|
501
527
|
exact: false,
|
|
502
528
|
});
|
|
503
529
|
}, [name, queryClient]);
|
|
530
|
+
const onMatchesAnyLoadMore = useCallback(() => {
|
|
531
|
+
if (!isFetchingNextPage) {
|
|
532
|
+
fetchNextPage();
|
|
533
|
+
}
|
|
534
|
+
}, [fetchNextPage, isFetchingNextPage]);
|
|
504
535
|
return {
|
|
505
536
|
/* Modify Column */
|
|
506
537
|
modifyColumn: {
|
|
@@ -526,11 +557,12 @@ export function useDataTableListing(props) {
|
|
|
526
557
|
filterMetrics,
|
|
527
558
|
matchesAny: {
|
|
528
559
|
list: matchesAnyList,
|
|
529
|
-
isLoading: isMatchesAnyListLoading && !!matchesAnyRequest,
|
|
560
|
+
isLoading: (isMatchesAnyListLoading || isMatchesAnyListFetching) && !!matchesAnyRequest,
|
|
530
561
|
},
|
|
531
562
|
onSaveFilter,
|
|
532
563
|
onChangeFilters,
|
|
533
564
|
onChangeFilterCondition,
|
|
565
|
+
onMatchesAnyLoadMore,
|
|
534
566
|
},
|
|
535
567
|
/* Pagination */
|
|
536
568
|
pagination: Object.assign(Object.assign({}, pagination), { total, onChange: onChangePagination }),
|
|
@@ -50,5 +50,7 @@ export interface FilterProps<TActionKey extends string[] = []> {
|
|
|
50
50
|
onSaveFilter?: (args: TOnSaveFilterArgs) => Promise<TConfirmResponse>;
|
|
51
51
|
/** Handle callback when filter condition changed */
|
|
52
52
|
onChangeFilterCondition?: (filterItem: FilterItem) => void;
|
|
53
|
+
/** Handle callback when matches any load more */
|
|
54
|
+
onMatchesAnyLoadMore?: (metricId?: FilterMetricItem['id']) => void;
|
|
53
55
|
}
|
|
54
56
|
export type TApiFilter = Record<string, any>[];
|
|
@@ -17,6 +17,7 @@ type TRenderValueField = {
|
|
|
17
17
|
value?: any;
|
|
18
18
|
matchesAny?: FilterProps['matchesAny'];
|
|
19
19
|
onChangeValue?: (value: any) => void;
|
|
20
|
+
onLoadMore?: () => void;
|
|
20
21
|
};
|
|
21
22
|
type TGetFilterMetricByName = {
|
|
22
23
|
filterMetrics: FilterMetricItem[];
|
|
@@ -74,7 +75,7 @@ export declare const serializeFilterMetricsToMenuItems: ({ filterMetrics, level,
|
|
|
74
75
|
* // ]
|
|
75
76
|
*/
|
|
76
77
|
export declare const flatFilterMetrics: (filterMetrics: FilterMetricItem[]) => FilterMetricItem[];
|
|
77
|
-
export declare const renderValueField: ({ filterMetric, operator, value, matchesAny, onChangeValue, }: TRenderValueField) => React.JSX.Element | null;
|
|
78
|
+
export declare const renderValueField: ({ filterMetric, operator, value, matchesAny, onLoadMore, onChangeValue, }: TRenderValueField) => React.JSX.Element | null;
|
|
78
79
|
/**
|
|
79
80
|
* Retrieves a filter metric by its name from a collection of filter metrics.
|
|
80
81
|
*
|
|
@@ -104,7 +104,7 @@ export const flatFilterMetrics = (filterMetrics) => filterMetrics.reduce((acc, c
|
|
|
104
104
|
return [...acc, cur];
|
|
105
105
|
}, []);
|
|
106
106
|
/* Render Value Field */
|
|
107
|
-
export const renderValueField = ({ filterMetric, operator, value, matchesAny, onChangeValue = () => { }, }) => {
|
|
107
|
+
export const renderValueField = ({ filterMetric, operator, value, matchesAny, onLoadMore = () => { }, onChangeValue = () => { }, }) => {
|
|
108
108
|
const { dataType, name } = filterMetric;
|
|
109
109
|
switch (operator) {
|
|
110
110
|
case OPERATORS_CODE.GREATER_THAN:
|
|
@@ -160,7 +160,7 @@ export const renderValueField = ({ filterMetric, operator, value, matchesAny, on
|
|
|
160
160
|
case OPERATORS_CODE.MATCHES_ANY:
|
|
161
161
|
case OPERATORS_CODE.NOT_MATCHES: {
|
|
162
162
|
const { list, isLoading } = matchesAny || {};
|
|
163
|
-
return (React.createElement(MatchesAnySelect, { selectedItems: Array.isArray(value) ? value : [], popupClassName: EXCEPTION_OFF_FILTER_CLASS, objectName: name, items: list, loading: isLoading, onChange: onChangeValue }));
|
|
163
|
+
return (React.createElement(MatchesAnySelect, { selectedItems: Array.isArray(value) ? value : [], popupClassName: EXCEPTION_OFF_FILTER_CLASS, objectName: name, items: list, loading: isLoading, onChange: onChangeValue, onLoadMore: onLoadMore }));
|
|
164
164
|
}
|
|
165
165
|
case OPERATORS_CODE.AFTER:
|
|
166
166
|
case OPERATORS_CODE.AFTER_DATE:
|
|
@@ -102,6 +102,7 @@ export const useTemplateListing = (options) => {
|
|
|
102
102
|
// enabled: false,
|
|
103
103
|
// },
|
|
104
104
|
});
|
|
105
|
+
console.log('infiniteObjectTemplate', infiniteObjectTemplate);
|
|
105
106
|
// useDeepCompareEffect(() => {
|
|
106
107
|
// refetchTemplateList();
|
|
107
108
|
// }, [getTemplateListParams]);
|
package/es/constants/queries.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UseMutationOptions, UseQueryOptions } from '@tanstack/react-query';
|
|
1
|
+
import { UseInfiniteQueryOptions, UseMutationOptions, UseQueryOptions } from '@tanstack/react-query';
|
|
2
2
|
import { TCreateModifyColumnArgs, TDeleteModifyColumnArgs, TDeleteSavedFilterArgs, TGetFilterMetricListArgs, TGetMatchesAnyListArgs, TGetMetricListArgs, TGetModifyColumnListArgs, TGetSavedFilterListArgs, TGetSearchListingArgs, TGetTableListingArgs, TSaveFilterArgs, TUpdateFilterArgs, TUpdateModifyColumnArgs } from '../../services/DataTable';
|
|
3
3
|
import { ColumnMetric, FilterMetric, ModifyColumn } from '../../models/DataTable';
|
|
4
4
|
import { SavedFilter } from '../../models/DataTable/SavedFilter';
|
|
@@ -41,6 +41,10 @@ export type TGetMatchesAnyList = {
|
|
|
41
41
|
args: TGetMatchesAnyListArgs;
|
|
42
42
|
options?: UseQueryOptions<any, any, any, any[]>;
|
|
43
43
|
};
|
|
44
|
+
export type TGetInfiniteMatchesAnyList = {
|
|
45
|
+
args: TGetMatchesAnyListArgs;
|
|
46
|
+
options?: UseInfiniteQueryOptions<any, any, any, any[]>;
|
|
47
|
+
};
|
|
44
48
|
export type TSaveFilter = {
|
|
45
49
|
auth: TSaveFilterArgs['auth'];
|
|
46
50
|
options?: UseMutationOptions<{
|
|
@@ -92,3 +96,4 @@ export declare const useDeleteSavedFilter: (params?: TDeleteSavedFilter) => impo
|
|
|
92
96
|
export declare const useGetTableListing: <T>(params: TGetTableListing<T>) => import("@tanstack/react-query").UseQueryResult<DataTableListing<T>, any>;
|
|
93
97
|
export declare const useGetSearchListing: (params: TGetSearchListing) => import("@tanstack/react-query").UseQueryResult<any, any>;
|
|
94
98
|
export declare const useGetMatchesAnyList: (params: TGetMatchesAnyList) => import("@tanstack/react-query").UseQueryResult<any, any>;
|
|
99
|
+
export declare const useGetInfiniteMatchesAnyList: (params: TGetMatchesAnyList) => import("@tanstack/react-query").UseInfiniteQueryResult<any, any>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Types
|
|
2
|
-
import { useMutation, useQuery, useQueryClient, } from '@tanstack/react-query';
|
|
2
|
+
import { useInfiniteQuery, useMutation, useQuery, useQueryClient, } from '@tanstack/react-query';
|
|
3
3
|
import { omit } from 'lodash';
|
|
4
4
|
// Services
|
|
5
5
|
import { dataTableServices, } from '../../services/DataTable';
|
|
6
6
|
// Constants
|
|
7
7
|
import { QUERY_KEYS } from '../../constants';
|
|
8
|
-
const { GET_COLUMN_METRICS, GET_MODIFY_COLUMN_LIST, GET_SAVED_FILTER_LIST, GET_FILTER_METRIC_LIST, GET_SEARCH_LIST, GET_MATCHES_ANY_LIST, GET_DATA_TABLE_LISTING, } = QUERY_KEYS;
|
|
8
|
+
const { GET_COLUMN_METRICS, GET_MODIFY_COLUMN_LIST, GET_SAVED_FILTER_LIST, GET_FILTER_METRIC_LIST, GET_SEARCH_LIST, GET_MATCHES_ANY_LIST, GET_DATA_TABLE_LISTING, GET_INFINITE_MATCHES_ANY_LIST, } = QUERY_KEYS;
|
|
9
9
|
/* Column */
|
|
10
10
|
export const useGetColumnMetrics = (params) => {
|
|
11
11
|
const { args, options } = params;
|
|
@@ -91,3 +91,7 @@ export const useGetMatchesAnyList = (params) => {
|
|
|
91
91
|
const { args, options } = params || {};
|
|
92
92
|
return useQuery(Object.assign({ queryKey: [GET_MATCHES_ANY_LIST, args], queryFn: ({ signal }) => dataTableServices.filter.getMatchesAnyList(Object.assign(Object.assign({}, args), { request: Object.assign(Object.assign({}, args.request), { signal }) })) }, options));
|
|
93
93
|
};
|
|
94
|
+
export const useGetInfiniteMatchesAnyList = (params) => {
|
|
95
|
+
const { args, options } = params || {};
|
|
96
|
+
return useInfiniteQuery(Object.assign({ queryKey: [GET_INFINITE_MATCHES_ANY_LIST, args], queryFn: ({ signal, pageParam = 1 }) => dataTableServices.filter.getMatchesAnyList(Object.assign(Object.assign({}, args), { request: Object.assign(Object.assign({}, args.request), { signal }), params: Object.assign(Object.assign({}, args.params), { page: pageParam }) })), getNextPageParam: () => undefined }, options));
|
|
97
|
+
};
|
|
@@ -49,12 +49,11 @@ export const DataTableTest = () => {
|
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
51
|
survey_id: {
|
|
52
|
-
url: 'https://sandbox-survey.antsomi.com/api/v1/survey/
|
|
53
|
-
params(
|
|
54
|
-
var _a;
|
|
52
|
+
url: 'https://sandbox-survey.antsomi.com/api/v1/survey/performance',
|
|
53
|
+
params() {
|
|
55
54
|
return {
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
page: 1,
|
|
56
|
+
limit: 20,
|
|
58
57
|
};
|
|
59
58
|
},
|
|
60
59
|
},
|
|
@@ -100,24 +99,24 @@ export const DataTableTest = () => {
|
|
|
100
99
|
},
|
|
101
100
|
survey_id: {
|
|
102
101
|
formatData(response) {
|
|
103
|
-
return response === null || response === void 0 ? void 0 : response.
|
|
104
|
-
key: record.
|
|
105
|
-
title: record.
|
|
102
|
+
return response === null || response === void 0 ? void 0 : response.body.map(record => ({
|
|
103
|
+
key: record.survey_id,
|
|
104
|
+
title: record.survey_id,
|
|
106
105
|
}));
|
|
107
106
|
},
|
|
108
107
|
},
|
|
109
108
|
},
|
|
110
|
-
formatFilterValue: {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
},
|
|
109
|
+
// formatFilterValue: {
|
|
110
|
+
// matches(value) {
|
|
111
|
+
// return Array.isArray(value) && value.map(item => item.key).join(',');
|
|
112
|
+
// },
|
|
113
|
+
// matches_any(value) {
|
|
114
|
+
// return Array.isArray(value) && value.map(item => item.key).join(',');
|
|
115
|
+
// },
|
|
116
|
+
// not_matches(value) {
|
|
117
|
+
// return Array.isArray(value) && value.map(item => item.key).join(',');
|
|
118
|
+
// },
|
|
119
|
+
// },
|
|
121
120
|
},
|
|
122
121
|
});
|
|
123
122
|
return (React.createElement(BrowserRouter, null,
|