@controleonline/ui-default 1.0.264 → 1.0.266
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 +3 -2
- package/package.json +21 -18
- package/src/react/components/errors/DefaultErrors.js +64 -82
- package/src/react/components/filters/CompactFilterSelector.js +58 -23
- package/src/react/components/filters/CompactFilterSelector.styles.js +17 -5
- package/src/react/components/filters/DefaultColumnFilter.js +177 -45
- package/src/react/components/filters/DefaultExternalFilters.js +201 -143
- package/src/react/components/filters/DefaultSearch.js +27 -25
- package/src/react/components/form/DefaultForm.js +72 -53
- package/src/react/components/form/DefaultForm.styles.js +71 -66
- package/src/react/components/inputs/DefaultDateInput.js +150 -0
- package/src/react/components/inputs/DefaultInput.js +44 -11
- package/src/react/components/inputs/DefaultInput.styles.js +6 -0
- package/src/react/components/inputs/DefaultSelect.js +200 -43
- package/src/react/components/inputs/defaultInputUtils.js +40 -19
- package/src/react/components/table/DefaultColumnMenu.js +108 -0
- package/src/react/components/table/DefaultDebug.js +234 -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 +322 -1964
- package/src/react/components/table/DefaultTable.styles.js +234 -102
- 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 +205 -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 +344 -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 +52 -0
- 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/form/DefaultForm.test.js +81 -0
- package/src/tests/react/components/inputs/DefaultSelect.test.js +106 -14
- package/src/tests/react/components/table/DefaultTable.test.js +1318 -334
- package/src/tests/react/store/defaultActions.test.js +59 -0
- package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +247 -168
|
@@ -25,7 +25,18 @@ const DEFAULT_TRANSLATABLE_FIELDS = new Set([
|
|
|
25
25
|
'status',
|
|
26
26
|
]);
|
|
27
27
|
|
|
28
|
-
export const getColumnKey = column => column?.key || column?.name || '';
|
|
28
|
+
export const getColumnKey = column => column?.key || column?.name || '';
|
|
29
|
+
|
|
30
|
+
export const isDateColumn = column =>
|
|
31
|
+
column?.inputType === 'date' ||
|
|
32
|
+
column?.type === 'date';
|
|
33
|
+
|
|
34
|
+
export const isDateRangeColumn = column =>
|
|
35
|
+
column?.inputType === 'date-range' ||
|
|
36
|
+
column?.type === 'range-date';
|
|
37
|
+
|
|
38
|
+
export const isDateLikeColumn = column =>
|
|
39
|
+
isDateColumn(column) || isDateRangeColumn(column);
|
|
29
40
|
|
|
30
41
|
const normalizeColumnKey = value =>
|
|
31
42
|
normalizeText(value)
|
|
@@ -114,7 +125,8 @@ export const resolveOptionLabel = (column, option, storeName = '') => {
|
|
|
114
125
|
return translateOptionLabel(rawLabel);
|
|
115
126
|
};
|
|
116
127
|
|
|
117
|
-
export const resolveStoreNameFromList = list =>
|
|
128
|
+
export const resolveStoreNameFromList = list =>
|
|
129
|
+
typeof list === 'string' ? normalizeText(list).split('/')[0] || '' : '';
|
|
118
130
|
|
|
119
131
|
const filterItemsByListRequestParams = (items = [], column = null) => {
|
|
120
132
|
const requestParams =
|
|
@@ -164,13 +176,17 @@ export const mapOptions = (column, items = [], storeName = '') => {
|
|
|
164
176
|
return options;
|
|
165
177
|
};
|
|
166
178
|
|
|
167
|
-
export const buildOptionsFromColumn = (column, getOptionsForColumn = null, storeName = '') => {
|
|
168
|
-
const explicitOptions = getOptionsForColumn?.(column);
|
|
169
|
-
if (Array.isArray(explicitOptions) && explicitOptions.length > 0) {
|
|
170
|
-
return mapOptions(column, explicitOptions, storeName);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
export const buildOptionsFromColumn = (column, getOptionsForColumn = null, storeName = '') => {
|
|
180
|
+
const explicitOptions = getOptionsForColumn?.(column);
|
|
181
|
+
if (Array.isArray(explicitOptions) && explicitOptions.length > 0) {
|
|
182
|
+
return mapOptions(column, explicitOptions, storeName);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (Array.isArray(column?.list)) {
|
|
186
|
+
return mapOptions(column, column.list, storeName);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const listStoreName = resolveStoreNameFromList(column?.list);
|
|
174
190
|
if (!listStoreName) return [];
|
|
175
191
|
|
|
176
192
|
const listStore = getAllStores()?.[listStoreName];
|
|
@@ -209,9 +225,9 @@ export const resolveEditValue = (column, row, value) => {
|
|
|
209
225
|
return normalizeText(column.editFormat(rawValue, column, row, true));
|
|
210
226
|
}
|
|
211
227
|
|
|
212
|
-
if ((column
|
|
213
|
-
return normalizeText(Formatter.formatDateYmdTodmY(rawValue));
|
|
214
|
-
}
|
|
228
|
+
if (isDateLikeColumn(column) && rawValue) {
|
|
229
|
+
return normalizeText(Formatter.formatDateYmdTodmY(rawValue));
|
|
230
|
+
}
|
|
215
231
|
|
|
216
232
|
return normalizeText(rawValue);
|
|
217
233
|
};
|
|
@@ -221,10 +237,15 @@ export const formatSaveValue = (column, value, row) => {
|
|
|
221
237
|
return column.saveFormat(value, column, row);
|
|
222
238
|
}
|
|
223
239
|
|
|
224
|
-
if (value && typeof value === 'object') {
|
|
225
|
-
if (value.value) return Number.isNaN(Number(value.value)) ? value.value : Number(value.value);
|
|
226
|
-
if (value['@id']) return value['@id'];
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
240
|
+
if (value && typeof value === 'object') {
|
|
241
|
+
if (value.value) return Number.isNaN(Number(value.value)) ? value.value : Number(value.value);
|
|
242
|
+
if (value['@id']) return value['@id'];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (isDateLikeColumn(column) && value) {
|
|
246
|
+
const normalizedDate = Formatter.buildAmericanDate(String(value));
|
|
247
|
+
return normalizedDate || value;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return Number.isNaN(Number(value)) || value === '' ? value : Number(value);
|
|
251
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Modal, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
|
3
|
+
import Icon from 'react-native-vector-icons/Feather';
|
|
4
|
+
import { useStore } from '@store';
|
|
5
|
+
import { formatStoreColumnLabel } from '@controleonline/ui-common/src/react/utils/storeColumns';
|
|
6
|
+
import { getColumnKey } from '../inputs/defaultInputUtils';
|
|
7
|
+
import {
|
|
8
|
+
persistVisibleColumnsPreference,
|
|
9
|
+
resolveDefaultTablePreferenceScope,
|
|
10
|
+
sanitizeVisibleColumnsPreference,
|
|
11
|
+
} from '../../utils/tableVisibleColumnsPreferences';
|
|
12
|
+
import styles from './DefaultTable.styles';
|
|
13
|
+
import { shouldIncludeColumn } from './DefaultTable.utils';
|
|
14
|
+
import useDefaultTableTheme from './useDefaultTableTheme';
|
|
15
|
+
|
|
16
|
+
const DefaultColumnMenu = ({ storeName, visible = false, onClose }) => {
|
|
17
|
+
const store = useStore(storeName);
|
|
18
|
+
const columns = Array.isArray(store?.getters?.columns) ? store.getters.columns : [];
|
|
19
|
+
const visibleColumns = store?.getters?.visibleColumns || {};
|
|
20
|
+
const configs = store?.getters?.configs || {};
|
|
21
|
+
const tablePreferenceScope =
|
|
22
|
+
configs.tablePreferenceScope ||
|
|
23
|
+
resolveDefaultTablePreferenceScope({ storeName });
|
|
24
|
+
const availableColumns = columns.filter(column => shouldIncludeColumn(column));
|
|
25
|
+
const {
|
|
26
|
+
checkboxBorderColor: resolvedCheckboxBorderColor,
|
|
27
|
+
checkboxSelectedMarkColor: resolvedCheckboxSelectedMarkColor,
|
|
28
|
+
modalColors,
|
|
29
|
+
} = useDefaultTableTheme();
|
|
30
|
+
if (!visible) return null;
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
backgroundColor,
|
|
34
|
+
borderColor,
|
|
35
|
+
closeIconColor,
|
|
36
|
+
headerTextColor,
|
|
37
|
+
overlayColor,
|
|
38
|
+
textColor,
|
|
39
|
+
} = modalColors;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Modal visible transparent animationType="fade" onRequestClose={onClose}>
|
|
43
|
+
<View style={[styles.modalOverlay, { backgroundColor: overlayColor }]}>
|
|
44
|
+
<View style={[styles.modalCard, styles.columnMenuModalCard, { borderColor, backgroundColor }]}>
|
|
45
|
+
<View style={[styles.modalHeader, { borderBottomColor: borderColor }]}>
|
|
46
|
+
<Text style={[styles.modalTitle, { color: headerTextColor }]} numberOfLines={1}>
|
|
47
|
+
{global.t?.t(storeName, 'label', 'columns')}
|
|
48
|
+
</Text>
|
|
49
|
+
<TouchableOpacity
|
|
50
|
+
style={[styles.modalCloseButton, { borderColor, backgroundColor }]}
|
|
51
|
+
activeOpacity={0.82}
|
|
52
|
+
onPress={onClose}
|
|
53
|
+
>
|
|
54
|
+
<Icon name="x" size={16} color={closeIconColor} />
|
|
55
|
+
</TouchableOpacity>
|
|
56
|
+
</View>
|
|
57
|
+
<ScrollView style={styles.columnMenuModalBody} contentContainerStyle={styles.columnMenuModalList}>
|
|
58
|
+
{availableColumns.map(column => {
|
|
59
|
+
const fieldName = getColumnKey(column);
|
|
60
|
+
const label = formatStoreColumnLabel({
|
|
61
|
+
columns,
|
|
62
|
+
fieldName,
|
|
63
|
+
fallbackLabel: column?.label || fieldName,
|
|
64
|
+
storeName,
|
|
65
|
+
});
|
|
66
|
+
const checked = visibleColumns[fieldName] !== false;
|
|
67
|
+
const toggleColumn = () => {
|
|
68
|
+
const nextVisibleColumns = sanitizeVisibleColumnsPreference({
|
|
69
|
+
columns,
|
|
70
|
+
visibleColumns: {
|
|
71
|
+
...visibleColumns,
|
|
72
|
+
[fieldName]: visibleColumns[fieldName] === false,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
persistVisibleColumnsPreference(tablePreferenceScope, nextVisibleColumns);
|
|
77
|
+
|
|
78
|
+
if (typeof store?.actions?.setVisibleColumns === 'function') {
|
|
79
|
+
store.actions.setVisibleColumns(nextVisibleColumns);
|
|
80
|
+
} else if (store?.getters) {
|
|
81
|
+
store.getters.visibleColumns = nextVisibleColumns;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<TouchableOpacity
|
|
87
|
+
key={fieldName}
|
|
88
|
+
style={styles.columnMenuItem}
|
|
89
|
+
activeOpacity={0.82}
|
|
90
|
+
onPress={toggleColumn}
|
|
91
|
+
>
|
|
92
|
+
<Icon
|
|
93
|
+
name={checked ? 'check-square' : 'square'}
|
|
94
|
+
size={16}
|
|
95
|
+
color={checked ? resolvedCheckboxSelectedMarkColor : resolvedCheckboxBorderColor}
|
|
96
|
+
/>
|
|
97
|
+
<Text style={[styles.columnMenuText, { color: textColor }]} numberOfLines={1}>{label}</Text>
|
|
98
|
+
</TouchableOpacity>
|
|
99
|
+
);
|
|
100
|
+
})}
|
|
101
|
+
</ScrollView>
|
|
102
|
+
</View>
|
|
103
|
+
</View>
|
|
104
|
+
</Modal>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export default DefaultColumnMenu;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import { Modal, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
|
3
|
+
import Icon from 'react-native-vector-icons/Feather';
|
|
4
|
+
import { useStore } from '@store';
|
|
5
|
+
import { useMessage } from '@controleonline/ui-common/src/react/components/MessageService';
|
|
6
|
+
import { normalizeText } from '../inputs/defaultInputUtils';
|
|
7
|
+
import styles from './DefaultTable.styles';
|
|
8
|
+
import useDefaultTableTheme from './useDefaultTableTheme';
|
|
9
|
+
|
|
10
|
+
const isObject = value =>
|
|
11
|
+
value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
12
|
+
|
|
13
|
+
const formatDebugValue = value => {
|
|
14
|
+
if (value === undefined) return '';
|
|
15
|
+
if (typeof value === 'string') return value;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
return JSON.stringify(value, null, 2);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
return normalizeText(value);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const copyTextToClipboard = async text => {
|
|
25
|
+
const normalizedText = normalizeText(text);
|
|
26
|
+
if (!normalizedText) return false;
|
|
27
|
+
|
|
28
|
+
if (
|
|
29
|
+
typeof navigator !== 'undefined' &&
|
|
30
|
+
navigator.clipboard &&
|
|
31
|
+
typeof navigator.clipboard.writeText === 'function'
|
|
32
|
+
) {
|
|
33
|
+
await navigator.clipboard.writeText(normalizedText);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const DebugCopyButton = ({ borderColor, color, onPress }) => (
|
|
41
|
+
<TouchableOpacity
|
|
42
|
+
style={[styles.debugCopyButton, { borderColor }]}
|
|
43
|
+
activeOpacity={0.82}
|
|
44
|
+
onPress={onPress}
|
|
45
|
+
>
|
|
46
|
+
<Icon name="copy" size={13} color={color} />
|
|
47
|
+
<Text style={[styles.debugCopyButtonText, { color }]}>Copiar</Text>
|
|
48
|
+
</TouchableOpacity>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const DebugSection = ({ borderColor, children, copyText, onCopy, textColor, title }) => (
|
|
52
|
+
<>
|
|
53
|
+
<View style={styles.debugSectionHeader}>
|
|
54
|
+
<Text style={[styles.debugSectionTitle, { color: textColor }]}>{title}</Text>
|
|
55
|
+
<DebugCopyButton
|
|
56
|
+
borderColor={borderColor}
|
|
57
|
+
color={textColor}
|
|
58
|
+
onPress={() => onCopy(copyText)}
|
|
59
|
+
/>
|
|
60
|
+
</View>
|
|
61
|
+
<Text selectable style={[styles.debugCodeBlock, { color: textColor, borderColor }]}>
|
|
62
|
+
{children}
|
|
63
|
+
</Text>
|
|
64
|
+
</>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const DefaultDebug = ({
|
|
68
|
+
storeName,
|
|
69
|
+
}) => {
|
|
70
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
71
|
+
const store = useStore(storeName);
|
|
72
|
+
const configs = store?.getters?.configs || {};
|
|
73
|
+
const { showError, showSuccess } = useMessage();
|
|
74
|
+
const { modalColors, resolvedAccentColor, tableButtonColors } = useDefaultTableTheme();
|
|
75
|
+
|
|
76
|
+
const resolvedDebug = isObject(store?.getters?.debug) ? store.getters.debug : {};
|
|
77
|
+
const debugQuery = normalizeText(resolvedDebug?.query);
|
|
78
|
+
const debugFilledQuery = normalizeText(
|
|
79
|
+
resolvedDebug?.filledQuery || resolvedDebug?.interpolatedQuery,
|
|
80
|
+
);
|
|
81
|
+
const hasDebugQuery = debugQuery !== '';
|
|
82
|
+
const debugParameters = isObject(resolvedDebug?.parameters)
|
|
83
|
+
? resolvedDebug.parameters
|
|
84
|
+
: configs.debugFallbackParameters || null;
|
|
85
|
+
const debugParametersText = useMemo(
|
|
86
|
+
() => formatDebugValue(debugParameters),
|
|
87
|
+
[debugParameters],
|
|
88
|
+
);
|
|
89
|
+
const debugCopyText = useMemo(
|
|
90
|
+
() =>
|
|
91
|
+
[
|
|
92
|
+
'Query:',
|
|
93
|
+
debugQuery,
|
|
94
|
+
'',
|
|
95
|
+
'Parameters:',
|
|
96
|
+
debugParametersText,
|
|
97
|
+
'',
|
|
98
|
+
'Filled query:',
|
|
99
|
+
debugFilledQuery,
|
|
100
|
+
].join('\n'),
|
|
101
|
+
[debugFilledQuery, debugParametersText, debugQuery],
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const requestCopyText = useCallback(
|
|
105
|
+
text =>
|
|
106
|
+
copyTextToClipboard(text)
|
|
107
|
+
.then(copied => {
|
|
108
|
+
if (copied) {
|
|
109
|
+
showSuccess?.('Copiado');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
showError?.('Nao foi possivel copiar automaticamente.');
|
|
114
|
+
})
|
|
115
|
+
.catch(() => {
|
|
116
|
+
showError?.('Nao foi possivel copiar automaticamente.');
|
|
117
|
+
}),
|
|
118
|
+
[showError, showSuccess],
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
if (!hasDebugQuery) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const {
|
|
126
|
+
backgroundColor,
|
|
127
|
+
borderColor,
|
|
128
|
+
closeIconColor,
|
|
129
|
+
headerTextColor,
|
|
130
|
+
overlayColor,
|
|
131
|
+
textColor,
|
|
132
|
+
} = modalColors;
|
|
133
|
+
const {
|
|
134
|
+
backgroundColor: buttonBackgroundColor,
|
|
135
|
+
borderColor: buttonBorderColor,
|
|
136
|
+
iconColor,
|
|
137
|
+
pressedBackgroundColor,
|
|
138
|
+
pressedBorderColor,
|
|
139
|
+
pressedIconColor,
|
|
140
|
+
textColor: buttonTextColor,
|
|
141
|
+
} = tableButtonColors;
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<>
|
|
145
|
+
<TouchableOpacity
|
|
146
|
+
accessibilityLabel="Debug query"
|
|
147
|
+
accessibilityRole="button"
|
|
148
|
+
style={[
|
|
149
|
+
styles.toolbarButton,
|
|
150
|
+
{ borderColor: buttonBorderColor, backgroundColor: buttonBackgroundColor },
|
|
151
|
+
isOpen
|
|
152
|
+
? { backgroundColor: pressedBackgroundColor, borderColor: pressedBorderColor }
|
|
153
|
+
: null,
|
|
154
|
+
]}
|
|
155
|
+
activeOpacity={0.82}
|
|
156
|
+
onPress={() => setIsOpen(true)}
|
|
157
|
+
>
|
|
158
|
+
<Icon name="code" size={14} color={buttonTextColor} />
|
|
159
|
+
</TouchableOpacity>
|
|
160
|
+
|
|
161
|
+
{isOpen ? (
|
|
162
|
+
<Modal visible transparent animationType="fade" onRequestClose={() => setIsOpen(false)}>
|
|
163
|
+
<View style={[styles.modalOverlay, { backgroundColor: overlayColor }]}>
|
|
164
|
+
<View style={[styles.modalCard, styles.debugModalCard, { borderColor, backgroundColor }]}>
|
|
165
|
+
<View style={[styles.modalHeader, { borderBottomColor: borderColor }]}>
|
|
166
|
+
<Text style={[styles.modalTitle, { color: headerTextColor }]} numberOfLines={1}>
|
|
167
|
+
Debug query
|
|
168
|
+
</Text>
|
|
169
|
+
<TouchableOpacity
|
|
170
|
+
style={[styles.modalCloseButton, { borderColor, backgroundColor }]}
|
|
171
|
+
activeOpacity={0.82}
|
|
172
|
+
onPress={() => setIsOpen(false)}
|
|
173
|
+
>
|
|
174
|
+
<Icon name="x" size={16} color={closeIconColor} />
|
|
175
|
+
</TouchableOpacity>
|
|
176
|
+
</View>
|
|
177
|
+
|
|
178
|
+
<ScrollView style={styles.debugModalBody} contentContainerStyle={styles.debugModalContent}>
|
|
179
|
+
<DebugSection
|
|
180
|
+
borderColor={borderColor}
|
|
181
|
+
copyText={debugQuery}
|
|
182
|
+
onCopy={requestCopyText}
|
|
183
|
+
textColor={textColor}
|
|
184
|
+
title="Query"
|
|
185
|
+
>
|
|
186
|
+
{debugQuery}
|
|
187
|
+
</DebugSection>
|
|
188
|
+
|
|
189
|
+
<DebugSection
|
|
190
|
+
borderColor={borderColor}
|
|
191
|
+
copyText={debugParametersText}
|
|
192
|
+
onCopy={requestCopyText}
|
|
193
|
+
textColor={textColor}
|
|
194
|
+
title="Parametros"
|
|
195
|
+
>
|
|
196
|
+
{debugParametersText}
|
|
197
|
+
</DebugSection>
|
|
198
|
+
|
|
199
|
+
<DebugSection
|
|
200
|
+
borderColor={borderColor}
|
|
201
|
+
copyText={debugFilledQuery}
|
|
202
|
+
onCopy={requestCopyText}
|
|
203
|
+
textColor={textColor}
|
|
204
|
+
title="Query preenchida"
|
|
205
|
+
>
|
|
206
|
+
{debugFilledQuery}
|
|
207
|
+
</DebugSection>
|
|
208
|
+
</ScrollView>
|
|
209
|
+
|
|
210
|
+
<View style={[styles.modalActions, { borderTopColor: borderColor }]}>
|
|
211
|
+
<TouchableOpacity
|
|
212
|
+
style={[styles.secondaryButton, { borderColor }]}
|
|
213
|
+
activeOpacity={0.82}
|
|
214
|
+
onPress={() => requestCopyText(debugCopyText)}
|
|
215
|
+
>
|
|
216
|
+
<Text style={[styles.secondaryButtonText, { color: textColor }]}>Copiar tudo</Text>
|
|
217
|
+
</TouchableOpacity>
|
|
218
|
+
<TouchableOpacity
|
|
219
|
+
style={[styles.primaryButton, { backgroundColor: resolvedAccentColor }]}
|
|
220
|
+
activeOpacity={0.82}
|
|
221
|
+
onPress={() => setIsOpen(false)}
|
|
222
|
+
>
|
|
223
|
+
<Text style={styles.primaryButtonText}>Fechar</Text>
|
|
224
|
+
</TouchableOpacity>
|
|
225
|
+
</View>
|
|
226
|
+
</View>
|
|
227
|
+
</View>
|
|
228
|
+
</Modal>
|
|
229
|
+
) : null}
|
|
230
|
+
</>
|
|
231
|
+
);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
export default DefaultDebug;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Modal, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
|
3
|
+
import Icon from 'react-native-vector-icons/Feather';
|
|
4
|
+
import { useStore } from '@store';
|
|
5
|
+
import { formatStoreColumnLabel } from '@controleonline/ui-common/src/react/utils/storeColumns';
|
|
6
|
+
import DefaultColumnFilter from '../filters/DefaultColumnFilter';
|
|
7
|
+
import { getColumnKey, normalizeText } from '../inputs/defaultInputUtils';
|
|
8
|
+
import styles from './DefaultTable.styles';
|
|
9
|
+
import { shouldIncludeColumn } from './DefaultTable.utils';
|
|
10
|
+
import {
|
|
11
|
+
persistTableFiltersPreference,
|
|
12
|
+
resolveDefaultTablePreferenceScope,
|
|
13
|
+
sanitizeTableFiltersPreference,
|
|
14
|
+
} from '../../utils/tableVisibleColumnsPreferences';
|
|
15
|
+
import useDefaultTableTheme from './useDefaultTableTheme';
|
|
16
|
+
|
|
17
|
+
const DefaultFiltersModal = ({ storeName, visible = false, onClose }) => {
|
|
18
|
+
const store = useStore(storeName);
|
|
19
|
+
const columns = (Array.isArray(store?.getters?.columns) ? store.getters.columns : []).filter(
|
|
20
|
+
column =>
|
|
21
|
+
shouldIncludeColumn(column) &&
|
|
22
|
+
column?.filter !== false &&
|
|
23
|
+
column?.filters !== false,
|
|
24
|
+
);
|
|
25
|
+
const filters = store?.getters?.filters || {};
|
|
26
|
+
const configs = store?.getters?.configs || {};
|
|
27
|
+
const tablePreferenceScope =
|
|
28
|
+
configs.tablePreferenceScope ||
|
|
29
|
+
resolveDefaultTablePreferenceScope({ storeName });
|
|
30
|
+
const applyFilters = nextFilters => {
|
|
31
|
+
const persistedFilters = sanitizeTableFiltersPreference({
|
|
32
|
+
columns,
|
|
33
|
+
filters: nextFilters,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
persistTableFiltersPreference(tablePreferenceScope, persistedFilters);
|
|
37
|
+
|
|
38
|
+
if (typeof store?.actions?.setFilters === 'function') {
|
|
39
|
+
store.actions.setFilters(nextFilters);
|
|
40
|
+
} else if (store?.getters) {
|
|
41
|
+
store.getters.filters = nextFilters;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
configs.onFilterChange?.(nextFilters);
|
|
45
|
+
};
|
|
46
|
+
const updateFilter = (fieldName, value) => {
|
|
47
|
+
const nextFilters = { ...(filters || {}) };
|
|
48
|
+
const isEmpty =
|
|
49
|
+
value === null ||
|
|
50
|
+
value === undefined ||
|
|
51
|
+
normalizeText(value) === '' ||
|
|
52
|
+
(Array.isArray(value) && value.length === 0);
|
|
53
|
+
|
|
54
|
+
if (isEmpty) delete nextFilters[fieldName];
|
|
55
|
+
else nextFilters[fieldName] = value;
|
|
56
|
+
|
|
57
|
+
applyFilters(nextFilters);
|
|
58
|
+
};
|
|
59
|
+
const applyLabel = global.t?.t(storeName, 'button', 'apply');
|
|
60
|
+
const clearLabel = global.t?.t(storeName, 'button', 'clear');
|
|
61
|
+
const title = global.t?.t(storeName, 'label', 'filters');
|
|
62
|
+
const { modalColors, resolvedAccentColor } = useDefaultTableTheme();
|
|
63
|
+
if (!visible) return null;
|
|
64
|
+
|
|
65
|
+
const {
|
|
66
|
+
backgroundColor,
|
|
67
|
+
borderColor,
|
|
68
|
+
closeIconColor,
|
|
69
|
+
headerTextColor,
|
|
70
|
+
overlayColor,
|
|
71
|
+
textColor,
|
|
72
|
+
} = modalColors;
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<Modal visible transparent animationType="fade" onRequestClose={onClose}>
|
|
76
|
+
<View style={[styles.modalOverlay, { backgroundColor: overlayColor }]}>
|
|
77
|
+
<View style={[styles.modalCard, styles.filtersModalCard, { borderColor, backgroundColor }]}>
|
|
78
|
+
<View style={[styles.modalHeader, { borderBottomColor: borderColor }]}>
|
|
79
|
+
<Text style={[styles.modalTitle, { color: headerTextColor }]} numberOfLines={1}>
|
|
80
|
+
{title}
|
|
81
|
+
</Text>
|
|
82
|
+
<TouchableOpacity
|
|
83
|
+
style={[styles.modalCloseButton, { borderColor, backgroundColor }]}
|
|
84
|
+
activeOpacity={0.82}
|
|
85
|
+
onPress={onClose}
|
|
86
|
+
>
|
|
87
|
+
<Icon name="x" size={16} color={closeIconColor} />
|
|
88
|
+
</TouchableOpacity>
|
|
89
|
+
</View>
|
|
90
|
+
<ScrollView style={styles.filtersModalBody} contentContainerStyle={styles.filtersModalList}>
|
|
91
|
+
{columns.map(column => {
|
|
92
|
+
const fieldName = getColumnKey(column);
|
|
93
|
+
const label = formatStoreColumnLabel({
|
|
94
|
+
columns,
|
|
95
|
+
fieldName,
|
|
96
|
+
fallbackLabel: column?.label || fieldName,
|
|
97
|
+
storeName,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<View key={fieldName} style={styles.filtersModalField}>
|
|
102
|
+
<Text style={[styles.formLabel, { color: textColor }]} numberOfLines={1}>
|
|
103
|
+
{label}
|
|
104
|
+
</Text>
|
|
105
|
+
<DefaultColumnFilter
|
|
106
|
+
column={column}
|
|
107
|
+
filters={filters}
|
|
108
|
+
onChange={updateFilter}
|
|
109
|
+
storeName={storeName}
|
|
110
|
+
style={styles.filtersModalInput}
|
|
111
|
+
/>
|
|
112
|
+
</View>
|
|
113
|
+
);
|
|
114
|
+
})}
|
|
115
|
+
</ScrollView>
|
|
116
|
+
<View style={[styles.modalActions, { borderTopColor: borderColor }]}>
|
|
117
|
+
<TouchableOpacity
|
|
118
|
+
style={[styles.secondaryButton, { borderColor }]}
|
|
119
|
+
activeOpacity={0.82}
|
|
120
|
+
onPress={() => applyFilters({})}
|
|
121
|
+
>
|
|
122
|
+
<Text style={[styles.secondaryButtonText, { color: textColor }]}>
|
|
123
|
+
{clearLabel}
|
|
124
|
+
</Text>
|
|
125
|
+
</TouchableOpacity>
|
|
126
|
+
<TouchableOpacity
|
|
127
|
+
style={[styles.primaryButton, { backgroundColor: resolvedAccentColor }]}
|
|
128
|
+
activeOpacity={0.82}
|
|
129
|
+
onPress={onClose}
|
|
130
|
+
>
|
|
131
|
+
<Text style={styles.primaryButtonText}>
|
|
132
|
+
{applyLabel}
|
|
133
|
+
</Text>
|
|
134
|
+
</TouchableOpacity>
|
|
135
|
+
</View>
|
|
136
|
+
</View>
|
|
137
|
+
</View>
|
|
138
|
+
</Modal>
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export default DefaultFiltersModal;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
const DefaultModalButton = ({ children, renderButton }) => {
|
|
4
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
5
|
+
|
|
6
|
+
const close = useCallback(() => setIsOpen(false), []);
|
|
7
|
+
const open = useCallback(() => setIsOpen(true), []);
|
|
8
|
+
const toggle = useCallback(() => setIsOpen(prev => !prev), []);
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<>
|
|
12
|
+
{renderButton?.({ close, isOpen, open, toggle })}
|
|
13
|
+
{children?.({ close, isOpen, open, toggle })}
|
|
14
|
+
</>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default DefaultModalButton;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Modal, Text, TouchableOpacity, View } from 'react-native';
|
|
3
|
+
import Icon from 'react-native-vector-icons/Feather';
|
|
4
|
+
import DefaultSearch from '../filters/DefaultSearch';
|
|
5
|
+
import styles from './DefaultTable.styles';
|
|
6
|
+
import useDefaultTableTheme from './useDefaultTableTheme';
|
|
7
|
+
|
|
8
|
+
const DefaultSearchModal = ({ storeName, visible = false, onClose }) => {
|
|
9
|
+
const { modalColors } = useDefaultTableTheme();
|
|
10
|
+
if (!visible) return null;
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
backgroundColor,
|
|
14
|
+
borderColor,
|
|
15
|
+
closeIconColor,
|
|
16
|
+
headerTextColor,
|
|
17
|
+
overlayColor,
|
|
18
|
+
} = modalColors;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Modal visible transparent animationType="fade" onRequestClose={onClose}>
|
|
22
|
+
<View style={[styles.modalOverlay, { backgroundColor: overlayColor }]}>
|
|
23
|
+
<View style={[styles.modalCard, styles.searchModalCard, { borderColor, backgroundColor }]}>
|
|
24
|
+
<View style={[styles.modalHeader, { borderBottomColor: borderColor }]}>
|
|
25
|
+
<Text style={[styles.modalTitle, { color: headerTextColor }]} numberOfLines={1}>
|
|
26
|
+
{global.t?.t(storeName, 'label', 'search')}
|
|
27
|
+
</Text>
|
|
28
|
+
<TouchableOpacity
|
|
29
|
+
style={[styles.modalCloseButton, { borderColor, backgroundColor }]}
|
|
30
|
+
activeOpacity={0.82}
|
|
31
|
+
onPress={onClose}
|
|
32
|
+
>
|
|
33
|
+
<Icon name="x" size={16} color={closeIconColor} />
|
|
34
|
+
</TouchableOpacity>
|
|
35
|
+
</View>
|
|
36
|
+
<View style={styles.searchModalBody}>
|
|
37
|
+
<DefaultSearch
|
|
38
|
+
autoFocus
|
|
39
|
+
storeName={storeName}
|
|
40
|
+
onSearch={onClose}
|
|
41
|
+
/>
|
|
42
|
+
</View>
|
|
43
|
+
</View>
|
|
44
|
+
</View>
|
|
45
|
+
</Modal>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default DefaultSearchModal;
|