@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.
Files changed (89) hide show
  1. package/.github/workflows/integration-source-gate.yml +68 -0
  2. package/.github/workflows/manager-worker.yml +19 -0
  3. package/AGENTS.md +46 -50
  4. package/docs/technical/Contrato-de-colunas.md +67 -0
  5. package/docs/technical/Default-Components.md +76 -0
  6. package/docs/technical/DefaultForm.md +80 -0
  7. package/docs/technical/DefaultInput-e-DefaultSelect.md +73 -0
  8. package/docs/technical/DefaultTable.md +203 -0
  9. package/docs/wiki.md +1 -0
  10. package/package.json +22 -21
  11. package/scripts/validate-wiki-pointer.cjs +13 -0
  12. package/src/react/components/address/DefaultAddress.js +435 -0
  13. package/src/react/components/address/DefaultAddress.styles.js +133 -0
  14. package/src/react/components/address/DefaultAddressMapPane.js +55 -0
  15. package/src/react/components/address/LatLonReadonlyFields.js +51 -0
  16. package/src/react/components/address/defaultAddressHelpers.js +163 -0
  17. package/src/react/components/address/defaultAddressStyles.js +136 -0
  18. package/src/react/components/address/defaultAddressUtils.js +129 -0
  19. package/src/react/components/extra-data/DefaultExtraData.js +152 -0
  20. package/src/react/components/extra-data/DefaultExtraData.styles.js +61 -0
  21. package/src/react/components/files/DefaultFile.js +231 -46
  22. package/src/react/components/files/DefaultFileColumn.js +123 -0
  23. package/src/react/components/files/DefaultFileColumn.styles.js +52 -0
  24. package/src/react/components/files/DefaultFileColumn.utils.js +104 -0
  25. package/src/react/components/filters/CompactFilterSelector.js +51 -35
  26. package/src/react/components/filters/DefaultColumnFilter.js +58 -41
  27. package/src/react/components/filters/DefaultExternalFilters.js +331 -265
  28. package/src/react/components/form/DefaultForm.js +99 -79
  29. package/src/react/components/help/DefaultTooltip.js +59 -33
  30. package/src/react/components/help/DefaultTooltip.styles.js +23 -3
  31. package/src/react/components/inputs/DefaultColorInput.js +153 -0
  32. package/src/react/components/inputs/DefaultFeatherIconPicker.js +146 -0
  33. package/src/react/components/inputs/DefaultFeatherIconPicker.styles.js +113 -0
  34. package/src/react/components/inputs/DefaultInput.js +69 -5
  35. package/src/react/components/inputs/DefaultInput.styles.js +26 -0
  36. package/src/react/components/inputs/DefaultSelect.js +134 -123
  37. package/src/react/components/inputs/defaultInputUtils.js +69 -43
  38. package/src/react/components/map/DefaultMap.native.js +73 -73
  39. package/src/react/components/map/DefaultMap.shared.js +911 -762
  40. package/src/react/components/map/DefaultMap.web.js +124 -68
  41. package/src/react/components/table/DefaultColumnMenu.js +109 -108
  42. package/src/react/components/table/DefaultDebug.js +44 -37
  43. package/src/react/components/table/DefaultSearchModal.js +9 -1
  44. package/src/react/components/table/DefaultTable.grouping.js +98 -0
  45. package/src/react/components/table/DefaultTable.js +213 -107
  46. package/src/react/components/table/DefaultTable.styles.js +367 -314
  47. package/src/react/components/table/DefaultTableBody.js +13 -8
  48. package/src/react/components/table/DefaultTableCards.js +20 -1
  49. package/src/react/components/table/DefaultTableControls.js +180 -205
  50. package/src/react/components/table/DefaultTableImportModal.js +108 -0
  51. package/src/react/components/table/DefaultTableInput.js +1 -0
  52. package/src/react/components/table/DefaultTableRowActions.js +1 -1
  53. package/src/react/components/table/DefaultTableRows.js +224 -363
  54. package/src/react/components/table/DefaultTableSearch.js +28 -5
  55. package/src/react/components/table/DefaultTableToolbar.js +92 -3
  56. package/src/react/components/table/useDefaultTablePagination.js +56 -7
  57. package/src/react/components/table/useDefaultTableStoreSync.js +164 -0
  58. package/src/react/components/upload/DefaultUpload.js +436 -0
  59. package/src/react/components/upload/DefaultUpload.styles.js +153 -0
  60. package/src/react/components/upload/DefaultUploadAttachmentsList.js +138 -0
  61. package/src/react/components/upload/DefaultUploadManagerModal.js +247 -0
  62. package/src/react/components/upload/defaultUploadHelpers.js +108 -0
  63. package/src/react/components/upload/defaultUploadLibrary.js +248 -0
  64. package/src/react/components/upload/fileUpload.js +93 -0
  65. package/src/react/hooks/usePostalCodeLookup.js +145 -0
  66. package/src/react/index.js +6 -0
  67. package/src/react/services/addressFormUtils.js +196 -0
  68. package/src/react/services/addressGeo.js +227 -0
  69. package/src/react/utils/tableVisibleColumnsPreferences.js +29 -12
  70. package/src/tests/react/components/address/defaultAddressHelpers.test.js +104 -0
  71. package/src/tests/react/components/files/DefaultFile.test.js +6 -6
  72. package/src/tests/react/components/form/DefaultForm.test.js +123 -0
  73. package/src/tests/react/components/inputs/DefaultSelect.test.js +108 -79
  74. package/src/tests/react/components/table/DefaultTable.grouping.test.js +49 -0
  75. package/src/tests/react/components/table/DefaultTable.test.js +53 -2
  76. package/src/tests/react/components/table/DefaultTableControls.filters.test.mjs +21 -0
  77. package/src/tests/react/components/table/resolveRowBackgroundColor.test.mjs +45 -0
  78. package/src/tests/react/components/table/useDefaultTableStoreSync.test.js +161 -0
  79. package/src/tests/react/components/upload/DefaultUpload.test.js +353 -0
  80. package/src/tests/react/components/upload/defaultUploadLibrary.test.js +82 -0
  81. package/src/tests/react/components/upload/peopleMediaLibraryPreview.test.mjs +57 -0
  82. package/src/tests/react/hooks/usePostalCodeLookup.test.js +40 -0
  83. package/src/tests/react/services/SMOKE-637-cep-hydrate.md +45 -0
  84. package/src/tests/react/services/addressFormUtils.test.js +176 -0
  85. package/src/tests/react/services/addressGeoLookup.async.test.js +104 -0
  86. package/src/tests/react/services/addressGeoLookup.test.js +105 -0
  87. package/src/tests/react/services/smoke-637/form-cep-12941040.png +0 -0
  88. package/src/tests/react/services/smoke-637/index.html +132 -0
  89. package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +84 -48
