@controleonline/ui-default 1.0.263

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 (56) hide show
  1. package/.github/agents/developer.agent.md +30 -0
  2. package/.github/agents/devops.agent.md +30 -0
  3. package/.github/agents/qa.agent.md +30 -0
  4. package/.github/agents/security.agent.md +30 -0
  5. package/.scrutinizer.yml +20 -0
  6. package/AGENTS.md +47 -0
  7. package/FUNDING.yml +1 -0
  8. package/package.json +21 -0
  9. package/src/react/components/errors/DefaultErrors.js +360 -0
  10. package/src/react/components/files/DefaultFile.js +46 -0
  11. package/src/react/components/filters/CompactFilterSelector.js +262 -0
  12. package/src/react/components/filters/CompactFilterSelector.styles.js +124 -0
  13. package/src/react/components/filters/DateShortcutFilter.js +264 -0
  14. package/src/react/components/filters/DateShortcutFilter.styles.js +82 -0
  15. package/src/react/components/filters/DefaultColumnFilter.js +97 -0
  16. package/src/react/components/filters/DefaultColumnFilter.styles.js +21 -0
  17. package/src/react/components/filters/DefaultExternalFilters.js +441 -0
  18. package/src/react/components/filters/DefaultExternalFilters.styles.js +177 -0
  19. package/src/react/components/filters/DefaultSearch.js +103 -0
  20. package/src/react/components/filters/DefaultSearch.styles.js +70 -0
  21. package/src/react/components/filters/dateFilterSelection.js +29 -0
  22. package/src/react/components/form/DefaultForm.js +198 -0
  23. package/src/react/components/form/DefaultForm.styles.js +70 -0
  24. package/src/react/components/help/DefaultTooltip.js +87 -0
  25. package/src/react/components/help/DefaultTooltip.styles.js +61 -0
  26. package/src/react/components/inputs/DefaultInput.js +160 -0
  27. package/src/react/components/inputs/DefaultInput.styles.js +93 -0
  28. package/src/react/components/inputs/DefaultSelect.js +192 -0
  29. package/src/react/components/inputs/DefaultSelect.styles.js +65 -0
  30. package/src/react/components/inputs/defaultInputUtils.js +230 -0
  31. package/src/react/components/map/DefaultGoogleMap.styles.js +9 -0
  32. package/src/react/components/map/DefaultGoogleMap.web.js +698 -0
  33. package/src/react/components/map/DefaultMap.native.js +71 -0
  34. package/src/react/components/map/DefaultMap.shared.js +762 -0
  35. package/src/react/components/map/DefaultMap.styles.js +16 -0
  36. package/src/react/components/map/DefaultMap.web.js +66 -0
  37. package/src/react/components/map/DefaultNativeMap.native.js +615 -0
  38. package/src/react/components/map/DefaultNativeMap.shared.js +532 -0
  39. package/src/react/components/map/DefaultNativeMap.styles.js +122 -0
  40. package/src/react/components/table/DefaultTable.js +1897 -0
  41. package/src/react/components/table/DefaultTable.styles.js +610 -0
  42. package/src/react/index.js +10 -0
  43. package/src/react/utils/tableVisibleColumnsPreferences.js +264 -0
  44. package/src/store/default/actions.js +444 -0
  45. package/src/store/default/getters.js +28 -0
  46. package/src/store/default/mutation_types.js +26 -0
  47. package/src/store/default/mutations.js +138 -0
  48. package/src/tests/react/components/DateShortcutFilter.test.js +96 -0
  49. package/src/tests/react/components/errors/DefaultErrors.test.js +162 -0
  50. package/src/tests/react/components/files/DefaultFile.test.js +80 -0
  51. package/src/tests/react/components/map/DefaultMap.shared.test.js +162 -0
  52. package/src/tests/react/filters/dateFilterSelection.test.js +46 -0
  53. package/src/tests/react/inputs/defaultInputUtils.test.js +45 -0
  54. package/src/tests/react/store/defaultActions.test.js +112 -0
  55. package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +223 -0
  56. package/src/utils/filters.js +56 -0
