@controleonline/ui-default 1.0.263 → 1.0.265
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/AGENTS.md +47 -45
- package/package.json +20 -20
- package/src/react/components/errors/DefaultErrors.js +307 -325
- package/src/react/components/files/DefaultFile.js +46 -46
- package/src/react/components/filters/CompactFilterSelector.js +78 -24
- package/src/react/components/filters/CompactFilterSelector.styles.js +17 -5
- package/src/react/components/filters/DefaultColumnFilter.js +191 -57
- package/src/react/components/filters/DefaultExternalFilters.js +245 -182
- package/src/react/components/filters/DefaultSearch.js +131 -92
- package/src/react/components/filters/DefaultSearch.styles.js +65 -70
- package/src/react/components/form/DefaultForm.js +172 -151
- package/src/react/components/form/DefaultForm.styles.js +71 -66
- package/src/react/components/help/DefaultTooltip.js +87 -87
- package/src/react/components/help/DefaultTooltip.styles.js +61 -61
- package/src/react/components/inputs/DefaultDateInput.js +150 -0
- package/src/react/components/inputs/DefaultInput.js +162 -127
- package/src/react/components/inputs/DefaultInput.styles.js +6 -0
- package/src/react/components/inputs/DefaultSelect.js +320 -152
- package/src/react/components/inputs/defaultInputUtils.js +40 -19
- package/src/react/components/map/DefaultGoogleMap.styles.js +115 -9
- package/src/react/components/map/DefaultGoogleMap.web.js +560 -446
- package/src/react/components/map/DefaultMap.native.js +73 -71
- package/src/react/components/map/DefaultMap.styles.js +16 -16
- package/src/react/components/map/DefaultMap.web.js +68 -66
- package/src/react/components/map/DefaultNativeMap.native.js +307 -183
- package/src/react/components/map/DefaultNativeMap.shared.js +439 -340
- package/src/react/components/map/DefaultNativeMap.styles.js +156 -122
- package/src/react/components/table/DefaultColumnMenu.js +108 -0
- package/src/react/components/table/DefaultDebug.js +233 -0
- package/src/react/components/table/DefaultFiltersModal.js +142 -0
- package/src/react/components/table/DefaultModalButton.js +18 -0
- package/src/react/components/table/DefaultSearchModal.js +49 -0
- package/src/react/components/table/DefaultTable.js +345 -1846
- package/src/react/components/table/DefaultTable.styles.js +247 -136
- package/src/react/components/table/DefaultTable.utils.js +502 -0
- package/src/react/components/table/DefaultTableBody.js +26 -0
- package/src/react/components/table/DefaultTableCards.js +171 -0
- package/src/react/components/table/DefaultTableControls.js +208 -0
- package/src/react/components/table/DefaultTableEmptyState.js +22 -0
- package/src/react/components/table/DefaultTableFooter.js +172 -0
- package/src/react/components/table/DefaultTableInput.js +145 -0
- package/src/react/components/table/DefaultTableRows.js +340 -0
- package/src/react/components/table/DefaultTableSearch.js +100 -0
- package/src/react/components/table/DefaultTableToolbar.js +98 -0
- package/src/react/components/table/DefaultToolbarAction.js +56 -0
- package/src/react/components/table/useDefaultTablePagination.js +370 -0
- package/src/react/components/table/useDefaultTableSorting.js +126 -0
- package/src/react/components/table/useDefaultTableTheme.js +58 -0
- package/src/react/index.js +10 -10
- package/src/react/utils/tableVisibleColumnsPreferences.js +239 -211
- package/src/store/default/actions.js +51 -13
- package/src/store/default/getters.js +10 -8
- package/src/store/default/mutation_types.js +4 -2
- package/src/store/default/mutations.js +10 -1
- package/src/tests/react/components/errors/DefaultErrors.test.js +162 -162
- package/src/tests/react/components/files/DefaultFile.test.js +80 -80
- package/src/tests/react/components/form/DefaultForm.test.js +81 -0
- package/src/tests/react/components/inputs/DefaultSelect.test.js +185 -0
- package/src/tests/react/components/table/DefaultTable.test.js +1183 -0
- package/src/tests/react/store/defaultActions.test.js +59 -0
- package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +247 -168
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
Modal,
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Modal,
|
|
4
4
|
ScrollView,
|
|
5
5
|
Text,
|
|
6
6
|
TextInput,
|
|
@@ -8,59 +8,85 @@ import {
|
|
|
8
8
|
TouchableWithoutFeedback,
|
|
9
9
|
View,
|
|
10
10
|
useWindowDimensions,
|
|
11
|
-
} from 'react-native';
|
|
12
|
-
import Icon from 'react-native-vector-icons/Feather';
|
|
13
|
-
import { useStore } from '@store';
|
|
14
|
-
import { resolveThemePalette, withOpacity } from '@controleonline/../../src/styles/branding';
|
|
15
|
-
import { colors } from '@controleonline/../../src/styles/colors';
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
import Icon from 'react-native-vector-icons/Feather';
|
|
13
|
+
import { getAllStores, useStore } from '@store';
|
|
14
|
+
import { resolveThemePalette, withOpacity } from '@controleonline/../../src/styles/branding';
|
|
15
|
+
import { colors } from '@controleonline/../../src/styles/colors';
|
|
16
|
+
import {
|
|
17
|
+
buildOptionsFromColumn,
|
|
18
|
+
getColumnKey,
|
|
19
|
+
isDateLikeColumn,
|
|
20
|
+
resolveStoreNameFromList,
|
|
21
|
+
} from '../inputs/defaultInputUtils';
|
|
16
22
|
import CompactFilterSelector from './CompactFilterSelector';
|
|
17
23
|
import DateShortcutFilter from './DateShortcutFilter';
|
|
18
24
|
import { resolveNextDateFilterValue } from './dateFilterSelection';
|
|
19
25
|
import styles from './DefaultExternalFilters.styles';
|
|
20
26
|
|
|
21
|
-
const DEFAULT_COMPACT_BREAKPOINT = 768;
|
|
22
|
-
const noop = () => {};
|
|
23
|
-
|
|
24
|
-
const normalizeText = value => String(value || '').trim();
|
|
25
|
-
const
|
|
26
|
-
'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
27
|
+
const DEFAULT_COMPACT_BREAKPOINT = 768;
|
|
28
|
+
const noop = () => {};
|
|
29
|
+
|
|
30
|
+
const normalizeText = value => String(value || '').trim();
|
|
31
|
+
const resolveListActionName = list =>
|
|
32
|
+
normalizeText(list).split('/')[1] || 'getItems';
|
|
33
|
+
|
|
34
|
+
const COMPANY_SCOPED_LIST_STORES = new Set([
|
|
35
|
+
'categories',
|
|
36
|
+
'paymentType',
|
|
37
|
+
'wallet',
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const resolveListLoadParams = ({currentCompanyId, listStoreName}) => {
|
|
41
|
+
if (!currentCompanyId || !COMPANY_SCOPED_LIST_STORES.has(listStoreName)) {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return listStoreName === 'categories'
|
|
46
|
+
? {company: currentCompanyId}
|
|
47
|
+
: {people: currentCompanyId};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const resolveColumnListLoadParams = ({
|
|
51
|
+
column,
|
|
52
|
+
currentCompanyId,
|
|
53
|
+
requestParams = {},
|
|
54
|
+
searchValue = '',
|
|
55
|
+
}) => {
|
|
56
|
+
const listStoreName = resolveStoreNameFromList(column?.list);
|
|
57
|
+
const resolvedCustomParams = typeof column?.listRequestParams === 'function'
|
|
58
|
+
? column.listRequestParams({currentCompanyId, requestParams})
|
|
59
|
+
: column?.listRequestParams;
|
|
60
|
+
const customParams =
|
|
61
|
+
resolvedCustomParams &&
|
|
62
|
+
typeof resolvedCustomParams === 'object' &&
|
|
63
|
+
!Array.isArray(resolvedCustomParams)
|
|
64
|
+
? resolvedCustomParams
|
|
65
|
+
: {};
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
...resolveListLoadParams({currentCompanyId, listStoreName}),
|
|
69
|
+
...customParams,
|
|
70
|
+
...(normalizeText(searchValue)
|
|
71
|
+
? {[column?.listSearchParam || column?.searchParam || 'search']: normalizeText(searchValue)}
|
|
72
|
+
: {}),
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const stableSerialize = value => {
|
|
77
|
+
if (value === null || value === undefined) return 'null';
|
|
78
|
+
if (Array.isArray(value)) return `[${value.map(item => stableSerialize(item)).join(',')}]`;
|
|
79
|
+
if (value instanceof Date) return JSON.stringify(value.toISOString());
|
|
80
|
+
if (typeof value === 'object') {
|
|
81
|
+
return `{${Object.keys(value)
|
|
82
|
+
.sort()
|
|
83
|
+
.map(key => `${JSON.stringify(key)}:${stableSerialize(value[key])}`)
|
|
84
|
+
.join(',')}}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return JSON.stringify(value);
|
|
88
|
+
};
|
|
89
|
+
|
|
64
90
|
const shouldIncludeColumn = column =>
|
|
65
91
|
Boolean(getColumnKey(column)) &&
|
|
66
92
|
column?.show !== false &&
|
|
@@ -69,13 +95,13 @@ const shouldIncludeColumn = column =>
|
|
|
69
95
|
column?.filter !== false &&
|
|
70
96
|
column?.filters !== false;
|
|
71
97
|
|
|
72
|
-
const normalizeFilterValue = value => {
|
|
73
|
-
if (value && typeof value === 'object') {
|
|
74
|
-
return normalizeFilterValue(value.value ?? value.id ?? value['@id'] ?? '');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return normalizeText(value);
|
|
78
|
-
};
|
|
98
|
+
const normalizeFilterValue = value => {
|
|
99
|
+
if (value && typeof value === 'object') {
|
|
100
|
+
return normalizeFilterValue(value.value ?? value.id ?? value['@id'] ?? value.key ?? '');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return normalizeText(value);
|
|
104
|
+
};
|
|
79
105
|
|
|
80
106
|
const isFilledFilterValue = value => {
|
|
81
107
|
if (Array.isArray(value)) return value.length > 0;
|
|
@@ -86,52 +112,6 @@ const isFilledFilterValue = value => {
|
|
|
86
112
|
return normalizeText(value) !== '';
|
|
87
113
|
};
|
|
88
114
|
|
|
89
|
-
const resolveOptionLabel = (column, option, storeName = '') => {
|
|
90
|
-
if (!option) return '';
|
|
91
|
-
|
|
92
|
-
const translateOptionLabel = rawLabel => {
|
|
93
|
-
const normalizedLabel = normalizeText(rawLabel);
|
|
94
|
-
if (!shouldTranslateOptionLabel(column, storeName, normalizedLabel)) {
|
|
95
|
-
return normalizedLabel;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return global.t?.t(storeName, 'label', normalizedLabel) || normalizedLabel;
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
if (typeof column?.formatList === 'function') {
|
|
102
|
-
const formatted = column.formatList(option, null, column);
|
|
103
|
-
if (formatted && typeof formatted === 'object') {
|
|
104
|
-
return translateOptionLabel(formatted.label ?? formatted.value);
|
|
105
|
-
}
|
|
106
|
-
if (formatted) return translateOptionLabel(formatted);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const rawLabel = normalizeText(
|
|
110
|
-
option.label ??
|
|
111
|
-
option[column?.searchParam] ??
|
|
112
|
-
option[column?.name] ??
|
|
113
|
-
option.name ??
|
|
114
|
-
option.status ??
|
|
115
|
-
option.wallet ??
|
|
116
|
-
option.paymentType ??
|
|
117
|
-
option.alias ??
|
|
118
|
-
option.id,
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
return translateOptionLabel(rawLabel);
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const buildColumnOptions = (column, options = [], storeName = '') => [
|
|
125
|
-
{
|
|
126
|
-
key: '',
|
|
127
|
-
label: global.t?.t(storeName || 'invoice', 'label', 'select'),
|
|
128
|
-
},
|
|
129
|
-
...(Array.isArray(options) ? options : []).map(option => ({
|
|
130
|
-
key: normalizeFilterValue(option),
|
|
131
|
-
label: resolveOptionLabel(column, option, storeName) || '-',
|
|
132
|
-
})),
|
|
133
|
-
];
|
|
134
|
-
|
|
135
115
|
const resolveDateState = filterValue => {
|
|
136
116
|
if (!filterValue || typeof filterValue !== 'object') {
|
|
137
117
|
return {
|
|
@@ -167,27 +147,29 @@ const resolveIcon = column => {
|
|
|
167
147
|
const key = getColumnKey(column);
|
|
168
148
|
if (key === 'status') return 'check-circle';
|
|
169
149
|
if (key === 'category') return 'tag';
|
|
170
|
-
if (column
|
|
150
|
+
if (isDateLikeColumn(column)) return 'calendar';
|
|
171
151
|
return 'sliders';
|
|
172
152
|
};
|
|
173
153
|
|
|
174
154
|
const DefaultExternalFilters = ({
|
|
175
|
-
accentColor = null,
|
|
176
|
-
compactBreakpoint = DEFAULT_COMPACT_BREAKPOINT,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
storeName = '',
|
|
184
|
-
}) => {
|
|
185
|
-
const { width } = useWindowDimensions();
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
const
|
|
155
|
+
accentColor = null,
|
|
156
|
+
compactBreakpoint = DEFAULT_COMPACT_BREAKPOINT,
|
|
157
|
+
dateOptionKeys = ['all', 'today', 'yesterday', '7d', '30d', 'custom'],
|
|
158
|
+
filters = {},
|
|
159
|
+
getOptionsForColumn = null,
|
|
160
|
+
onActiveCountChange = null,
|
|
161
|
+
onChangeFilters = null,
|
|
162
|
+
requestParams = {},
|
|
163
|
+
storeName = '',
|
|
164
|
+
}) => {
|
|
165
|
+
const { width } = useWindowDimensions();
|
|
166
|
+
const externalFilterStore = useStore(storeName);
|
|
167
|
+
const peopleStore = useStore('people');
|
|
168
|
+
const themeStore = useStore('theme');
|
|
169
|
+
const [isFiltersModalOpen, setIsFiltersModalOpen] = useState(false);
|
|
170
|
+
const loadedListStoresRef = useRef(new Set());
|
|
171
|
+
const { currentCompany } = peopleStore.getters || {};
|
|
172
|
+
const { colors: themeColors } = themeStore.getters;
|
|
191
173
|
const themeTokens = useMemo(
|
|
192
174
|
() => ({...themeColors, ...(currentCompany?.theme?.colors || {})}),
|
|
193
175
|
[currentCompany?.theme?.colors, themeColors],
|
|
@@ -199,15 +181,24 @@ const DefaultExternalFilters = ({
|
|
|
199
181
|
const resolvedAccentColor = accentColor || palette.primary;
|
|
200
182
|
const surfaceColor = themeTokens['bg-odd-light'] || palette.background;
|
|
201
183
|
const panelColor = themeTokens['bg-headers-light'] || palette.background;
|
|
202
|
-
const borderColor = palette.border;
|
|
203
|
-
const textColor = palette.text;
|
|
204
|
-
const textSecondaryColor = palette.textSecondary;
|
|
205
|
-
const
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
184
|
+
const borderColor = palette.border;
|
|
185
|
+
const textColor = palette.text;
|
|
186
|
+
const textSecondaryColor = palette.textSecondary;
|
|
187
|
+
const buttonBackgroundColor = themeColors.buttonBackground;
|
|
188
|
+
const buttonTextColor = themeColors.buttonText;
|
|
189
|
+
const tableFilterBackgroundColor = themeColors.tableFilterBackground;
|
|
190
|
+
const tableFilterBorderColor = themeColors.tableFilterBorder;
|
|
191
|
+
const tableFilterTextColor = themeColors.tableFilterText;
|
|
192
|
+
const onAccentColor = palette.secondary || palette.text;
|
|
193
|
+
const isCompactView = width > 0 && width <= compactBreakpoint;
|
|
194
|
+
const resolvedColumns = useMemo(() => {
|
|
195
|
+
const storeColumns = externalFilterStore?.getters?.columns;
|
|
196
|
+
return Array.isArray(storeColumns) ? storeColumns : [];
|
|
197
|
+
}, [externalFilterStore?.getters?.columns]);
|
|
198
|
+
const filterColumns = useMemo(
|
|
199
|
+
() => resolvedColumns.filter(shouldIncludeColumn),
|
|
200
|
+
[resolvedColumns],
|
|
201
|
+
);
|
|
211
202
|
const activeCount = useMemo(
|
|
212
203
|
() => filterColumns.filter(column => isFilledFilterValue(filters[getColumnKey(column)])).length,
|
|
213
204
|
[filterColumns, filters],
|
|
@@ -231,23 +222,80 @@ const DefaultExternalFilters = ({
|
|
|
231
222
|
onChangeFilters?.({});
|
|
232
223
|
}, [onChangeFilters]);
|
|
233
224
|
|
|
234
|
-
useEffect(() => {
|
|
235
|
-
if (!isCompactView) setIsFiltersModalOpen(false);
|
|
236
|
-
}, [isCompactView]);
|
|
237
|
-
|
|
238
|
-
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (!isCompactView) setIsFiltersModalOpen(false);
|
|
227
|
+
}, [isCompactView]);
|
|
228
|
+
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
loadedListStoresRef.current.clear();
|
|
231
|
+
}, [currentCompany?.id]);
|
|
232
|
+
|
|
233
|
+
const loadListOptionsForColumn = useCallback((column, searchValue = '') => {
|
|
234
|
+
const explicitOptions = getOptionsForColumn?.(column);
|
|
235
|
+
if (!normalizeText(searchValue) && Array.isArray(explicitOptions) && explicitOptions.length > 0) {
|
|
236
|
+
return Promise.resolve([]);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const listStoreName = resolveStoreNameFromList(column?.list);
|
|
240
|
+
const actionName = resolveListActionName(column?.list);
|
|
241
|
+
const stores = getAllStores?.() || {};
|
|
242
|
+
const listStore = stores?.[listStoreName];
|
|
243
|
+
const listAction = listStore?.actions?.[actionName];
|
|
244
|
+
|
|
245
|
+
if (!listStoreName || typeof listAction !== 'function') {
|
|
246
|
+
return Promise.resolve([]);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const listLoadParams = resolveColumnListLoadParams({
|
|
250
|
+
column,
|
|
251
|
+
currentCompanyId: currentCompany?.id,
|
|
252
|
+
requestParams,
|
|
253
|
+
searchValue,
|
|
254
|
+
});
|
|
255
|
+
const hasScopedParams = Object.keys(listLoadParams).length > 0;
|
|
256
|
+
|
|
257
|
+
if (
|
|
258
|
+
!normalizeText(searchValue) &&
|
|
259
|
+
!hasScopedParams &&
|
|
260
|
+
Array.isArray(listStore?.getters?.items) &&
|
|
261
|
+
listStore.getters.items.length > 0
|
|
262
|
+
) {
|
|
263
|
+
return Promise.resolve(listStore.getters.items);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const loadKey = `${getColumnKey(column)}:${listStoreName}:${actionName}:${stableSerialize(listLoadParams)}`;
|
|
267
|
+
if (loadedListStoresRef.current.has(loadKey)) {
|
|
268
|
+
return Promise.resolve(listStore?.getters?.items || []);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
loadedListStoresRef.current.add(loadKey);
|
|
272
|
+
return Promise.resolve(
|
|
273
|
+
listAction({
|
|
274
|
+
...listLoadParams,
|
|
275
|
+
__storeMeta: {
|
|
276
|
+
dedupeKey: `default-external-filter-list-options:${loadKey}`,
|
|
277
|
+
skipSystemError: true,
|
|
278
|
+
},
|
|
279
|
+
}),
|
|
280
|
+
).catch(error => {
|
|
281
|
+
loadedListStoresRef.current.delete(loadKey);
|
|
282
|
+
throw error;
|
|
283
|
+
});
|
|
284
|
+
}, [currentCompany?.id, getOptionsForColumn, requestParams]);
|
|
285
|
+
|
|
286
|
+
if (filterColumns.length === 0) return null;
|
|
239
287
|
|
|
240
288
|
const renderFilterField = (column, compact = false) => {
|
|
241
289
|
const key = getColumnKey(column);
|
|
242
290
|
const fieldStyle = compact ? styles.modalField : styles.field;
|
|
243
291
|
|
|
244
|
-
if (column
|
|
292
|
+
if (isDateLikeColumn(column)) {
|
|
245
293
|
const dateState = resolveDateState(filters[key]);
|
|
246
294
|
|
|
247
295
|
return (
|
|
248
296
|
<View key={key} style={fieldStyle}>
|
|
249
|
-
<DateShortcutFilter
|
|
250
|
-
value={dateState.value}
|
|
297
|
+
<DateShortcutFilter
|
|
298
|
+
value={dateState.value}
|
|
251
299
|
onChange={optionKey => {
|
|
252
300
|
updateFilter(
|
|
253
301
|
key,
|
|
@@ -263,9 +311,10 @@ const DefaultExternalFilters = ({
|
|
|
263
311
|
});
|
|
264
312
|
}}
|
|
265
313
|
dense
|
|
266
|
-
store={storeName}
|
|
267
|
-
field={key}
|
|
268
|
-
|
|
314
|
+
store={storeName}
|
|
315
|
+
field={key}
|
|
316
|
+
labelCaption={global.t?.t(storeName, 'label', column.label || key)}
|
|
317
|
+
optionKeys={dateOptionKeys}
|
|
269
318
|
colors={{
|
|
270
319
|
accent: resolvedAccentColor,
|
|
271
320
|
appBg: 'transparent',
|
|
@@ -283,11 +332,22 @@ const DefaultExternalFilters = ({
|
|
|
283
332
|
/>
|
|
284
333
|
</View>
|
|
285
334
|
);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (column.list) {
|
|
289
|
-
const
|
|
290
|
-
const
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (column.list) {
|
|
338
|
+
const listStoreName = resolveStoreNameFromList(column?.list);
|
|
339
|
+
const options = [
|
|
340
|
+
{
|
|
341
|
+
key: '',
|
|
342
|
+
label:
|
|
343
|
+
(column?.emptyOptionLabel
|
|
344
|
+
? global.t?.t(storeName || 'invoice', 'label', column.emptyOptionLabel) || column.emptyOptionLabel
|
|
345
|
+
: null) ||
|
|
346
|
+
global.t?.t(storeName || 'invoice', 'label', 'select'),
|
|
347
|
+
},
|
|
348
|
+
...buildOptionsFromColumn(column, getOptionsForColumn, storeName),
|
|
349
|
+
];
|
|
350
|
+
const selectedKey = normalizeFilterValue(filters[key]);
|
|
291
351
|
const selectedLabel =
|
|
292
352
|
options.find(option => option.key === selectedKey)?.label ||
|
|
293
353
|
options[0]?.label ||
|
|
@@ -300,11 +360,14 @@ const DefaultExternalFilters = ({
|
|
|
300
360
|
label={selectedLabel}
|
|
301
361
|
accentColor={resolvedAccentColor}
|
|
302
362
|
active={Boolean(selectedKey)}
|
|
303
|
-
dense
|
|
304
|
-
store={storeName}
|
|
305
|
-
field={key}
|
|
306
|
-
|
|
307
|
-
|
|
363
|
+
dense
|
|
364
|
+
store={storeName}
|
|
365
|
+
field={key}
|
|
366
|
+
onBeforeOpen={listStoreName ? () => loadListOptionsForColumn(column) : null}
|
|
367
|
+
onSearchChange={listStoreName ? value => loadListOptionsForColumn(column, value) : null}
|
|
368
|
+
options={options}
|
|
369
|
+
searchable
|
|
370
|
+
selectedKey={selectedKey}
|
|
308
371
|
onSelect={optionKey => {
|
|
309
372
|
updateFilter(key, optionKey);
|
|
310
373
|
return true;
|
|
@@ -332,33 +395,33 @@ const DefaultExternalFilters = ({
|
|
|
332
395
|
const filterTitle = global.t?.t(storeName, 'label', 'filters');
|
|
333
396
|
const filterFields = compact => filterColumns.map(column => renderFilterField(column, compact));
|
|
334
397
|
|
|
335
|
-
if (isCompactView) {
|
|
398
|
+
if (isCompactView) {
|
|
336
399
|
return (
|
|
337
400
|
<View style={styles.mobileWrap}>
|
|
338
|
-
<TouchableOpacity
|
|
339
|
-
style={[
|
|
340
|
-
styles.mobileButton,
|
|
341
|
-
{ borderColor: borderColor, backgroundColor: surfaceColor },
|
|
342
|
-
activeCount > 0 ? { borderColor:
|
|
343
|
-
]}
|
|
344
|
-
activeOpacity={0.84}
|
|
345
|
-
onPress={() => setIsFiltersModalOpen(true)}
|
|
346
|
-
>
|
|
347
|
-
<Icon name="filter" size={15} color={activeCount > 0 ?
|
|
348
|
-
<Text
|
|
349
|
-
style={[
|
|
350
|
-
styles.mobileButtonText,
|
|
351
|
-
{ color: textColor },
|
|
352
|
-
activeCount > 0 ? { color:
|
|
353
|
-
]}
|
|
354
|
-
>
|
|
355
|
-
{filterTitle}
|
|
356
|
-
</Text>
|
|
357
|
-
{activeCount > 0 ? (
|
|
358
|
-
<View style={[styles.mobileCountBadge, { backgroundColor:
|
|
359
|
-
<Text style={styles.mobileCountBadgeText}>{activeCount}</Text>
|
|
360
|
-
</View>
|
|
361
|
-
) : null}
|
|
401
|
+
<TouchableOpacity
|
|
402
|
+
style={[
|
|
403
|
+
styles.mobileButton,
|
|
404
|
+
{ borderColor: borderColor, backgroundColor: surfaceColor },
|
|
405
|
+
activeCount > 0 ? { borderColor: tableFilterBorderColor, backgroundColor: tableFilterBackgroundColor } : null,
|
|
406
|
+
]}
|
|
407
|
+
activeOpacity={0.84}
|
|
408
|
+
onPress={() => setIsFiltersModalOpen(true)}
|
|
409
|
+
>
|
|
410
|
+
<Icon name="filter" size={15} color={activeCount > 0 ? tableFilterTextColor : textSecondaryColor} />
|
|
411
|
+
<Text
|
|
412
|
+
style={[
|
|
413
|
+
styles.mobileButtonText,
|
|
414
|
+
{ color: textColor },
|
|
415
|
+
activeCount > 0 ? { color: tableFilterTextColor } : null,
|
|
416
|
+
]}
|
|
417
|
+
>
|
|
418
|
+
{filterTitle}
|
|
419
|
+
</Text>
|
|
420
|
+
{activeCount > 0 ? (
|
|
421
|
+
<View style={[styles.mobileCountBadge, { backgroundColor: tableFilterBorderColor }]}>
|
|
422
|
+
<Text style={styles.mobileCountBadgeText}>{activeCount}</Text>
|
|
423
|
+
</View>
|
|
424
|
+
) : null}
|
|
362
425
|
</TouchableOpacity>
|
|
363
426
|
|
|
364
427
|
<Modal
|
|
@@ -402,12 +465,12 @@ const DefaultExternalFilters = ({
|
|
|
402
465
|
</Text>
|
|
403
466
|
</TouchableOpacity>
|
|
404
467
|
) : null}
|
|
405
|
-
<TouchableOpacity
|
|
406
|
-
style={[styles.modalPrimaryButton, { backgroundColor:
|
|
407
|
-
activeOpacity={0.84}
|
|
408
|
-
onPress={() => setIsFiltersModalOpen(false)}
|
|
409
|
-
>
|
|
410
|
-
<Text style={[styles.modalPrimaryButtonText, { color:
|
|
468
|
+
<TouchableOpacity
|
|
469
|
+
style={[styles.modalPrimaryButton, { backgroundColor: buttonBackgroundColor }]}
|
|
470
|
+
activeOpacity={0.84}
|
|
471
|
+
onPress={() => setIsFiltersModalOpen(false)}
|
|
472
|
+
>
|
|
473
|
+
<Text style={[styles.modalPrimaryButtonText, { color: buttonTextColor }]}>
|
|
411
474
|
{global.t?.t(storeName, 'button', 'apply')}
|
|
412
475
|
</Text>
|
|
413
476
|
</TouchableOpacity>
|