@@ -1,68 +1,124 @@
1
- import React, {useMemo} from 'react';
2
- import {View} from 'react-native';
3
-
4
- import DefaultGoogleMap from './DefaultGoogleMap.web';
5
-
6
- import styles from './DefaultMap.styles';
7
- import {
8
- buildOpenStreetMapHtml,
9
- resolveDefaultMapPayload,
10
- } from './DefaultMap.shared';
11
-
12
- export default function DefaultMap({
13
- config = null,
14
- addresses = null,
15
- markerPayloads = [],
16
- paths = [],
17
- userCoordinates = null,
18
- apiKey = '',
19
- popupTheme = null,
20
- }) {
21
- const resolvedPayload = useMemo(
22
- () =>
23
- resolveDefaultMapPayload({
24
- config,
25
- addresses,
26
- markerPayloads,
27
- paths,
28
- userCoordinates,
29
- apiKey,
30
- }),
31
- [apiKey, addresses, config, markerPayloads, paths, userCoordinates],
32
- );
33
-
34
- if (
35
- resolvedPayload.markerPayloads.length === 0 &&
36
- !resolvedPayload.userCoordinates &&
37
- resolvedPayload.paths.length === 0
38
- ) {
39
- return null;
40
- }
41
-
42
- if (resolvedPayload.apiKey) {
43
- return (
44
- <DefaultGoogleMap
45
- apiKey={resolvedPayload.apiKey}
46
- markerPayloads={resolvedPayload.markerPayloads}
47
- userCoordinates={resolvedPayload.userCoordinates}
48
- paths={resolvedPayload.paths}
49
- popupTheme={popupTheme}
50
- />
51
- );
52
- }
53
-
54
- return (
55
- <View style={styles.mapContainer}>
56
- <iframe
57
- title="Mapa"
58
- srcDoc={buildOpenStreetMapHtml({
59
- markerPayloads: resolvedPayload.markerPayloads,
60
- paths: resolvedPayload.paths,
61
- userCoordinates: resolvedPayload.userCoordinates,
62
- routeColor: '#0EA5E9',
63
- })}
64
- style={{width: '100%', height: '100%', border: 0}}
65
- />
66
- </View>
67
- );
68
- }
1
+ import React, {useEffect, useMemo, useRef} from 'react';
2
+ import {View} from 'react-native';
3
+
4
+ import DefaultGoogleMap from './DefaultGoogleMap.web';
5
+
6
+ import styles from './DefaultMap.styles';
7
+ import {
8
+ buildOpenStreetMapHtml,
9
+ resolveDefaultMapPayload,
10
+ } from './DefaultMap.shared';
11
+
12
+ export default function DefaultMap({
13
+ config = null,
14
+ addresses = null,
15
+ markerPayloads = [],
16
+ paths = [],
17
+ userCoordinates = null,
18
+ apiKey = '',
19
+ popupTheme = null,
20
+ }) {
21
+ const iframeRef = useRef(null);
22
+ const resolvedPayload = useMemo(
23
+ () =>
24
+ resolveDefaultMapPayload({
25
+ config,
26
+ addresses,
27
+ markerPayloads,
28
+ paths,
29
+ userCoordinates,
30
+ apiKey,
31
+ }),
32
+ [apiKey, addresses, config, markerPayloads, paths, userCoordinates],
33
+ );
34
+
35
+ const mapKey = useMemo(() => {
36
+ const markers = resolvedPayload.markerPayloads || [];
37
+ const first = markers[0] || {};
38
+ const user = resolvedPayload.userCoordinates || {};
39
+ return [
40
+ first.latitude,
41
+ first.longitude,
42
+ first.geocodeQuery || first.addressLine || '',
43
+ user.latitude,
44
+ user.longitude,
45
+ markers.length,
46
+ (resolvedPayload.paths || []).length,
47
+ ].join('|');
48
+ }, [resolvedPayload]);
49
+
50
+ useEffect(() => {
51
+ const el = iframeRef.current;
52
+ if (!el) {
53
+ return undefined;
54
+ }
55
+
56
+ const bump = () => {
57
+ try {
58
+ const win = el.contentWindow;
59
+ if (win && typeof win.dispatchEvent === 'function') {
60
+ win.dispatchEvent(new Event('resize'));
61
+ }
62
+ } catch (e) {
63
+ // empty srcDoc during mount
64
+ }
65
+ };
66
+
67
+ const onLoad = () => {
68
+ bump();
69
+ setTimeout(bump, 50);
70
+ setTimeout(bump, 250);
71
+ setTimeout(bump, 800);
72
+ };
73
+
74
+ el.addEventListener('load', onLoad);
75
+ return () => el.removeEventListener('load', onLoad);
76
+ }, [mapKey]);
77
+
78
+ if (
79
+ resolvedPayload.markerPayloads.length === 0 &&
80
+ !resolvedPayload.userCoordinates &&
81
+ resolvedPayload.paths.length === 0
82
+ ) {
83
+ return null;
84
+ }
85
+
86
+ if (resolvedPayload.apiKey) {
87
+ return (
88
+ <DefaultGoogleMap
89
+ apiKey={resolvedPayload.apiKey}
90
+ markerPayloads={resolvedPayload.markerPayloads}
91
+ userCoordinates={resolvedPayload.userCoordinates}
92
+ paths={resolvedPayload.paths}
93
+ popupTheme={popupTheme}
94
+ />
95
+ );
96
+ }
97
+
98
+ const html = buildOpenStreetMapHtml({
99
+ markerPayloads: resolvedPayload.markerPayloads,
100
+ paths: resolvedPayload.paths,
101
+ userCoordinates: resolvedPayload.userCoordinates,
102
+ routeColor: '#0EA5E9',
103
+ });
104
+
105
+ return (
106
+ <View style={[styles.mapContainer, {height: '100%', minHeight: 220, width: '100%', flex: 1}]}>
107
+ <iframe
108
+ ref={iframeRef}
109
+ key={mapKey}
110
+ title="Mapa"
111
+ srcDoc={html}
112
+ style={{
113
+ width: '100%',
114
+ height: '100%',
115
+ minHeight: 220,
116
+ border: 0,
117
+ display: 'block',
118
+ }}
119
+ loading="eager"
120
+ referrerPolicy="no-referrer-when-downgrade"
121
+ />
122
+ </View>
123
+ );
124
+ }
@@ -1,108 +1,109 @@
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;
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
+ themeColors,
30
+ } = useDefaultTableTheme();
31
+ if (!visible) return null;
32
+
33
+ const {
34
+ backgroundColor,
35
+ borderColor,
36
+ closeIconColor,
37
+ headerTextColor,
38
+ overlayColor,
39
+ textColor,
40
+ } = modalColors;
41
+
42
+ return (
43
+ <Modal visible transparent animationType="fade" onRequestClose={onClose}>
44
+ <View style={[styles.modalOverlay, { backgroundColor: overlayColor }]}>
45
+ <View style={[styles.modalCard, styles.columnMenuModalCard, { borderColor, backgroundColor }]}>
46
+ <View style={[styles.modalHeader, { borderBottomColor: borderColor }]}>
47
+ <Text style={[styles.modalTitle, { color: headerTextColor }]} numberOfLines={1}>
48
+ {global.t?.t(storeName, 'label', 'columns')}
49
+ </Text>
50
+ <TouchableOpacity
51
+ style={[styles.modalCloseButton, { borderColor, backgroundColor }]}
52
+ activeOpacity={0.82}
53
+ onPress={onClose}
54
+ >
55
+ <Icon name="x" size={16} color={themeColors.iconWarning || closeIconColor} />
56
+ </TouchableOpacity>
57
+ </View>
58
+ <ScrollView style={styles.columnMenuModalBody} contentContainerStyle={styles.columnMenuModalList}>
59
+ {availableColumns.map(column => {
60
+ const fieldName = getColumnKey(column);
61
+ const label = formatStoreColumnLabel({
62
+ columns,
63
+ fieldName,
64
+ fallbackLabel: column?.label || fieldName,
65
+ storeName,
66
+ });
67
+ const checked = visibleColumns[fieldName] !== false;
68
+ const toggleColumn = () => {
69
+ const nextVisibleColumns = sanitizeVisibleColumnsPreference({
70
+ columns,
71
+ visibleColumns: {
72
+ ...visibleColumns,
73
+ [fieldName]: visibleColumns[fieldName] === false,
74
+ },
75
+ });
76
+
77
+ persistVisibleColumnsPreference(tablePreferenceScope, nextVisibleColumns);
78
+
79
+ if (typeof store?.actions?.setVisibleColumns === 'function') {
80
+ store.actions.setVisibleColumns(nextVisibleColumns);
81
+ } else if (store?.getters) {
82
+ store.getters.visibleColumns = nextVisibleColumns;
83
+ }
84
+ };
85
+
86
+ return (
87
+ <TouchableOpacity
88
+ key={fieldName}
89
+ style={styles.columnMenuItem}
90
+ activeOpacity={0.82}
91
+ onPress={toggleColumn}
92
+ >
93
+ <Icon
94
+ name={checked ? 'check-square' : 'square'}
95
+ size={16}
96
+ color={checked ? resolvedCheckboxSelectedMarkColor : resolvedCheckboxBorderColor}
97
+ />
98
+ <Text style={[styles.columnMenuText, { color: textColor }]} numberOfLines={1}>{label}</Text>
99
+ </TouchableOpacity>
100
+ );
101
+ })}
102
+ </ScrollView>
103
+ </View>
104
+ </View>
105
+ </Modal>
106
+ );
107
+ };
108
+
109
+ export default DefaultColumnMenu;
@@ -82,11 +82,12 @@ const DefaultDebug = ({
82
82
  const debugParameters = isObject(resolvedDebug?.parameters)
83
83
  ? resolvedDebug.parameters
84
84
  : configs.debugFallbackParameters || null;
85
- const debugParametersText = useMemo(
86
- () => formatDebugValue(debugParameters),
87
- [debugParameters],
88
- );
89
- const debugCopyText = useMemo(
85
+ const debugParametersText = useMemo(
86
+ () => formatDebugValue(debugParameters),
87
+ [debugParameters],
88
+ );
89
+ const hasDebugParameters = debugParametersText !== '';
90
+ const debugCopyText = useMemo(
90
91
  () =>
91
92
  [
92
93
  'Query:',
@@ -118,9 +119,9 @@ const DefaultDebug = ({
118
119
  [showError, showSuccess],
119
120
  );
120
121
 
121
- if (!hasDebugQuery) {
122
- return null;
123
- }
122
+ if (!hasDebugQuery && !hasDebugParameters && !debugFilledQuery) {
123
+ return null;
124
+ }
124
125
 
125
126
  const {
126
127
  backgroundColor,
@@ -176,35 +177,41 @@ const DefaultDebug = ({
176
177
  </View>
177
178
 
178
179
  <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>
180
+ {hasDebugQuery ? (
181
+ <DebugSection
182
+ borderColor={borderColor}
183
+ copyText={debugQuery}
184
+ onCopy={requestCopyText}
185
+ textColor={textColor}
186
+ title="Query"
187
+ >
188
+ {debugQuery}
189
+ </DebugSection>
190
+ ) : null}
191
+
192
+ {hasDebugParameters ? (
193
+ <DebugSection
194
+ borderColor={borderColor}
195
+ copyText={debugParametersText}
196
+ onCopy={requestCopyText}
197
+ textColor={textColor}
198
+ title="Parametros"
199
+ >
200
+ {debugParametersText}
201
+ </DebugSection>
202
+ ) : null}
203
+
204
+ {debugFilledQuery ? (
205
+ <DebugSection
206
+ borderColor={borderColor}
207
+ copyText={debugFilledQuery}
208
+ onCopy={requestCopyText}
209
+ textColor={textColor}
210
+ title="Query preenchida"
211
+ >
212
+ {debugFilledQuery}
213
+ </DebugSection>
214
+ ) : null}
208
215
  </ScrollView>
209
216
 
210
217
  <View style={[styles.modalActions, { borderTopColor: borderColor }]}>
@@ -5,7 +5,13 @@ import DefaultSearch from '../filters/DefaultSearch';
5
5
  import styles from './DefaultTable.styles';
6
6
  import useDefaultTableTheme from './useDefaultTableTheme';
7
7
 
8
- const DefaultSearchModal = ({ storeName, visible = false, onClose }) => {
8
+ const DefaultSearchModal = ({
9
+ searchKey = 'search',
10
+ searchPlaceholder = '',
11
+ storeName,
12
+ visible = false,
13
+ onClose,
14
+ }) => {
9
15
  const { modalColors } = useDefaultTableTheme();
10
16
  if (!visible) return null;
11
17
 
@@ -36,6 +42,8 @@ const DefaultSearchModal = ({ storeName, visible = false, onClose }) => {
36
42
  <View style={styles.searchModalBody}>
37
43
  <DefaultSearch
38
44
  autoFocus
45
+ placeholder={searchPlaceholder}
46
+ searchKey={searchKey}
39
47
  storeName={storeName}
40
48
  onSearch={onClose}
41
49
  />
@@ -0,0 +1,98 @@
1
+ import { getColumnKey } from '../inputs/defaultInputUtils';
2
+ import { getRowKey } from './DefaultTable.utils';
3
+
4
+ export const isGroupingColumn = column => column?.grouping === true;
5
+
6
+ export const getGroupingColumns = columns =>
7
+ (Array.isArray(columns) ? columns : []).filter(isGroupingColumn);
8
+
9
+ export const getRowFieldValue = (row, column) => {
10
+ const fieldName = typeof column === 'string' ? column : getColumnKey(column);
11
+ if (!fieldName) return '';
12
+ const value = row?.[fieldName];
13
+ if (value && typeof value === 'object') {
14
+ return String(value.label ?? value.name ?? value.alias ?? value.id ?? '').trim();
15
+ }
16
+ return String(value ?? '').trim();
17
+ };
18
+
19
+ export const buildGroupKey = (row, groupingColumns) =>
20
+ getGroupingColumns(groupingColumns)
21
+ .map(column => getRowFieldValue(row, column) || 'none')
22
+ .join(':');
23
+
24
+ export const buildGroupLabel = (row, groupingColumns) =>
25
+ getGroupingColumns(groupingColumns)
26
+ .map(column => getRowFieldValue(row, column) || column?.label || getColumnKey(column))
27
+ .filter(Boolean)
28
+ .join(' · ');
29
+
30
+ export const groupTableRows = (rows, columns) => {
31
+ const groupingColumns = getGroupingColumns(columns);
32
+ const items = Array.isArray(rows) ? rows : [];
33
+ if (!groupingColumns.length) {
34
+ return [{ id: 'all', key: 'all', label: '', rows: items, count: items.length }];
35
+ }
36
+ const groups = new Map();
37
+ items.forEach(row => {
38
+ const key = buildGroupKey(row, groupingColumns);
39
+ if (!groups.has(key)) {
40
+ groups.set(key, { id: key, key, label: buildGroupLabel(row, groupingColumns), rows: [], count: 0 });
41
+ }
42
+ const group = groups.get(key);
43
+ group.rows.push(row);
44
+ group.count += 1;
45
+ });
46
+ return Array.from(groups.values());
47
+ };
48
+
49
+ export const flattenGroupedTableItems = (rows, columns) => {
50
+ const groupingColumns = getGroupingColumns(columns);
51
+ const items = Array.isArray(rows) ? rows : [];
52
+ if (!groupingColumns.length) {
53
+ return items.map((row, index) => ({ type: 'row', id: `row:${getRowKey(row, index)}`, row }));
54
+ }
55
+ return groupTableRows(items, groupingColumns).flatMap(group => [
56
+ { type: 'group', id: `group:${group.id}`, group },
57
+ ...group.rows.map((row, index) => ({ type: 'row', id: `row:${group.id}:${getRowKey(row, index)}`, group, row })),
58
+ ]);
59
+ };
60
+
61
+ export const normalizeSelectedIds = selectedIds =>
62
+ Array.from(new Set((Array.isArray(selectedIds) ? selectedIds : []).map(id => String(id))));
63
+
64
+ export const resolveStoreSelectedIds = store =>
65
+ normalizeSelectedIds(store?.getters?.selected);
66
+
67
+ export const isRowSelected = (row, selectedIds, index = 0) =>
68
+ normalizeSelectedIds(selectedIds).includes(String(getRowKey(row, index)));
69
+
70
+ export const toggleSelectedId = (selectedIds, row, index = 0) => {
71
+ const current = normalizeSelectedIds(selectedIds);
72
+ const id = String(getRowKey(row, index));
73
+ return current.includes(id) ? current.filter(item => item !== id) : [...current, id];
74
+ };
75
+
76
+ export const toggleGroupSelection = (selectedIds, groupRows) => {
77
+ const current = normalizeSelectedIds(selectedIds);
78
+ const ids = (Array.isArray(groupRows) ? groupRows : []).map(row => String(getRowKey(row)));
79
+ const allSelected = ids.length > 0 && ids.every(id => current.includes(id));
80
+ if (allSelected) return current.filter(id => !ids.includes(id));
81
+ return normalizeSelectedIds([...current, ...ids]);
82
+ };
83
+
84
+ export const persistStoreSelection = (store, nextIds, rows, columns) => {
85
+ const selected = normalizeSelectedIds(nextIds);
86
+ store?.actions?.setSelected?.(selected);
87
+ const items = (Array.isArray(rows) ? rows : []).filter(row => selected.includes(String(getRowKey(row))));
88
+ const groups = groupTableRows(items, columns).filter(group => group.rows.length > 0);
89
+ const totalValue = items.reduce((sum, row) => {
90
+ const amount = Number(row?.invoiceTotal ?? row?.total ?? row?.value ?? 0);
91
+ return sum + (Number.isFinite(amount) ? amount : 0);
92
+ }, 0);
93
+ store?.actions?.setSummary?.({
94
+ count: { invoices: items.length, groups: groups.length },
95
+ sum: { invoiceTotal: Number(totalValue.toFixed(2)) },
96
+ });
97
+ return selected;
98
+ };