@@ -0,0 +1,177 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ const styles = StyleSheet.create({
4
+ wrap: {
5
+ flexDirection: 'row',
6
+ flexWrap: 'wrap',
7
+ alignItems: 'center',
8
+ gap: 8,
9
+ marginTop: 8,
10
+ },
11
+ field: {
12
+ minWidth: 136,
13
+ flexGrow: 1,
14
+ flexBasis: 160,
15
+ },
16
+ inputWrap: {
17
+ minHeight: 40,
18
+ borderWidth: 1,
19
+ borderColor: '#CBD5E1',
20
+ borderRadius: 12,
21
+ backgroundColor: '#FFFFFF',
22
+ paddingHorizontal: 10,
23
+ paddingVertical: 6,
24
+ },
25
+ inputLabel: {
26
+ color: '#94A3B8',
27
+ fontSize: 9,
28
+ fontWeight: '900',
29
+ letterSpacing: 0.35,
30
+ textTransform: 'uppercase',
31
+ },
32
+ input: {
33
+ minHeight: 20,
34
+ padding: 0,
35
+ color: '#0F172A',
36
+ fontSize: 12,
37
+ fontWeight: '800',
38
+ },
39
+ clearButton: {
40
+ height: 40,
41
+ minWidth: 40,
42
+ borderRadius: 12,
43
+ borderWidth: 1,
44
+ borderColor: '#CBD5E1',
45
+ backgroundColor: '#FFFFFF',
46
+ alignItems: 'center',
47
+ justifyContent: 'center',
48
+ paddingHorizontal: 10,
49
+ },
50
+ mobileWrap: {
51
+ alignItems: 'flex-start',
52
+ },
53
+ mobileButton: {
54
+ minHeight: 38,
55
+ borderRadius: 12,
56
+ borderWidth: 1,
57
+ borderColor: '#CBD5E1',
58
+ backgroundColor: '#FFFFFF',
59
+ paddingHorizontal: 12,
60
+ paddingVertical: 8,
61
+ flexDirection: 'row',
62
+ alignItems: 'center',
63
+ gap: 8,
64
+ },
65
+ mobileButtonText: {
66
+ color: '#0F172A',
67
+ fontSize: 12,
68
+ lineHeight: 14,
69
+ fontWeight: '900',
70
+ textTransform: 'uppercase',
71
+ },
72
+ mobileCountBadge: {
73
+ minWidth: 19,
74
+ height: 19,
75
+ borderRadius: 10,
76
+ paddingHorizontal: 6,
77
+ alignItems: 'center',
78
+ justifyContent: 'center',
79
+ },
80
+ mobileCountBadgeText: {
81
+ color: '#FFFFFF',
82
+ fontSize: 11,
83
+ lineHeight: 13,
84
+ fontWeight: '900',
85
+ },
86
+ modalOverlay: {
87
+ flex: 1,
88
+ backgroundColor: 'rgba(15, 23, 42, 0.42)',
89
+ justifyContent: 'flex-end',
90
+ },
91
+ modalCard: {
92
+ width: '100%',
93
+ maxHeight: '86%',
94
+ borderTopLeftRadius: 18,
95
+ borderTopRightRadius: 18,
96
+ backgroundColor: '#FFFFFF',
97
+ paddingHorizontal: 14,
98
+ paddingTop: 14,
99
+ paddingBottom: 12,
100
+ },
101
+ modalHeader: {
102
+ minHeight: 34,
103
+ flexDirection: 'row',
104
+ alignItems: 'center',
105
+ justifyContent: 'space-between',
106
+ gap: 12,
107
+ marginBottom: 10,
108
+ },
109
+ modalTitle: {
110
+ flex: 1,
111
+ color: '#0F172A',
112
+ fontSize: 17,
113
+ lineHeight: 20,
114
+ fontWeight: '900',
115
+ },
116
+ modalCloseButton: {
117
+ width: 34,
118
+ height: 34,
119
+ borderRadius: 17,
120
+ borderWidth: 1,
121
+ borderColor: '#CBD5E1',
122
+ backgroundColor: '#F8FAFC',
123
+ alignItems: 'center',
124
+ justifyContent: 'center',
125
+ },
126
+ modalScroll: {
127
+ flexGrow: 0,
128
+ },
129
+ modalContent: {
130
+ gap: 8,
131
+ paddingBottom: 10,
132
+ },
133
+ modalField: {
134
+ width: '100%',
135
+ minWidth: 0,
136
+ flexGrow: 0,
137
+ },
138
+ modalActions: {
139
+ borderTopWidth: 1,
140
+ borderTopColor: '#E2E8F0',
141
+ paddingTop: 10,
142
+ flexDirection: 'row',
143
+ justifyContent: 'flex-end',
144
+ gap: 8,
145
+ },
146
+ modalSecondaryButton: {
147
+ minHeight: 38,
148
+ borderRadius: 999,
149
+ borderWidth: 1,
150
+ borderColor: '#CBD5E1',
151
+ backgroundColor: '#F8FAFC',
152
+ paddingHorizontal: 14,
153
+ alignItems: 'center',
154
+ justifyContent: 'center',
155
+ },
156
+ modalSecondaryButtonText: {
157
+ color: '#334155',
158
+ fontSize: 12,
159
+ lineHeight: 14,
160
+ fontWeight: '900',
161
+ },
162
+ modalPrimaryButton: {
163
+ minHeight: 38,
164
+ borderRadius: 999,
165
+ paddingHorizontal: 16,
166
+ alignItems: 'center',
167
+ justifyContent: 'center',
168
+ },
169
+ modalPrimaryButtonText: {
170
+ color: '#FFFFFF',
171
+ fontSize: 12,
172
+ lineHeight: 14,
173
+ fontWeight: '900',
174
+ },
175
+ });
176
+
177
+ export default styles;
@@ -0,0 +1,103 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { TextInput, TouchableOpacity, View } from 'react-native';
3
+ import Icon from 'react-native-vector-icons/Feather';
4
+ import styles from './DefaultSearch.styles';
5
+
6
+ const normalizeText = value => String(value || '').trim();
7
+
8
+ const DefaultSearch = ({
9
+ accentColor = '#2563EB',
10
+ autoFocus = false,
11
+ compact = false,
12
+ filters = null,
13
+ onChangeFilters = null,
14
+ onClear = null,
15
+ onSearch = null,
16
+ placeholder = '',
17
+ searchKey = 'search',
18
+ storeName = '',
19
+ style = null,
20
+ value,
21
+ }) => {
22
+ const resolvedValue = useMemo(
23
+ () => normalizeText(value ?? filters?.[searchKey]),
24
+ [filters, searchKey, value],
25
+ );
26
+ const [draftValue, setDraftValue] = useState(resolvedValue);
27
+
28
+ useEffect(() => {
29
+ setDraftValue(resolvedValue);
30
+ }, [resolvedValue]);
31
+
32
+ const commitSearch = nextValue => {
33
+ const searchValue = normalizeText(nextValue ?? draftValue);
34
+
35
+ if (typeof onChangeFilters === 'function') {
36
+ const nextFilters = { ...(filters || {}) };
37
+ if (searchValue) nextFilters[searchKey] = searchValue;
38
+ else delete nextFilters[searchKey];
39
+ onChangeFilters(nextFilters);
40
+ }
41
+
42
+ onSearch?.(searchValue);
43
+ };
44
+
45
+ const clearSearch = () => {
46
+ setDraftValue('');
47
+
48
+ if (typeof onChangeFilters === 'function') {
49
+ const nextFilters = { ...(filters || {}) };
50
+ delete nextFilters[searchKey];
51
+ onChangeFilters(nextFilters);
52
+ }
53
+
54
+ onClear?.();
55
+ onSearch?.('');
56
+ };
57
+
58
+ const resolvedPlaceholder =
59
+ placeholder ||
60
+ global.t?.t(storeName, 'input', searchKey) ||
61
+ global.t?.t(storeName, 'placeholder', searchKey) ||
62
+ 'Buscar...';
63
+
64
+ return (
65
+ <View style={[styles.wrap, compact ? styles.wrapCompact : null, style]}>
66
+ <Icon name="search" size={compact ? 14 : 16} color="#64748B" />
67
+ <TextInput
68
+ autoCapitalize="none"
69
+ autoCorrect={false}
70
+ autoFocus={autoFocus}
71
+ placeholder={resolvedPlaceholder}
72
+ placeholderTextColor="#94A3B8"
73
+ returnKeyType="search"
74
+ style={[styles.input, compact ? styles.inputCompact : null]}
75
+ value={draftValue}
76
+ onChangeText={setDraftValue}
77
+ onSubmitEditing={() => commitSearch()}
78
+ />
79
+ {draftValue ? (
80
+ <TouchableOpacity
81
+ style={[styles.iconButton, compact ? styles.iconButtonCompact : null]}
82
+ activeOpacity={0.82}
83
+ onPress={clearSearch}
84
+ >
85
+ <Icon name="x" size={14} color="#64748B" />
86
+ </TouchableOpacity>
87
+ ) : null}
88
+ <TouchableOpacity
89
+ style={[
90
+ styles.searchButton,
91
+ compact ? styles.searchButtonCompact : null,
92
+ { borderColor: accentColor },
93
+ ]}
94
+ activeOpacity={0.82}
95
+ onPress={() => commitSearch()}
96
+ >
97
+ <Icon name="arrow-right" size={compact ? 13 : 15} color={accentColor} />
98
+ </TouchableOpacity>
99
+ </View>
100
+ );
101
+ };
102
+
103
+ export default DefaultSearch;
@@ -0,0 +1,70 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ const styles = StyleSheet.create({
4
+ wrap: {
5
+ minHeight: 40,
6
+ minWidth: 180,
7
+ borderWidth: 1,
8
+ borderColor: '#CBD5E1',
9
+ borderRadius: 12,
10
+ backgroundColor: '#FFFFFF',
11
+ paddingLeft: 10,
12
+ paddingRight: 5,
13
+ paddingVertical: 5,
14
+ flexDirection: 'row',
15
+ alignItems: 'center',
16
+ gap: 8,
17
+ },
18
+ wrapCompact: {
19
+ minHeight: 30,
20
+ minWidth: 150,
21
+ maxWidth: 240,
22
+ borderRadius: 15,
23
+ paddingLeft: 9,
24
+ paddingRight: 3,
25
+ paddingVertical: 3,
26
+ gap: 6,
27
+ },
28
+ input: {
29
+ flex: 1,
30
+ minWidth: 0,
31
+ padding: 0,
32
+ color: '#0F172A',
33
+ fontSize: 13,
34
+ lineHeight: 16,
35
+ fontWeight: '700',
36
+ },
37
+ inputCompact: {
38
+ fontSize: 12,
39
+ lineHeight: 14,
40
+ },
41
+ iconButton: {
42
+ width: 28,
43
+ height: 28,
44
+ borderRadius: 14,
45
+ alignItems: 'center',
46
+ justifyContent: 'center',
47
+ backgroundColor: '#F8FAFC',
48
+ },
49
+ iconButtonCompact: {
50
+ width: 22,
51
+ height: 22,
52
+ borderRadius: 11,
53
+ },
54
+ searchButton: {
55
+ width: 30,
56
+ height: 30,
57
+ borderRadius: 15,
58
+ borderWidth: 1,
59
+ alignItems: 'center',
60
+ justifyContent: 'center',
61
+ backgroundColor: '#FFFFFF',
62
+ },
63
+ searchButtonCompact: {
64
+ width: 24,
65
+ height: 24,
66
+ borderRadius: 12,
67
+ },
68
+ });
69
+
70
+ export default styles;
@@ -0,0 +1,29 @@
1
+ const EMPTY_CUSTOM_RANGE = {
2
+ from: '',
3
+ to: '',
4
+ };
5
+
6
+ export const resolveNextDateFilterValue = (currentValue, optionKey) => {
7
+ if (optionKey === 'all') {
8
+ return null;
9
+ }
10
+
11
+ if (optionKey === 'custom') {
12
+ return currentValue && typeof currentValue === 'object'
13
+ ? currentValue
14
+ : {
15
+ shortcut: 'custom',
16
+ customRange: EMPTY_CUSTOM_RANGE,
17
+ };
18
+ }
19
+
20
+ return {
21
+ ...(currentValue && typeof currentValue === 'object' ? currentValue : {}),
22
+ shortcut: optionKey,
23
+ customRange:
24
+ currentValue?.customRange && typeof currentValue.customRange === 'object'
25
+ ? currentValue.customRange
26
+ : EMPTY_CUSTOM_RANGE,
27
+ };
28
+ };
29
+
@@ -0,0 +1,198 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
3
+ import { formatStoreColumnLabel } from '@controleonline/ui-common/src/react/utils/storeColumns';
4
+ import DefaultInput from '../inputs/DefaultInput';
5
+ import {
6
+ formatSaveValue,
7
+ getColumnKey,
8
+ isEditableColumn,
9
+ normalizeId,
10
+ normalizeText,
11
+ resolveEditValue,
12
+ } from '../inputs/defaultInputUtils';
13
+ import styles from './DefaultForm.styles';
14
+
15
+ const hasValue = value => {
16
+ if (value === null || value === undefined) return false;
17
+ if (typeof value === 'boolean' || typeof value === 'number') return true;
18
+ if (typeof value === 'object') {
19
+ return normalizeText(value.value ?? value.id ?? value['@id'] ?? '') !== '';
20
+ }
21
+
22
+ return normalizeText(value) !== '';
23
+ };
24
+
25
+ const resolveDefaultValue = (column, row) => {
26
+ const fieldName = getColumnKey(column);
27
+ if (row && row[fieldName] !== undefined) return row[fieldName];
28
+ if (typeof column?.defaultValue === 'function') return column.defaultValue(row, column);
29
+ if (column?.defaultValue !== undefined) return column.defaultValue;
30
+ if (typeof column?.initialValue === 'function') return column.initialValue(row, column);
31
+ if (column?.initialValue !== undefined) return column.initialValue;
32
+ return undefined;
33
+ };
34
+
35
+ const shouldIncludeColumn = (column, row, mode) => {
36
+ const fieldName = getColumnKey(column);
37
+ if (!fieldName || column?.isIdentity === true) return false;
38
+ if (column?.form === false || column?.multiline === true) return false;
39
+ if (mode === 'create' && column?.add === false) return false;
40
+ if (mode !== 'create' && !isEditableColumn(column)) return false;
41
+ if (typeof column?.visibleForm === 'function') {
42
+ return column.visibleForm(row, column) !== false;
43
+ }
44
+
45
+ return column?.visibleForm !== false;
46
+ };
47
+
48
+ const buildDraft = (columns, row) =>
49
+ columns.reduce((draft, column) => {
50
+ const fieldName = getColumnKey(column);
51
+ const defaultValue = resolveDefaultValue(column, row);
52
+ draft[fieldName] = resolveEditValue(column, row, defaultValue);
53
+ return draft;
54
+ }, {});
55
+
56
+ const DefaultForm = ({
57
+ accentColor = '#2563EB',
58
+ actions = {},
59
+ columns = [],
60
+ getOptionsForColumn = null,
61
+ mode = 'edit',
62
+ onCancel = null,
63
+ onSaved = null,
64
+ row = {},
65
+ storeName = '',
66
+ }) => {
67
+ const formColumns = useMemo(
68
+ () => columns.filter(column => shouldIncludeColumn(column, row, mode)),
69
+ [columns, mode, row],
70
+ );
71
+ const [draft, setDraft] = useState(() => buildDraft(formColumns, row));
72
+ const [isSaving, setIsSaving] = useState(false);
73
+
74
+ useEffect(() => {
75
+ setDraft(buildDraft(formColumns, row));
76
+ }, [formColumns, row]);
77
+
78
+ const save = useCallback(() => {
79
+ if (typeof actions.save !== 'function') {
80
+ onCancel?.();
81
+ return Promise.resolve(null);
82
+ }
83
+
84
+ const isCreate = mode === 'create';
85
+ const payload = isCreate
86
+ ? {}
87
+ : { id: normalizeId(row?.['@id'] || row?.id) };
88
+
89
+ formColumns.forEach(column => {
90
+ const fieldName = getColumnKey(column);
91
+ const nextValue = draft[fieldName];
92
+
93
+ if (isCreate) {
94
+ if (hasValue(nextValue)) {
95
+ payload[fieldName] = formatSaveValue(column, nextValue, row);
96
+ }
97
+ return;
98
+ }
99
+
100
+ const currentValue = resolveEditValue(column, row);
101
+ if (normalizeText(currentValue) !== normalizeText(nextValue)) {
102
+ payload[fieldName] = formatSaveValue(column, nextValue, row);
103
+ }
104
+ });
105
+
106
+ const payloadKeys = Object.keys(payload);
107
+ if ((isCreate && payloadKeys.length === 0) || (!isCreate && payloadKeys.length <= 1)) {
108
+ onCancel?.();
109
+ return Promise.resolve(null);
110
+ }
111
+
112
+ setIsSaving(true);
113
+
114
+ return actions.save(payload)
115
+ .then(savedItem => {
116
+ onSaved?.(savedItem || { ...row, ...draft }, row);
117
+ return savedItem;
118
+ })
119
+ .catch(error => {
120
+ console.error(error);
121
+ return null;
122
+ })
123
+ .finally(() => {
124
+ setIsSaving(false);
125
+ });
126
+ }, [actions, draft, formColumns, mode, onCancel, onSaved, row]);
127
+
128
+ return (
129
+ <>
130
+ <ScrollView style={styles.body} keyboardShouldPersistTaps="handled">
131
+ {formColumns.length > 0 ? (
132
+ <View style={styles.formGrid}>
133
+ {formColumns.map(column => {
134
+ const fieldName = getColumnKey(column);
135
+ const label = formatStoreColumnLabel({
136
+ columns,
137
+ fieldName,
138
+ fallbackLabel: column?.label || fieldName,
139
+ storeName,
140
+ });
141
+
142
+ return (
143
+ <View key={fieldName} style={styles.formField}>
144
+ <DefaultInput
145
+ accentColor={accentColor}
146
+ autoFocus={false}
147
+ autoSave={false}
148
+ column={column}
149
+ columns={columns}
150
+ editing
151
+ getOptionsForColumn={getOptionsForColumn}
152
+ label={label}
153
+ onChangeValue={value => setDraft(prev => ({ ...prev, [fieldName]: value }))}
154
+ row={row}
155
+ showLabel
156
+ storeName={storeName}
157
+ value={draft[fieldName]}
158
+ variant="form"
159
+ />
160
+ </View>
161
+ );
162
+ })}
163
+ </View>
164
+ ) : (
165
+ <View style={styles.emptyBox}>
166
+ <Text style={styles.emptyText}>
167
+ {global.t?.t(storeName, 'label', 'no_fields') || 'Nenhum campo disponivel'}
168
+ </Text>
169
+ </View>
170
+ )}
171
+ </ScrollView>
172
+
173
+ <View style={styles.actions}>
174
+ <TouchableOpacity style={styles.secondaryButton} onPress={onCancel}>
175
+ <Text style={styles.secondaryButtonText}>
176
+ {global.t?.t(storeName, 'button', 'cancel') || 'Cancelar'}
177
+ </Text>
178
+ </TouchableOpacity>
179
+ <TouchableOpacity
180
+ style={[
181
+ styles.primaryButton,
182
+ { backgroundColor: accentColor },
183
+ isSaving ? styles.primaryButtonDisabled : null,
184
+ ]}
185
+ activeOpacity={0.86}
186
+ disabled={isSaving}
187
+ onPress={save}
188
+ >
189
+ <Text style={styles.primaryButtonText}>
190
+ {isSaving ? 'Salvando' : global.t?.t(storeName, 'button', 'save') || 'Salvar'}
191
+ </Text>
192
+ </TouchableOpacity>
193
+ </View>
194
+ </>
195
+ );
196
+ };
197
+
198
+ export default DefaultForm;
@@ -0,0 +1,70 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ const styles = StyleSheet.create({
4
+ body: {
5
+ maxHeight: 520,
6
+ },
7
+ formGrid: {
8
+ padding: 14,
9
+ flexDirection: 'row',
10
+ flexWrap: 'wrap',
11
+ gap: 10,
12
+ },
13
+ formField: {
14
+ flexGrow: 1,
15
+ flexBasis: 220,
16
+ minWidth: 220,
17
+ },
18
+ emptyBox: {
19
+ padding: 18,
20
+ alignItems: 'center',
21
+ justifyContent: 'center',
22
+ },
23
+ emptyText: {
24
+ color: '#64748B',
25
+ fontSize: 12,
26
+ fontWeight: '700',
27
+ },
28
+ actions: {
29
+ minHeight: 52,
30
+ paddingHorizontal: 14,
31
+ borderTopWidth: 1,
32
+ borderTopColor: '#E2E8F0',
33
+ flexDirection: 'row',
34
+ alignItems: 'center',
35
+ justifyContent: 'flex-end',
36
+ gap: 8,
37
+ },
38
+ secondaryButton: {
39
+ height: 34,
40
+ borderRadius: 8,
41
+ borderWidth: 1,
42
+ borderColor: '#CBD5E1',
43
+ paddingHorizontal: 14,
44
+ alignItems: 'center',
45
+ justifyContent: 'center',
46
+ backgroundColor: '#FFFFFF',
47
+ },
48
+ secondaryButtonText: {
49
+ color: '#334155',
50
+ fontSize: 12,
51
+ fontWeight: '800',
52
+ },
53
+ primaryButton: {
54
+ height: 34,
55
+ borderRadius: 8,
56
+ paddingHorizontal: 16,
57
+ alignItems: 'center',
58
+ justifyContent: 'center',
59
+ },
60
+ primaryButtonDisabled: {
61
+ opacity: 0.62,
62
+ },
63
+ primaryButtonText: {
64
+ color: '#FFFFFF',
65
+ fontSize: 12,
66
+ fontWeight: '900',
67
+ },
68
+ });
69
+
70
+ export default styles;