@controleonline/ui-default 1.0.270 → 1.0.272
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/.github/workflows/integration-source-gate.yml +68 -0
- package/.github/workflows/manager-worker.yml +19 -0
- package/AGENTS.md +46 -50
- package/docs/technical/Contrato-de-colunas.md +67 -0
- package/docs/technical/Default-Components.md +76 -0
- package/docs/technical/DefaultForm.md +80 -0
- package/docs/technical/DefaultInput-e-DefaultSelect.md +73 -0
- package/docs/technical/DefaultTable.md +203 -0
- package/docs/wiki.md +1 -0
- package/package.json +22 -21
- package/scripts/validate-wiki-pointer.cjs +13 -0
- package/src/react/components/address/DefaultAddress.js +435 -0
- package/src/react/components/address/DefaultAddress.styles.js +133 -0
- package/src/react/components/address/DefaultAddressMapPane.js +55 -0
- package/src/react/components/address/LatLonReadonlyFields.js +51 -0
- package/src/react/components/address/defaultAddressHelpers.js +163 -0
- package/src/react/components/address/defaultAddressStyles.js +136 -0
- package/src/react/components/address/defaultAddressUtils.js +129 -0
- package/src/react/components/extra-data/DefaultExtraData.js +152 -0
- package/src/react/components/extra-data/DefaultExtraData.styles.js +61 -0
- package/src/react/components/files/DefaultFile.js +231 -46
- package/src/react/components/files/DefaultFileColumn.js +123 -0
- package/src/react/components/files/DefaultFileColumn.styles.js +52 -0
- package/src/react/components/files/DefaultFileColumn.utils.js +104 -0
- package/src/react/components/filters/CompactFilterSelector.js +51 -35
- package/src/react/components/filters/DefaultColumnFilter.js +58 -41
- package/src/react/components/filters/DefaultExternalFilters.js +331 -265
- package/src/react/components/form/DefaultForm.js +99 -79
- package/src/react/components/help/DefaultTooltip.js +59 -33
- package/src/react/components/help/DefaultTooltip.styles.js +23 -3
- package/src/react/components/inputs/DefaultColorInput.js +153 -0
- package/src/react/components/inputs/DefaultFeatherIconPicker.js +146 -0
- package/src/react/components/inputs/DefaultFeatherIconPicker.styles.js +113 -0
- package/src/react/components/inputs/DefaultInput.js +69 -5
- package/src/react/components/inputs/DefaultInput.styles.js +26 -0
- package/src/react/components/inputs/DefaultSelect.js +134 -123
- package/src/react/components/inputs/defaultInputUtils.js +69 -43
- package/src/react/components/map/DefaultMap.native.js +73 -73
- package/src/react/components/map/DefaultMap.shared.js +911 -762
- package/src/react/components/map/DefaultMap.web.js +124 -68
- package/src/react/components/table/DefaultColumnMenu.js +109 -108
- package/src/react/components/table/DefaultDebug.js +44 -37
- package/src/react/components/table/DefaultSearchModal.js +9 -1
- package/src/react/components/table/DefaultTable.grouping.js +98 -0
- package/src/react/components/table/DefaultTable.js +213 -107
- package/src/react/components/table/DefaultTable.styles.js +367 -314
- package/src/react/components/table/DefaultTableBody.js +13 -8
- package/src/react/components/table/DefaultTableCards.js +20 -1
- package/src/react/components/table/DefaultTableControls.js +180 -205
- package/src/react/components/table/DefaultTableImportModal.js +108 -0
- package/src/react/components/table/DefaultTableInput.js +1 -0
- package/src/react/components/table/DefaultTableRowActions.js +1 -1
- package/src/react/components/table/DefaultTableRows.js +224 -363
- package/src/react/components/table/DefaultTableSearch.js +28 -5
- package/src/react/components/table/DefaultTableToolbar.js +92 -3
- package/src/react/components/table/useDefaultTablePagination.js +56 -7
- package/src/react/components/table/useDefaultTableStoreSync.js +164 -0
- package/src/react/components/upload/DefaultUpload.js +436 -0
- package/src/react/components/upload/DefaultUpload.styles.js +153 -0
- package/src/react/components/upload/DefaultUploadAttachmentsList.js +138 -0
- package/src/react/components/upload/DefaultUploadManagerModal.js +247 -0
- package/src/react/components/upload/defaultUploadHelpers.js +108 -0
- package/src/react/components/upload/defaultUploadLibrary.js +248 -0
- package/src/react/components/upload/fileUpload.js +93 -0
- package/src/react/hooks/usePostalCodeLookup.js +145 -0
- package/src/react/index.js +6 -0
- package/src/react/services/addressFormUtils.js +196 -0
- package/src/react/services/addressGeo.js +227 -0
- package/src/react/utils/tableVisibleColumnsPreferences.js +29 -12
- package/src/tests/react/components/address/defaultAddressHelpers.test.js +104 -0
- package/src/tests/react/components/files/DefaultFile.test.js +6 -6
- package/src/tests/react/components/form/DefaultForm.test.js +123 -0
- package/src/tests/react/components/inputs/DefaultSelect.test.js +108 -79
- package/src/tests/react/components/table/DefaultTable.grouping.test.js +49 -0
- package/src/tests/react/components/table/DefaultTable.test.js +53 -2
- package/src/tests/react/components/table/DefaultTableControls.filters.test.mjs +21 -0
- package/src/tests/react/components/table/resolveRowBackgroundColor.test.mjs +45 -0
- package/src/tests/react/components/table/useDefaultTableStoreSync.test.js +161 -0
- package/src/tests/react/components/upload/DefaultUpload.test.js +353 -0
- package/src/tests/react/components/upload/defaultUploadLibrary.test.js +82 -0
- package/src/tests/react/components/upload/peopleMediaLibraryPreview.test.mjs +57 -0
- package/src/tests/react/hooks/usePostalCodeLookup.test.js +40 -0
- package/src/tests/react/services/SMOKE-637-cep-hydrate.md +45 -0
- package/src/tests/react/services/addressFormUtils.test.js +176 -0
- package/src/tests/react/services/addressGeoLookup.async.test.js +104 -0
- package/src/tests/react/services/addressGeoLookup.test.js +105 -0
- package/src/tests/react/services/smoke-637/form-cep-12941040.png +0 -0
- package/src/tests/react/services/smoke-637/index.html +132 -0
- package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +84 -48
|
@@ -12,6 +12,11 @@ import styles from './DefaultTable.styles';
|
|
|
12
12
|
import useDefaultTableTheme from './useDefaultTableTheme';
|
|
13
13
|
|
|
14
14
|
const shouldRenderSearch = store => {
|
|
15
|
+
const configs = store?.getters?.configs || {};
|
|
16
|
+
|
|
17
|
+
if (configs.showSearch === true) return true;
|
|
18
|
+
if (configs.showSearch === false) return false;
|
|
19
|
+
|
|
15
20
|
const columns = Array.isArray(store?.getters?.columns) ? store.getters.columns : [];
|
|
16
21
|
|
|
17
22
|
return columns.some(column =>
|
|
@@ -45,22 +50,38 @@ const useSearchState = storeName => {
|
|
|
45
50
|
};
|
|
46
51
|
|
|
47
52
|
export const DefaultTableCompactSearch = ({ storeName }) => {
|
|
53
|
+
const store = useStore(storeName);
|
|
54
|
+
const configs = store?.getters?.configs || {};
|
|
48
55
|
const { renders, rendersCompactTotal } = useSearchState(storeName);
|
|
49
56
|
|
|
50
57
|
return renders && rendersCompactTotal ? (
|
|
51
|
-
<DefaultSearch
|
|
58
|
+
<DefaultSearch
|
|
59
|
+
compact
|
|
60
|
+
placeholder={configs.searchPlaceholder}
|
|
61
|
+
searchKey={configs.searchKey || 'search'}
|
|
62
|
+
storeName={storeName}
|
|
63
|
+
/>
|
|
52
64
|
) : null;
|
|
53
65
|
};
|
|
54
66
|
|
|
55
67
|
export const DefaultTableInlineSearch = ({ storeName }) => {
|
|
68
|
+
const store = useStore(storeName);
|
|
69
|
+
const configs = store?.getters?.configs || {};
|
|
56
70
|
const { collapses, renders, rendersCompactTotal } = useSearchState(storeName);
|
|
57
71
|
|
|
58
72
|
return renders && !rendersCompactTotal && !collapses ? (
|
|
59
|
-
<DefaultSearch
|
|
73
|
+
<DefaultSearch
|
|
74
|
+
compact
|
|
75
|
+
placeholder={configs.searchPlaceholder}
|
|
76
|
+
searchKey={configs.searchKey || 'search'}
|
|
77
|
+
storeName={storeName}
|
|
78
|
+
/>
|
|
60
79
|
) : null;
|
|
61
80
|
};
|
|
62
81
|
|
|
63
82
|
export const DefaultTableCollapsedSearch = ({ storeName }) => {
|
|
83
|
+
const store = useStore(storeName);
|
|
84
|
+
const configs = store?.getters?.configs || {};
|
|
64
85
|
const { collapses, renders, rendersCompactTotal } = useSearchState(storeName);
|
|
65
86
|
const { tableButtonColors } = useDefaultTableTheme();
|
|
66
87
|
|
|
@@ -89,9 +110,11 @@ export const DefaultTableCollapsedSearch = ({ storeName }) => {
|
|
|
89
110
|
)}
|
|
90
111
|
>
|
|
91
112
|
{({ close, isOpen }) => (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
113
|
+
<DefaultSearchModal
|
|
114
|
+
searchKey={configs.searchKey || 'search'}
|
|
115
|
+
searchPlaceholder={configs.searchPlaceholder}
|
|
116
|
+
storeName={storeName}
|
|
117
|
+
visible={isOpen}
|
|
95
118
|
onClose={close}
|
|
96
119
|
/>
|
|
97
120
|
)}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, {useMemo, useState} from 'react';
|
|
2
2
|
import { Text, View, useWindowDimensions } from 'react-native';
|
|
3
3
|
import { useStore } from '@store';
|
|
4
4
|
import DefaultTableControls from './DefaultTableControls';
|
|
5
|
+
import DefaultTableImportModal from './DefaultTableImportModal';
|
|
5
6
|
import {
|
|
6
7
|
DefaultTableCollapsedSearch,
|
|
7
8
|
DefaultTableCompactSearch,
|
|
@@ -11,8 +12,31 @@ import DefaultToolbarAction from './DefaultToolbarAction';
|
|
|
11
12
|
import styles from './DefaultTable.styles';
|
|
12
13
|
import useDefaultTableTheme from './useDefaultTableTheme';
|
|
13
14
|
|
|
15
|
+
const translateConfigKey = key => {
|
|
16
|
+
if (!Array.isArray(key) || key.length < 3) {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return String(global.t?.t?.(key[0], key[1], key[2]) ?? '').trim();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const resolveConfiguredLabel = config => {
|
|
24
|
+
const literalLabel = String(config?.label ?? '').trim();
|
|
25
|
+
if (literalLabel) {
|
|
26
|
+
return literalLabel;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const configuredLabel = translateConfigKey(config?.labelKey);
|
|
30
|
+
if (configuredLabel) {
|
|
31
|
+
return configuredLabel;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return global.t?.t?.('defaultTable', 'button', 'import');
|
|
35
|
+
};
|
|
36
|
+
|
|
14
37
|
const DefaultTableToolbar = ({ storeName }) => {
|
|
15
38
|
const store = useStore(storeName);
|
|
39
|
+
const [isImportModalVisible, setIsImportModalVisible] = useState(false);
|
|
16
40
|
const { width } = useWindowDimensions();
|
|
17
41
|
const configs = store?.getters?.configs || {};
|
|
18
42
|
const compactBreakpoint = Number(configs.compactBreakpoint || 768);
|
|
@@ -20,6 +44,66 @@ const DefaultTableToolbar = ({ storeName }) => {
|
|
|
20
44
|
const showTotalItemsInCompactToolbar = configs.showTotalItemsInCompactToolbar === true;
|
|
21
45
|
const showTotalItemsInFooter = configs.showTotalItemsInFooter !== false;
|
|
22
46
|
const toolbarActions = Array.isArray(configs.toolbarActions) ? configs.toolbarActions : [];
|
|
47
|
+
const buildTransferAction = (action, fallback) => {
|
|
48
|
+
if (!action) return null;
|
|
49
|
+
|
|
50
|
+
if (typeof action === 'function') {
|
|
51
|
+
return {
|
|
52
|
+
...fallback,
|
|
53
|
+
onPress: action,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof action === 'object') {
|
|
58
|
+
return {
|
|
59
|
+
...fallback,
|
|
60
|
+
...action,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return null;
|
|
65
|
+
};
|
|
66
|
+
const importConfig = configs.import || null;
|
|
67
|
+
const importActionOverrides =
|
|
68
|
+
configs.importAction && typeof configs.importAction === 'object'
|
|
69
|
+
? configs.importAction
|
|
70
|
+
: null;
|
|
71
|
+
const genericImportAction = useMemo(() => {
|
|
72
|
+
if (!importConfig || importConfig.enabled === false || !importConfig.importType) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
...(importActionOverrides || {}),
|
|
78
|
+
key: 'default-table-import',
|
|
79
|
+
icon: importConfig.icon || 'upload',
|
|
80
|
+
label: resolveConfiguredLabel(importConfig),
|
|
81
|
+
onPress: () => setIsImportModalVisible(true),
|
|
82
|
+
};
|
|
83
|
+
}, [importActionOverrides, importConfig]);
|
|
84
|
+
const importTableAction =
|
|
85
|
+
genericImportAction ||
|
|
86
|
+
buildTransferAction(
|
|
87
|
+
configs.importAction || configs.onImport,
|
|
88
|
+
{
|
|
89
|
+
key: 'default-table-import',
|
|
90
|
+
icon: 'upload',
|
|
91
|
+
label: global.t?.t?.('defaultTable', 'button', 'import'),
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
const exportTableAction = buildTransferAction(
|
|
95
|
+
configs.exportAction || configs.onExport,
|
|
96
|
+
{
|
|
97
|
+
key: 'default-table-export',
|
|
98
|
+
icon: 'download',
|
|
99
|
+
label: global.t?.t?.('defaultTable', 'button', 'export'),
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
const resolvedToolbarActions = [
|
|
103
|
+
importTableAction,
|
|
104
|
+
exportTableAction,
|
|
105
|
+
...toolbarActions,
|
|
106
|
+
].filter(Boolean);
|
|
23
107
|
const { tableBorderColors, toolbarColors } = useDefaultTableTheme();
|
|
24
108
|
const {
|
|
25
109
|
backgroundColor: toolbarBackgroundColor,
|
|
@@ -49,9 +133,9 @@ const DefaultTableToolbar = ({ storeName }) => {
|
|
|
49
133
|
? `${totalItemsNumber} ${global.t?.t(storeName, 'label', 'items')}`
|
|
50
134
|
: '';
|
|
51
135
|
const renderToolbarActions = () =>
|
|
52
|
-
|
|
136
|
+
resolvedToolbarActions.length > 0 ? (
|
|
53
137
|
<View style={styles.toolbarActionGroup}>
|
|
54
|
-
{
|
|
138
|
+
{resolvedToolbarActions.map(action => (
|
|
55
139
|
<DefaultToolbarAction
|
|
56
140
|
key={action?.key || action?.icon || action?.label}
|
|
57
141
|
action={action}
|
|
@@ -97,6 +181,11 @@ const DefaultTableToolbar = ({ storeName }) => {
|
|
|
97
181
|
<DefaultTableControls storeName={storeName} />
|
|
98
182
|
</View>
|
|
99
183
|
</View>
|
|
184
|
+
<DefaultTableImportModal
|
|
185
|
+
onClose={() => setIsImportModalVisible(false)}
|
|
186
|
+
storeName={storeName}
|
|
187
|
+
visible={isImportModalVisible}
|
|
188
|
+
/>
|
|
100
189
|
</View>
|
|
101
190
|
);
|
|
102
191
|
};
|
|
@@ -79,22 +79,31 @@ const useAutoPageLoader = ({
|
|
|
79
79
|
const [autoHasLoaded, setAutoHasLoaded] = useState(false);
|
|
80
80
|
const [autoLoading, setAutoLoading] = useState(false);
|
|
81
81
|
const [autoLoadingMore, setAutoLoadingMore] = useState(false);
|
|
82
|
+
const [autoRefreshing, setAutoRefreshing] = useState(false);
|
|
82
83
|
const [autoLastPageCount, setAutoLastPageCount] = useState(0);
|
|
83
84
|
const autoRequestIdRef = useRef(0);
|
|
84
85
|
const autoLoadedQueryKeyRef = useRef('');
|
|
85
86
|
const autoErroredQueryKeyRef = useRef('');
|
|
86
87
|
const autoPageRef = useRef(0);
|
|
88
|
+
const isFetchingRef = useRef(false);
|
|
87
89
|
|
|
88
90
|
const loadAutoPage = useCallback(
|
|
89
|
-
(page, { append = false } = {}) => {
|
|
91
|
+
(page, { append = false, refresh = false } = {}) => {
|
|
90
92
|
if (!autoMode || typeof resolvedActions.getItems !== 'function') {
|
|
91
93
|
return Promise.resolve([]);
|
|
92
94
|
}
|
|
95
|
+
if (isFetchingRef.current) {
|
|
96
|
+
return Promise.resolve([]);
|
|
97
|
+
}
|
|
98
|
+
isFetchingRef.current = true;
|
|
93
99
|
|
|
94
100
|
const requestId = autoRequestIdRef.current + 1;
|
|
95
101
|
autoRequestIdRef.current = requestId;
|
|
96
102
|
|
|
97
|
-
if (
|
|
103
|
+
if (refresh) {
|
|
104
|
+
setAutoRefreshing(true);
|
|
105
|
+
autoPageRef.current = 0;
|
|
106
|
+
} else if (append) {
|
|
98
107
|
setAutoLoadingMore(true);
|
|
99
108
|
} else {
|
|
100
109
|
setAutoLoading(true);
|
|
@@ -130,9 +139,11 @@ const useAutoPageLoader = ({
|
|
|
130
139
|
return [];
|
|
131
140
|
})
|
|
132
141
|
.finally(() => {
|
|
142
|
+
isFetchingRef.current = false;
|
|
133
143
|
if (autoRequestIdRef.current === requestId) {
|
|
134
144
|
setAutoLoading(false);
|
|
135
145
|
setAutoLoadingMore(false);
|
|
146
|
+
setAutoRefreshing(false);
|
|
136
147
|
endReachedLockRef.current = false;
|
|
137
148
|
}
|
|
138
149
|
});
|
|
@@ -155,6 +166,7 @@ const useAutoPageLoader = ({
|
|
|
155
166
|
autoLoadedQueryKeyRef,
|
|
156
167
|
autoLoading,
|
|
157
168
|
autoLoadingMore,
|
|
169
|
+
autoRefreshing,
|
|
158
170
|
autoPageRef,
|
|
159
171
|
loadAutoPage,
|
|
160
172
|
pageSizeNumber,
|
|
@@ -209,6 +221,7 @@ export const useDefaultTablePagination = ({
|
|
|
209
221
|
hasMore,
|
|
210
222
|
isFocused,
|
|
211
223
|
isLoading,
|
|
224
|
+
onRefresh,
|
|
212
225
|
onEndReached,
|
|
213
226
|
pageSize,
|
|
214
227
|
requestParams,
|
|
@@ -227,6 +240,8 @@ export const useDefaultTablePagination = ({
|
|
|
227
240
|
sortField: resolvedSort?.field,
|
|
228
241
|
});
|
|
229
242
|
const pageSizeNumber = Number(requestParams.itemsPerPage || pageSize || 50) || 50;
|
|
243
|
+
const resourceEndpoint = store?.getters?.resourceEndpoint || '';
|
|
244
|
+
const storeReload = store?.getters?.reload;
|
|
230
245
|
const buildRequestQuery = useCallback(
|
|
231
246
|
(page, append = false) =>
|
|
232
247
|
buildQueryFromState({
|
|
@@ -243,12 +258,20 @@ export const useDefaultTablePagination = ({
|
|
|
243
258
|
const autoQuerySignature = useMemo(
|
|
244
259
|
() =>
|
|
245
260
|
stableSerialize({
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
261
|
+
endpoint: resourceEndpoint,
|
|
262
|
+
query: buildQueryFromState({
|
|
263
|
+
append: false,
|
|
264
|
+
columnsForTable,
|
|
265
|
+
filters,
|
|
266
|
+
page: 1,
|
|
267
|
+
pageSizeNumber,
|
|
268
|
+
requestParams,
|
|
269
|
+
sort: resolvedSort,
|
|
270
|
+
}),
|
|
271
|
+
reload: Boolean(storeReload),
|
|
272
|
+
storeName,
|
|
250
273
|
}),
|
|
251
|
-
[filters, pageSizeNumber, requestParams, resolvedSort],
|
|
274
|
+
[columnsForTable, filters, pageSizeNumber, requestParams, resolvedSort, resourceEndpoint, storeName, storeReload],
|
|
252
275
|
);
|
|
253
276
|
const {
|
|
254
277
|
autoErroredQueryKeyRef,
|
|
@@ -257,6 +280,7 @@ export const useDefaultTablePagination = ({
|
|
|
257
280
|
autoLoadedQueryKeyRef,
|
|
258
281
|
autoLoading,
|
|
259
282
|
autoLoadingMore,
|
|
283
|
+
autoRefreshing,
|
|
260
284
|
autoPageRef,
|
|
261
285
|
loadAutoPage,
|
|
262
286
|
} = useAutoPageLoader({
|
|
@@ -283,9 +307,18 @@ export const useDefaultTablePagination = ({
|
|
|
283
307
|
resolvedActions,
|
|
284
308
|
});
|
|
285
309
|
|
|
310
|
+
useEffect(() => {
|
|
311
|
+
if (storeReload && typeof store?.actions?.setReload === 'function') {
|
|
312
|
+
store.actions.setReload(false);
|
|
313
|
+
}
|
|
314
|
+
}, [storeReload, store]);
|
|
315
|
+
|
|
286
316
|
const resolvedIsLoading = autoMode
|
|
287
317
|
? (autoLoading || autoLoadingMore)
|
|
288
318
|
: Boolean(isLoading);
|
|
319
|
+
const resolvedIsRefreshing = autoMode
|
|
320
|
+
? autoRefreshing
|
|
321
|
+
: false;
|
|
289
322
|
const resolvedData = autoMode
|
|
290
323
|
? (autoHasLoaded && Array.isArray(store?.getters?.items) ? store.getters.items : [])
|
|
291
324
|
: (Array.isArray(data) ? data : []);
|
|
@@ -358,13 +391,29 @@ export const useDefaultTablePagination = ({
|
|
|
358
391
|
resolvedIsLoading,
|
|
359
392
|
]);
|
|
360
393
|
|
|
394
|
+
const handleRefresh = useCallback(() => {
|
|
395
|
+
endReachedLockRef.current = false;
|
|
396
|
+
|
|
397
|
+
if (autoMode) {
|
|
398
|
+
return loadAutoPage(1, { append: false, refresh: true });
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (typeof onRefresh === 'function') {
|
|
402
|
+
return Promise.resolve(onRefresh());
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return Promise.resolve([]);
|
|
406
|
+
}, [autoMode, loadAutoPage, onRefresh]);
|
|
407
|
+
|
|
361
408
|
return {
|
|
362
409
|
buildRequestQuery,
|
|
363
410
|
currentPage: autoPageRef.current,
|
|
364
411
|
handleEndReached,
|
|
412
|
+
handleRefresh,
|
|
365
413
|
pageSizeNumber,
|
|
366
414
|
resolvedData,
|
|
367
415
|
resolvedHasMore,
|
|
416
|
+
resolvedIsRefreshing,
|
|
368
417
|
resolvedIsLoading,
|
|
369
418
|
};
|
|
370
419
|
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
resolveStoredTableFiltersPreference,
|
|
4
|
+
resolveStoredVisibleColumnsPreference,
|
|
5
|
+
sanitizeTableFiltersPreference,
|
|
6
|
+
sanitizeVisibleColumnsPreference,
|
|
7
|
+
} from '../../utils/tableVisibleColumnsPreferences';
|
|
8
|
+
|
|
9
|
+
export const publishStoreValue = (store, actionName, value, getterName) => {
|
|
10
|
+
if (typeof store?.actions?.[actionName] === 'function') {
|
|
11
|
+
store.actions[actionName](value);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (store?.getters) {
|
|
16
|
+
store.getters[getterName] = value;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const assignGetterValue = (store, getterName, value) => {
|
|
21
|
+
if (store?.getters) {
|
|
22
|
+
store.getters[getterName] = value;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** Stable JSON compare for filter objects (anti React #185 on period toggle). */
|
|
27
|
+
export const areTableFiltersEqual = (a, b) => {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.stringify(a ?? {}) === JSON.stringify(b ?? {});
|
|
30
|
+
} catch {
|
|
31
|
+
return a === b;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Keeps controlled DefaultTable props (data/columns/configs/preferences) in sync
|
|
37
|
+
* with the zustand store without depending on store object identity.
|
|
38
|
+
* Zustand replaces the store slice on every commit; using that identity in effect
|
|
39
|
+
* deps caused React error #185 (Maximum update depth exceeded) on Devices.
|
|
40
|
+
*
|
|
41
|
+
* Stored filters are hydrated once per preference scope / columns signature.
|
|
42
|
+
* Re-applying on every storeFilters change caused Maximum update depth when
|
|
43
|
+
* toggling date period (today → all → today) on OrderHistoryPage (app-community#448).
|
|
44
|
+
*/
|
|
45
|
+
export function useDefaultTableStoreSync({
|
|
46
|
+
columns,
|
|
47
|
+
columnsForTable,
|
|
48
|
+
data,
|
|
49
|
+
defaultTableConfigs,
|
|
50
|
+
defaultTableConfigsSignature,
|
|
51
|
+
store,
|
|
52
|
+
storeColumnsLength,
|
|
53
|
+
storeFilters,
|
|
54
|
+
storeName,
|
|
55
|
+
tablePreferenceScope,
|
|
56
|
+
}) {
|
|
57
|
+
const storeRef = useRef(store);
|
|
58
|
+
storeRef.current = store;
|
|
59
|
+
const lastPublishedItemsRef = useRef(undefined);
|
|
60
|
+
const configsSignatureRef = useRef('');
|
|
61
|
+
const lastHydratedFiltersKeyRef = useRef('');
|
|
62
|
+
const storeFiltersRef = useRef(storeFilters);
|
|
63
|
+
storeFiltersRef.current = storeFilters;
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (storeColumnsLength > 0 || !Array.isArray(columns) || columns.length === 0) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
publishStoreValue(storeRef.current, 'setColumns', columns, 'columns');
|
|
71
|
+
}, [columns, storeColumnsLength, storeName]);
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (data === undefined || !Array.isArray(data)) {
|
|
75
|
+
lastPublishedItemsRef.current = undefined;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (lastPublishedItemsRef.current === data) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
lastPublishedItemsRef.current = data;
|
|
84
|
+
publishStoreValue(storeRef.current, 'setItems', data, 'items');
|
|
85
|
+
}, [data, storeName]);
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (!columnsForTable.length) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const storedVisibleColumns = resolveStoredVisibleColumnsPreference(tablePreferenceScope);
|
|
93
|
+
if (!storedVisibleColumns) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const nextVisibleColumns = sanitizeVisibleColumnsPreference({
|
|
98
|
+
columns: columnsForTable,
|
|
99
|
+
visibleColumns: storedVisibleColumns,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (!nextVisibleColumns.length) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
publishStoreValue(
|
|
107
|
+
storeRef.current,
|
|
108
|
+
'setVisibleColumns',
|
|
109
|
+
nextVisibleColumns,
|
|
110
|
+
'visibleColumns',
|
|
111
|
+
);
|
|
112
|
+
}, [columnsForTable, storeName, tablePreferenceScope]);
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!columnsForTable.length) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const scopeKey = [
|
|
120
|
+
tablePreferenceScope?.companyKey || '',
|
|
121
|
+
tablePreferenceScope?.storeKey || '',
|
|
122
|
+
tablePreferenceScope?.routeKey || '',
|
|
123
|
+
columnsForTable.map(c => c?.key || c?.name || '').join('|'),
|
|
124
|
+
].join('::');
|
|
125
|
+
|
|
126
|
+
const storedFilters = resolveStoredTableFiltersPreference(tablePreferenceScope);
|
|
127
|
+
if (!storedFilters) {
|
|
128
|
+
lastHydratedFiltersKeyRef.current = scopeKey;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const nextFilters = sanitizeTableFiltersPreference({
|
|
133
|
+
columns: columnsForTable,
|
|
134
|
+
filters: storedFilters,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
if (Object.keys(nextFilters).length === 0) {
|
|
138
|
+
lastHydratedFiltersKeyRef.current = scopeKey;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Hydrate once per scope/columns; never chase storeFilters (loop on period toggle).
|
|
143
|
+
if (lastHydratedFiltersKeyRef.current === scopeKey) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (areTableFiltersEqual(storeFiltersRef.current, nextFilters)) {
|
|
148
|
+
lastHydratedFiltersKeyRef.current = scopeKey;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
lastHydratedFiltersKeyRef.current = scopeKey;
|
|
153
|
+
publishStoreValue(storeRef.current, 'setFilters', nextFilters, 'filters');
|
|
154
|
+
}, [columnsForTable, storeName, tablePreferenceScope]);
|
|
155
|
+
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
if (configsSignatureRef.current === defaultTableConfigsSignature) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
configsSignatureRef.current = defaultTableConfigsSignature;
|
|
162
|
+
publishStoreValue(storeRef.current, 'setConfigs', defaultTableConfigs, 'configs');
|
|
163
|
+
}, [defaultTableConfigs, defaultTableConfigsSignature, storeName]);
|
|
164
|
+
}
|