@controleonline/ui-customers 1.0.81

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 (102) 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 +50 -0
  7. package/FUNDING.yml +1 -0
  8. package/README.md +23 -0
  9. package/docs/technical/Client-Details-Franquia-Filial.md +18 -0
  10. package/docs/technical/Client-Details-People-Name-Case.md +103 -0
  11. package/docs/technical/Cliente-Vendedor-Vinculo-e-Permissoes.md +152 -0
  12. package/docs/wiki.md +1 -0
  13. package/package.json +32 -0
  14. package/scripts/validate-wiki-pointer.cjs +13 -0
  15. package/src/react/components/tabs/AddressesTab.js +494 -0
  16. package/src/react/components/tabs/CategoriesTab.js +196 -0
  17. package/src/react/components/tabs/ClassificationChips.js +494 -0
  18. package/src/react/components/tabs/ContactTab.js +621 -0
  19. package/src/react/components/tabs/ContactTab.styles.js +119 -0
  20. package/src/react/components/tabs/ContractsTab.js +13 -0
  21. package/src/react/components/tabs/DocumentsTab.js +463 -0
  22. package/src/react/components/tabs/DocumentsTab.styles.js +133 -0
  23. package/src/react/components/tabs/EmployeeContactRow.js +83 -0
  24. package/src/react/components/tabs/EmployeesTab.js +454 -0
  25. package/src/react/components/tabs/EmployeesTab.styles.js +172 -0
  26. package/src/react/components/tabs/FiscalTab.js +6 -0
  27. package/src/react/components/tabs/FranchiseCommissionSection.js +307 -0
  28. package/src/react/components/tabs/FranchiseLinksManageModal.js +215 -0
  29. package/src/react/components/tabs/FranchiseLinksTab.js +282 -0
  30. package/src/react/components/tabs/GeneralTab.js +466 -0
  31. package/src/react/components/tabs/GeneralTab.styles.js +101 -0
  32. package/src/react/components/tabs/MediaTab.js +356 -0
  33. package/src/react/components/tabs/PeopleCategoriesPanel.js +601 -0
  34. package/src/react/components/tabs/ProductsTab.js +236 -0
  35. package/src/react/components/tabs/ProductsTab.styles.js +143 -0
  36. package/src/react/components/tabs/SalesmanCommissionBlock.js +120 -0
  37. package/src/react/components/tabs/SalesmanManageModal.js +208 -0
  38. package/src/react/components/tabs/SalesmanTab.js +436 -0
  39. package/src/react/components/tabs/SalesmanTab.styles.js +4 -0
  40. package/src/react/components/tabs/UserApiKeyModal.js +90 -0
  41. package/src/react/components/tabs/UserFormModal.js +301 -0
  42. package/src/react/components/tabs/UsersTab.js +426 -0
  43. package/src/react/components/tabs/UsersTab.styles.js +283 -0
  44. package/src/react/components/tabs/UsersTab.styles.test.js +42 -0
  45. package/src/react/components/tabs/contactMutationMessage.js +18 -0
  46. package/src/react/components/tabs/employeeContacts.js +214 -0
  47. package/src/react/components/tabs/employeeUnlink.js +68 -0
  48. package/src/react/components/tabs/employeesTabHelpers.js +227 -0
  49. package/src/react/components/tabs/franchiseCommissionHelpers.js +134 -0
  50. package/src/react/components/tabs/franchiseLinksTab.helpers.js +247 -0
  51. package/src/react/components/tabs/generalTabHelpers.js +120 -0
  52. package/src/react/components/tabs/salesmanTab.helpers.js +114 -0
  53. package/src/react/components/tabs/salesmanTabHelpers.js +173 -0
  54. package/src/react/components/tabs/salesmanTabMedia.js +55 -0
  55. package/src/react/components/tabs/salesmanTabSession.js +39 -0
  56. package/src/react/components/tabs/useEmployeeUnlink.js +58 -0
  57. package/src/react/components/tabs/useFranchiseLinksManage.js +215 -0
  58. package/src/react/components/tabs/useSalesmanManage.js +222 -0
  59. package/src/react/components/tabs/usersTabHelpers.js +221 -0
  60. package/src/react/domain/productSupplierFlow.js +37 -0
  61. package/src/react/index.js +4 -0
  62. package/src/react/pages/ClientDetailsSkeleton.js +84 -0
  63. package/src/react/pages/clientDetailsHelpers.js +17 -0
  64. package/src/react/pages/clients.js +27 -0
  65. package/src/react/pages/details.js +529 -0
  66. package/src/react/pages/details.page.styles.js +105 -0
  67. package/src/react/pages/details.styles.js +15 -0
  68. package/src/react/pages/employees.js +69 -0
  69. package/src/react/pages/franchisees.js +30 -0
  70. package/src/react/pages/prospects.js +21 -0
  71. package/src/react/pages/provider.js +21 -0
  72. package/src/react/router/routes.js +104 -0
  73. package/src/react/styles/details.js +571 -0
  74. package/src/store/people_categories/index.js +72 -0
  75. package/src/tests/browser/manager/client-details-create-user.spec.js +321 -0
  76. package/src/tests/browser/manager/client-details-employee-unlink.spec.js +215 -0
  77. package/src/tests/browser/manager/client-details-employees-refresh.spec.js +271 -0
  78. package/src/tests/browser/manager/client-details-enable.spec.js +241 -0
  79. package/src/tests/browser/manager/client-details-fiscal-tabs.spec.js +410 -0
  80. package/src/tests/browser/manager/client-details-franchise-commission.spec.js +318 -0
  81. package/src/tests/browser/manager/client-details-franchise-links.spec.js +365 -0
  82. package/src/tests/browser/manager/client-details-media.spec.js +405 -0
  83. package/src/tests/browser/manager/client-details-relationships.spec.js +313 -0
  84. package/src/tests/browser/manager/client-details-salesman-commission.spec.js +291 -0
  85. package/src/tests/browser/manager/client-details-salesman-manage.spec.js +448 -0
  86. package/src/tests/react/components/tabs/contactMutationMessage.test.js +25 -0
  87. package/src/tests/react/components/tabs/employeeContacts.test.js +266 -0
  88. package/src/tests/react/components/tabs/employeeUnlink.test.js +67 -0
  89. package/src/tests/react/components/tabs/employeesTabHelpers.media.test.js +78 -0
  90. package/src/tests/react/components/tabs/employeesTabHelpers.test.js +64 -0
  91. package/src/tests/react/components/tabs/fiscalTabContract.test.mjs +29 -0
  92. package/src/tests/react/components/tabs/franchiseCommissionHelpers.test.js +128 -0
  93. package/src/tests/react/components/tabs/franchiseLinksTab.helpers.test.mjs +166 -0
  94. package/src/tests/react/components/tabs/generalTabHelpers.test.js +130 -0
  95. package/src/tests/react/components/tabs/peopleMediaTabs.test.js +322 -0
  96. package/src/tests/react/components/tabs/salesmanTab.helpers.test.mjs +75 -0
  97. package/src/tests/react/components/tabs/salesmanTabHelpers.test.js +142 -0
  98. package/src/tests/react/components/tabs/salesmanTabHelpers.test.mjs +92 -0
  99. package/src/tests/react/components/tabs/usersTabHelpers.test.js +59 -0
  100. package/src/tests/react/domain/productSupplierFlow.test.mjs +55 -0
  101. package/src/tests/react/pages/clientDetailsHelpers.test.js +98 -0
  102. package/src/tests/react/pages/employees.test.js +44 -0
@@ -0,0 +1,133 @@
1
+ export const inlineStyle_265_6 = {
2
+ justifyContent: 'flex-end',
3
+ };
4
+
5
+ export const inlineStyle_266_12 = {
6
+ backgroundColor: '#fff',
7
+ borderTopLeftRadius: 24,
8
+ borderTopRightRadius: 24,
9
+ maxHeight: '80%',
10
+ width: '100%',
11
+ shadowColor: '#000',
12
+
13
+ shadowOffset: {
14
+ width: 0,
15
+ height: -4,
16
+ },
17
+
18
+ shadowOpacity: 0.1,
19
+ shadowRadius: 12,
20
+ elevation: 10,
21
+ };
22
+
23
+ export const inlineStyle_279_14 = {
24
+ flexDirection: 'row',
25
+ alignItems: 'center',
26
+ justifyContent: 'space-between',
27
+ paddingHorizontal: 24,
28
+ paddingVertical: 20,
29
+ borderBottomWidth: 1,
30
+ borderBottomColor: '#F1F5F9',
31
+ };
32
+
33
+ export const inlineStyle_288_16 = {
34
+ fontSize: 20,
35
+ fontWeight: '700',
36
+ color: '#0F172A',
37
+ };
38
+
39
+ export const inlineStyle_291_49 = {
40
+ width: 32,
41
+ height: 32,
42
+ borderRadius: 16,
43
+ backgroundColor: '#F1F5F9',
44
+ alignItems: 'center',
45
+ justifyContent: 'center',
46
+ };
47
+
48
+ export const inlineStyle_299_10 = {
49
+ padding: 24,
50
+ };
51
+
52
+ export const inlineStyle_302_16 = {
53
+ marginBottom: 20,
54
+ };
55
+
56
+ export const inlineStyle_303_18 = {
57
+ fontSize: 16,
58
+ fontWeight: '600',
59
+ color: '#212529',
60
+ marginBottom: 8,
61
+ };
62
+
63
+ export const inlineStyle_304_18 = {
64
+ flexDirection: 'row',
65
+ flexWrap: 'wrap',
66
+ gap: 8,
67
+ };
68
+
69
+ export const inlineStyle_308_18 = (
70
+ {
71
+ formData: formData,
72
+ type: type,
73
+ },
74
+ ) => ({
75
+ paddingHorizontal: 16,
76
+ paddingVertical: 10,
77
+ borderRadius: 12,
78
+ borderWidth: 1,
79
+ borderColor: formData.type === type['@id'] ? '#007bff' : '#dee2e6',
80
+ backgroundColor: formData.type === type['@id'] ? '#e7f3ff' : '#f8f9fa',
81
+ });
82
+
83
+ export const inlineStyle_314_24 = (
84
+ {
85
+ formData: formData,
86
+ type: type,
87
+ },
88
+ ) => ({
89
+ fontSize: 14,
90
+ color: formData.type === type['@id'] ? '#007bff' : '#64748B',
91
+ fontWeight: formData.type === type['@id'] ? '600' : '400',
92
+ });
93
+
94
+ export const inlineStyle_324_16 = {
95
+ marginBottom: 24,
96
+ };
97
+
98
+ export const inlineStyle_325_18 = {
99
+ fontSize: 16,
100
+ fontWeight: '600',
101
+ color: '#212529',
102
+ marginBottom: 8,
103
+ };
104
+
105
+ export const inlineStyle_327_14 = {
106
+ borderWidth: 1,
107
+ borderColor: '#e9ecef',
108
+ borderRadius: 12,
109
+ paddingHorizontal: 16,
110
+ paddingVertical: 12,
111
+ fontSize: 16,
112
+ backgroundColor: '#f8f9fa',
113
+ };
114
+
115
+ export const inlineStyle_341_16 = {
116
+ flexDirection: 'row',
117
+ gap: 12,
118
+ };
119
+
120
+ export const inlineStyle_343_14 = {
121
+ flex: 1,
122
+ paddingVertical: 14,
123
+ borderRadius: 12,
124
+ borderWidth: 1,
125
+ borderColor: '#64748B',
126
+ alignItems: 'center',
127
+ };
128
+
129
+ export const inlineStyle_350_20 = {
130
+ fontSize: 16,
131
+ fontWeight: '600',
132
+ color: '#64748B',
133
+ };
@@ -0,0 +1,83 @@
1
+ import React from 'react';
2
+ import {Text, TouchableOpacity, View} from 'react-native';
3
+ import FeatherIcon from 'react-native-vector-icons/Feather';
4
+ import PeopleAvatar from '@controleonline/ui-people/src/react/components/PeopleAvatar';
5
+ import {
6
+ buildEmployeeDetailNavParams,
7
+ extractId,
8
+ formatEmployeeContactMeta,
9
+ formatEmployeeContactTitle,
10
+ } from './employeesTabHelpers';
11
+
12
+ const EmployeeContactRow = ({
13
+ item,
14
+ parentPeopleId,
15
+ customStyles,
16
+ peopleActions,
17
+ navigation,
18
+ onRemove,
19
+ }) => {
20
+ const linkId = extractId(
21
+ item?.peopleLink?.id || item?.peopleLink?.['@id'] || item?.peopleLinkId,
22
+ );
23
+
24
+ return (
25
+ <TouchableOpacity
26
+ style={[customStyles.listItem, customStyles.listItemWithEndAction]}
27
+ activeOpacity={0.8}
28
+ onPress={() => {
29
+ const params = buildEmployeeDetailNavParams({
30
+ employee: item,
31
+ parentPeopleId,
32
+ });
33
+ if (!params) {
34
+ return;
35
+ }
36
+ peopleActions?.setItem?.(item);
37
+ navigation.push('ClientDetails', params);
38
+ }}>
39
+ <View style={customStyles.itemContent}>
40
+ <PeopleAvatar
41
+ people={item}
42
+ size={40}
43
+ backgroundColor={customStyles.listAvatarBrand.backgroundColor}
44
+ borderColor={customStyles.listAvatarBrand.borderColor}
45
+ borderWidth={2}
46
+ textColor={customStyles.listAvatarText.color}
47
+ style={customStyles.listAvatar}
48
+ />
49
+ <View>
50
+ <Text style={customStyles.itemText}>
51
+ {formatEmployeeContactTitle(item)}
52
+ </Text>
53
+ <Text style={customStyles.itemSubtext}>
54
+ {formatEmployeeContactMeta(item)}
55
+ </Text>
56
+ </View>
57
+ </View>
58
+ <View style={{flexDirection: 'row', alignItems: 'center', gap: 4}}>
59
+ <View style={customStyles.iconButtonGhost}>
60
+ <FeatherIcon
61
+ name="edit-2"
62
+ size={16}
63
+ color={customStyles.iconButtonGhostIcon.color}
64
+ />
65
+ </View>
66
+ <TouchableOpacity
67
+ style={customStyles.iconButtonGhost}
68
+ onPress={event => {
69
+ event?.stopPropagation?.();
70
+ onRemove?.(item);
71
+ }}
72
+ testID={`employee-unlink-${linkId}`}
73
+ accessibilityRole="button"
74
+ accessibilityLabel="Remover colaborador"
75
+ hitSlop={{top: 8, bottom: 8, left: 8, right: 8}}>
76
+ <FeatherIcon name="trash-2" size={16} color="#B91C1C" />
77
+ </TouchableOpacity>
78
+ </View>
79
+ </TouchableOpacity>
80
+ );
81
+ };
82
+
83
+ export default EmployeeContactRow;
@@ -0,0 +1,454 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+
3
+ import {
4
+ ActivityIndicator,
5
+ Keyboard,
6
+ Platform,
7
+ ScrollView,
8
+ Text,
9
+ TextInput,
10
+ TouchableOpacity,
11
+ View,
12
+ } from 'react-native';
13
+
14
+ import { Picker } from '@react-native-picker/picker';
15
+ import FeatherIcon from 'react-native-vector-icons/Feather';
16
+ import Icon from 'react-native-vector-icons/MaterialIcons';
17
+ import { useFocusEffect, useNavigation } from '@react-navigation/native';
18
+ import { useStore, useStores } from '@store';
19
+ import AnimatedModal from '@controleonline/ui-common/src/react/components/AnimatedModal';
20
+ import { useMessage } from '@controleonline/ui-common/src/react/components/MessageService';
21
+ import {
22
+ uppercaseText,
23
+ } from '@controleonline/ui-common/src/react/utils/entityDisplay';
24
+ import {
25
+ buildEmployeeContactsFromPeopleLinks,
26
+ buildPeopleLinkReadParams,
27
+ } from './employeeContacts';
28
+
29
+ import {
30
+ inlineStyle_243_18,
31
+ inlineStyle_279_8,
32
+ inlineStyle_281_10,
33
+ inlineStyle_294_12,
34
+ inlineStyle_303_18,
35
+ inlineStyle_308_14,
36
+ inlineStyle_321_12,
37
+ inlineStyle_324_18,
38
+ inlineStyle_326_16,
39
+ inlineStyle_338_16,
40
+ inlineStyle_351_18,
41
+ inlineStyle_353_16,
42
+ inlineStyle_365_16,
43
+ inlineStyle_378_18,
44
+ inlineStyle_380_16,
45
+ inlineStyle_389_16,
46
+ inlineStyle_408_18,
47
+ inlineStyle_422_18,
48
+ inlineStyle_424_16,
49
+ inlineStyle_433_16,
50
+ inlineStyle_446_18,
51
+ inlineStyle_460_12,
52
+ inlineStyle_472_14,
53
+ inlineStyle_480_20,
54
+ } from './EmployeesTab.styles';
55
+
56
+ import {
57
+ extractId,
58
+ normalizeIdentityValue,
59
+ extractPeopleMediaUrl,
60
+ formatDateInput,
61
+ parseBrDateToYmd,
62
+ LINK_TYPE_OPTIONS,
63
+ resolveEmployeeContactLinkType,
64
+ buildEmployeeCreatePayload,
65
+ } from './employeesTabHelpers';
66
+ import EmployeeContactRow from './EmployeeContactRow';
67
+ import {useEmployeeUnlink} from './useEmployeeUnlink';
68
+
69
+
70
+ export {
71
+ resolveEmployeeContactLinkType,
72
+ formatEmployeeContactTitle,
73
+ formatEmployeeContactMeta,
74
+ } from './employeesTabHelpers';
75
+
76
+ const EmployeesTab = ({
77
+ client,
78
+ customStyles,
79
+ txt_title = global.t?.t('people','title','contact'),
80
+ txt_title_emptyText = global.t?.t('people','title','emptyText'),
81
+ txt_title_addPeople = global.t?.t('people','title','addPeople'),
82
+ txt_message_loadError = global.t?.t('people','message','loadError'),
83
+ txt_message_requiredError = global.t?.t('people','message','requiredError'),
84
+ txt_message_createError = global.t?.t('people','message','createError'),
85
+ txt_message_createSuccess = global.t?.t('people','message','createSuccess'),
86
+ }) => {
87
+ const navigation = useNavigation();
88
+ const { showDialog, showError, showSuccess } = useMessage();
89
+
90
+ const [employees, setEmployees] = useState([]);
91
+ const [isLoading, setIsLoading] = useState(false);
92
+ const [isSaving, setIsSaving] = useState(false);
93
+ const [error, setError] = useState('');
94
+ const [showModal, setShowModal] = useState(false);
95
+ const [formData, setFormData] = useState({
96
+ name: '',
97
+ alias: '',
98
+ foundationDateBr: '',
99
+ linkType: 'employee',
100
+ });
101
+ const [linkTypeOptions, setLinkTypeOptions] = useState(
102
+ LINK_TYPE_OPTIONS.map(option => ({
103
+ value: option.value,
104
+ label: '',
105
+ })),
106
+ );
107
+ const pickerMode = Platform.OS === 'android' ? 'dropdown' : undefined;
108
+
109
+ const peopleStore = useStores(state => state.people) || {};
110
+ const peopleActions = peopleStore.actions || {};
111
+ const peopleLinkStore = useStore('people_link');
112
+ const getPeopleLinks = peopleLinkStore?.actions?.getItems;
113
+ const removePeople = peopleActions?.remove;
114
+ const removePeopleLink = peopleLinkStore?.actions?.remove;
115
+
116
+ const parentPeopleId = useMemo(
117
+ () => extractId(client?.id || client?.['@id']),
118
+ [client?.id, client?.['@id']],
119
+ );
120
+
121
+ const {handleRemoveEmployee} = useEmployeeUnlink({
122
+ removePeople,
123
+ removePeopleLink,
124
+ showDialog,
125
+ showError,
126
+ showSuccess,
127
+ setEmployees,
128
+ });
129
+
130
+ const fetchEmployees = useCallback(async () => {
131
+ if (!parentPeopleId) {
132
+ setEmployees([]);
133
+ setError('');
134
+ return;
135
+ }
136
+
137
+ setIsLoading(true);
138
+ setError('');
139
+
140
+ try {
141
+ // Company contacts live in people_links. Request contact roles + a high
142
+ // itemsPerPage so large companies are not truncated to the first API page
143
+ // (app-community#636 — employee missing while owners still listed).
144
+ const allowedLinkTypes = LINK_TYPE_OPTIONS.map(option => option.value);
145
+ const response = await getPeopleLinks(
146
+ buildPeopleLinkReadParams({
147
+ companyId: parentPeopleId,
148
+ linkTypes: allowedLinkTypes,
149
+ itemsPerPage: 100,
150
+ }),
151
+ );
152
+
153
+ const normalized = buildEmployeeContactsFromPeopleLinks(response, {
154
+ parentPeopleId,
155
+ allowedLinkTypes,
156
+ });
157
+ // Avatares só a partir de peopleMedia no payload de people_links (#380).
158
+ // Sem chamadas a /people_media nesta lista.
159
+ setEmployees(
160
+ normalized.map(item => {
161
+ const avatarImageUrl =
162
+ extractPeopleMediaUrl(item, 'avatar') ||
163
+ extractPeopleMediaUrl(item?.people, 'avatar') ||
164
+ '';
165
+
166
+ return {
167
+ ...item,
168
+ avatar: avatarImageUrl || item?.avatar,
169
+ avatarImageUrl,
170
+ };
171
+ }),
172
+ );
173
+ } catch {
174
+ setEmployees([]);
175
+ setError(txt_message_loadError);
176
+ } finally {
177
+ setIsLoading(false);
178
+ }
179
+ }, [getPeopleLinks, txt_message_loadError, parentPeopleId]);
180
+
181
+ useFocusEffect(
182
+ useCallback(() => {
183
+ fetchEmployees();
184
+ }, [fetchEmployees]),
185
+ );
186
+
187
+ useEffect(() => {
188
+ setLinkTypeOptions(
189
+ LINK_TYPE_OPTIONS.map(option => ({
190
+ value: option.value,
191
+ label: global.t?.t('people', 'label', option.translationKey),
192
+ })),
193
+ );
194
+ }, []);
195
+
196
+ const resetForm = () => {
197
+ setFormData({
198
+ name: '',
199
+ alias: '',
200
+ foundationDateBr: '',
201
+ linkType: 'employee',
202
+ });
203
+ };
204
+
205
+ const handleOpenModal = () => {
206
+ resetForm();
207
+ setShowModal(true);
208
+ };
209
+
210
+ const handleCloseModal = () => {
211
+ setShowModal(false);
212
+ resetForm();
213
+ };
214
+
215
+ const handleSaveEmployee = async () => {
216
+ const name = normalizeIdentityValue(formData.name);
217
+ const alias = normalizeIdentityValue(formData.alias);
218
+
219
+ if (!name || !alias) {
220
+ showError(txt_message_requiredError);
221
+ return;
222
+ }
223
+
224
+ let foundationDate;
225
+ if (formData.foundationDateBr) {
226
+ foundationDate = parseBrDateToYmd(formData.foundationDateBr);
227
+ if (!foundationDate) {
228
+ showError('Data invalida. Use o formato DD/MM/AAAA.');
229
+ return;
230
+ }
231
+ }
232
+
233
+ if (!peopleActions?.company || !parentPeopleId) {
234
+ showError(txt_message_createError);
235
+ return;
236
+ }
237
+
238
+ setIsSaving(true);
239
+ try {
240
+ const payload = buildEmployeeCreatePayload({
241
+ name,
242
+ alias,
243
+ foundationDate,
244
+ linkType: formData.linkType,
245
+ parentPeopleId,
246
+ });
247
+
248
+ await peopleActions.company(payload);
249
+ showSuccess(txt_message_createSuccess);
250
+ handleCloseModal();
251
+ fetchEmployees();
252
+ } catch (saveError) {
253
+ showError(saveError?.message || txt_message_createError);
254
+ } finally {
255
+ setIsSaving(false);
256
+ }
257
+ };
258
+
259
+ return (
260
+ <>
261
+ <View style={customStyles.tabContent}>
262
+ <View style={customStyles.section}>
263
+ <View style={customStyles.sectionHeader}>
264
+ <Text style={customStyles.sectionTitle}>{txt_title}</Text>
265
+ <TouchableOpacity
266
+ onPress={handleOpenModal}
267
+ style={customStyles.iconButtonPrimary}>
268
+ <FeatherIcon
269
+ name="plus"
270
+ size={16}
271
+ color={customStyles.iconButtonPrimaryIcon.color}
272
+ />
273
+ </TouchableOpacity>
274
+ </View>
275
+
276
+ {isLoading ? (
277
+ <View style={inlineStyle_243_18}>
278
+ <ActivityIndicator
279
+ size="small"
280
+ color={customStyles.loadingIndicator.color}
281
+ />
282
+ </View>
283
+ ) : error ? (
284
+ <Text style={customStyles.txt_title_emptyText}>{error}</Text>
285
+ ) : employees.length === 0 ? (
286
+ <Text style={customStyles.txt_title_emptyText}>{txt_title_emptyText}</Text>
287
+ ) : (
288
+ employees.map(item => (
289
+ <EmployeeContactRow
290
+ key={String(
291
+ item?.peopleLink?.id ||
292
+ item?.peopleLink?.['@id'] ||
293
+ item?.id ||
294
+ item?.['@id'],
295
+ )}
296
+ item={item}
297
+ parentPeopleId={parentPeopleId}
298
+ customStyles={customStyles}
299
+ peopleActions={peopleActions}
300
+ navigation={navigation}
301
+ onRemove={handleRemoveEmployee}
302
+ />
303
+ ))
304
+ )}
305
+ </View>
306
+ </View>
307
+ <AnimatedModal
308
+ visible={showModal}
309
+ onRequestClose={handleCloseModal}
310
+ style={inlineStyle_279_8}>
311
+ <View
312
+ style={inlineStyle_281_10}>
313
+ <View
314
+ style={inlineStyle_294_12}>
315
+ <Text style={inlineStyle_303_18}>
316
+ {txt_title_addPeople}
317
+ </Text>
318
+ <TouchableOpacity
319
+ onPress={handleCloseModal}
320
+ style={inlineStyle_308_14}>
321
+ <Icon name="close" size={20} color="#64748B" />
322
+ </TouchableOpacity>
323
+ </View>
324
+
325
+ <ScrollView
326
+ style={inlineStyle_321_12}
327
+ keyboardShouldPersistTaps="handled"
328
+ keyboardDismissMode="on-drag">
329
+ <View style={inlineStyle_324_18}>
330
+ <Text
331
+ style={inlineStyle_326_16}>
332
+ Nome *
333
+ </Text>
334
+ <TextInput
335
+ value={formData.name}
336
+ onChangeText={text => setFormData(prev => ({ ...prev, name: uppercaseText(text) }))}
337
+ placeholder="Digite o nome"
338
+ style={inlineStyle_338_16}
339
+ placeholderTextColor="#6c757d"
340
+ />
341
+ </View>
342
+
343
+ <View style={inlineStyle_351_18}>
344
+ <Text
345
+ style={inlineStyle_353_16}>
346
+ Apelido *
347
+ </Text>
348
+ <TextInput
349
+ value={formData.alias}
350
+ onChangeText={text => setFormData(prev => ({ ...prev, alias: uppercaseText(text) }))}
351
+ placeholder="Digite o apelido"
352
+ style={inlineStyle_365_16}
353
+ placeholderTextColor="#6c757d"
354
+ />
355
+ </View>
356
+
357
+ <View style={inlineStyle_378_18}>
358
+ <Text
359
+ style={inlineStyle_380_16}>
360
+ Data de Nascimento
361
+ </Text>
362
+ <View
363
+ style={inlineStyle_389_16}>
364
+ <Icon name="calendar-today" size={20} color="#6c757d" />
365
+ <TextInput
366
+ value={formData.foundationDateBr}
367
+ onChangeText={text =>
368
+ setFormData(prev => ({
369
+ ...prev,
370
+ foundationDateBr: formatDateInput(text),
371
+ }))
372
+ }
373
+ placeholder="DD/MM/AAAA"
374
+ style={inlineStyle_408_18}
375
+ placeholderTextColor="#6c757d"
376
+ keyboardType="numeric"
377
+ maxLength={10}
378
+ />
379
+ </View>
380
+ </View>
381
+
382
+ <View style={inlineStyle_422_18}>
383
+ <Text
384
+ style={inlineStyle_424_16}>
385
+ Tipo de Vinculo
386
+ </Text>
387
+ <View
388
+ style={inlineStyle_433_16}>
389
+ <Picker
390
+ selectedValue={formData.linkType}
391
+ onValueChange={value =>
392
+ setFormData(prev => ({ ...prev, linkType: value }))
393
+ }
394
+ mode={pickerMode}
395
+ style={inlineStyle_446_18}>
396
+ {linkTypeOptions.map(option => (
397
+ <Picker.Item
398
+ key={option.value}
399
+ label={option.label}
400
+ value={option.value}
401
+ />
402
+ ))}
403
+ </Picker>
404
+ </View>
405
+ </View>
406
+ </ScrollView>
407
+
408
+ <View
409
+ style={inlineStyle_460_12}>
410
+ <TouchableOpacity
411
+ onPress={() => {
412
+ Keyboard.dismiss();
413
+ handleCloseModal();
414
+ }}
415
+ style={inlineStyle_472_14}>
416
+ <Text style={inlineStyle_480_20}>
417
+ Cancelar
418
+ </Text>
419
+ </TouchableOpacity>
420
+
421
+ <TouchableOpacity
422
+ onPress={() => {
423
+ Keyboard.dismiss();
424
+ handleSaveEmployee();
425
+ }}
426
+ disabled={isSaving}
427
+ style={[
428
+ customStyles.saveButton,
429
+ isSaving && customStyles.saveButtonDisabled,
430
+ ]}>
431
+ {isSaving ? (
432
+ <ActivityIndicator
433
+ size="small"
434
+ color={customStyles.saveButtonText.color}
435
+ />
436
+ ) : (
437
+ <>
438
+ <FeatherIcon
439
+ name="save"
440
+ size={16}
441
+ color={customStyles.iconButtonPrimaryIcon.color}
442
+ />
443
+ <Text style={customStyles.saveButtonText}>Salvar</Text>
444
+ </>
445
+ )}
446
+ </TouchableOpacity>
447
+ </View>
448
+ </View>
449
+ </AnimatedModal>
450
+ </>
451
+ );
452
+ };
453
+
454
+ export default EmployeesTab;