@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.
- package/.github/agents/developer.agent.md +30 -0
- package/.github/agents/devops.agent.md +30 -0
- package/.github/agents/qa.agent.md +30 -0
- package/.github/agents/security.agent.md +30 -0
- package/.scrutinizer.yml +20 -0
- package/AGENTS.md +50 -0
- package/FUNDING.yml +1 -0
- package/README.md +23 -0
- package/docs/technical/Client-Details-Franquia-Filial.md +18 -0
- package/docs/technical/Client-Details-People-Name-Case.md +103 -0
- package/docs/technical/Cliente-Vendedor-Vinculo-e-Permissoes.md +152 -0
- package/docs/wiki.md +1 -0
- package/package.json +32 -0
- package/scripts/validate-wiki-pointer.cjs +13 -0
- package/src/react/components/tabs/AddressesTab.js +494 -0
- package/src/react/components/tabs/CategoriesTab.js +196 -0
- package/src/react/components/tabs/ClassificationChips.js +494 -0
- package/src/react/components/tabs/ContactTab.js +621 -0
- package/src/react/components/tabs/ContactTab.styles.js +119 -0
- package/src/react/components/tabs/ContractsTab.js +13 -0
- package/src/react/components/tabs/DocumentsTab.js +463 -0
- package/src/react/components/tabs/DocumentsTab.styles.js +133 -0
- package/src/react/components/tabs/EmployeeContactRow.js +83 -0
- package/src/react/components/tabs/EmployeesTab.js +454 -0
- package/src/react/components/tabs/EmployeesTab.styles.js +172 -0
- package/src/react/components/tabs/FiscalTab.js +6 -0
- package/src/react/components/tabs/FranchiseCommissionSection.js +307 -0
- package/src/react/components/tabs/FranchiseLinksManageModal.js +215 -0
- package/src/react/components/tabs/FranchiseLinksTab.js +282 -0
- package/src/react/components/tabs/GeneralTab.js +466 -0
- package/src/react/components/tabs/GeneralTab.styles.js +101 -0
- package/src/react/components/tabs/MediaTab.js +356 -0
- package/src/react/components/tabs/PeopleCategoriesPanel.js +601 -0
- package/src/react/components/tabs/ProductsTab.js +236 -0
- package/src/react/components/tabs/ProductsTab.styles.js +143 -0
- package/src/react/components/tabs/SalesmanCommissionBlock.js +120 -0
- package/src/react/components/tabs/SalesmanManageModal.js +208 -0
- package/src/react/components/tabs/SalesmanTab.js +436 -0
- package/src/react/components/tabs/SalesmanTab.styles.js +4 -0
- package/src/react/components/tabs/UserApiKeyModal.js +90 -0
- package/src/react/components/tabs/UserFormModal.js +301 -0
- package/src/react/components/tabs/UsersTab.js +426 -0
- package/src/react/components/tabs/UsersTab.styles.js +283 -0
- package/src/react/components/tabs/UsersTab.styles.test.js +42 -0
- package/src/react/components/tabs/contactMutationMessage.js +18 -0
- package/src/react/components/tabs/employeeContacts.js +214 -0
- package/src/react/components/tabs/employeeUnlink.js +68 -0
- package/src/react/components/tabs/employeesTabHelpers.js +227 -0
- package/src/react/components/tabs/franchiseCommissionHelpers.js +134 -0
- package/src/react/components/tabs/franchiseLinksTab.helpers.js +247 -0
- package/src/react/components/tabs/generalTabHelpers.js +120 -0
- package/src/react/components/tabs/salesmanTab.helpers.js +114 -0
- package/src/react/components/tabs/salesmanTabHelpers.js +173 -0
- package/src/react/components/tabs/salesmanTabMedia.js +55 -0
- package/src/react/components/tabs/salesmanTabSession.js +39 -0
- package/src/react/components/tabs/useEmployeeUnlink.js +58 -0
- package/src/react/components/tabs/useFranchiseLinksManage.js +215 -0
- package/src/react/components/tabs/useSalesmanManage.js +222 -0
- package/src/react/components/tabs/usersTabHelpers.js +221 -0
- package/src/react/domain/productSupplierFlow.js +37 -0
- package/src/react/index.js +4 -0
- package/src/react/pages/ClientDetailsSkeleton.js +84 -0
- package/src/react/pages/clientDetailsHelpers.js +17 -0
- package/src/react/pages/clients.js +27 -0
- package/src/react/pages/details.js +529 -0
- package/src/react/pages/details.page.styles.js +105 -0
- package/src/react/pages/details.styles.js +15 -0
- package/src/react/pages/employees.js +69 -0
- package/src/react/pages/franchisees.js +30 -0
- package/src/react/pages/prospects.js +21 -0
- package/src/react/pages/provider.js +21 -0
- package/src/react/router/routes.js +104 -0
- package/src/react/styles/details.js +571 -0
- package/src/store/people_categories/index.js +72 -0
- package/src/tests/browser/manager/client-details-create-user.spec.js +321 -0
- package/src/tests/browser/manager/client-details-employee-unlink.spec.js +215 -0
- package/src/tests/browser/manager/client-details-employees-refresh.spec.js +271 -0
- package/src/tests/browser/manager/client-details-enable.spec.js +241 -0
- package/src/tests/browser/manager/client-details-fiscal-tabs.spec.js +410 -0
- package/src/tests/browser/manager/client-details-franchise-commission.spec.js +318 -0
- package/src/tests/browser/manager/client-details-franchise-links.spec.js +365 -0
- package/src/tests/browser/manager/client-details-media.spec.js +405 -0
- package/src/tests/browser/manager/client-details-relationships.spec.js +313 -0
- package/src/tests/browser/manager/client-details-salesman-commission.spec.js +291 -0
- package/src/tests/browser/manager/client-details-salesman-manage.spec.js +448 -0
- package/src/tests/react/components/tabs/contactMutationMessage.test.js +25 -0
- package/src/tests/react/components/tabs/employeeContacts.test.js +266 -0
- package/src/tests/react/components/tabs/employeeUnlink.test.js +67 -0
- package/src/tests/react/components/tabs/employeesTabHelpers.media.test.js +78 -0
- package/src/tests/react/components/tabs/employeesTabHelpers.test.js +64 -0
- package/src/tests/react/components/tabs/fiscalTabContract.test.mjs +29 -0
- package/src/tests/react/components/tabs/franchiseCommissionHelpers.test.js +128 -0
- package/src/tests/react/components/tabs/franchiseLinksTab.helpers.test.mjs +166 -0
- package/src/tests/react/components/tabs/generalTabHelpers.test.js +130 -0
- package/src/tests/react/components/tabs/peopleMediaTabs.test.js +322 -0
- package/src/tests/react/components/tabs/salesmanTab.helpers.test.mjs +75 -0
- package/src/tests/react/components/tabs/salesmanTabHelpers.test.js +142 -0
- package/src/tests/react/components/tabs/salesmanTabHelpers.test.mjs +92 -0
- package/src/tests/react/components/tabs/usersTabHelpers.test.js +59 -0
- package/src/tests/react/domain/productSupplierFlow.test.mjs +55 -0
- package/src/tests/react/pages/clientDetailsHelpers.test.js +98 -0
- package/src/tests/react/pages/employees.test.js +44 -0
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Contract imported from AGENTS.md
|
|
3
|
+
* ## Escopo
|
|
4
|
+
* - `ui-customers` e o modulo React de cadastro e edicao de clientes.
|
|
5
|
+
* - Esta pagina concentra detalhes, enderecos e dados de exibicao de pessoas.
|
|
6
|
+
*
|
|
7
|
+
* ## Estado
|
|
8
|
+
*
|
|
9
|
+
* ## Limites
|
|
10
|
+
* - Nao duplicar validacoes de formato fora dos helpers compartilhados.
|
|
11
|
+
* - Manter aqui a orquestracao da tela e a edicao do cadastro de pessoas.
|
|
12
|
+
*/
|
|
13
|
+
import React, { useCallback, useState, useLayoutEffect, useEffect, useMemo } from 'react';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
Text,
|
|
17
|
+
View,
|
|
18
|
+
ScrollView,
|
|
19
|
+
TouchableOpacity,
|
|
20
|
+
} from 'react-native';
|
|
21
|
+
|
|
22
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
23
|
+
import { resolveFileImageUrl } from '@controleonline/ui-common/src/react/utils/fileUrl';
|
|
24
|
+
import PeopleAvatar from '@controleonline/ui-people/src/react/components/PeopleAvatar';
|
|
25
|
+
import { useStore, useStores } from '@store';
|
|
26
|
+
import { createDetailsStyles } from '../styles/details';
|
|
27
|
+
import GeneralTab from '../components/tabs/GeneralTab';
|
|
28
|
+
import UsersTab from '../components/tabs/UsersTab';
|
|
29
|
+
import SalesmanTab from '../components/tabs/SalesmanTab';
|
|
30
|
+
import EmployeesTab from '../components/tabs/EmployeesTab';
|
|
31
|
+
import ContractsTab from '../components/tabs/ContractsTab';
|
|
32
|
+
import ProductsTab from '../components/tabs/ProductsTab';
|
|
33
|
+
import MediaTab from '../components/tabs/MediaTab';
|
|
34
|
+
import FiscalTab from '../components/tabs/FiscalTab';
|
|
35
|
+
import FranchiseLinksTab from '../components/tabs/FranchiseLinksTab';
|
|
36
|
+
import {
|
|
37
|
+
buildEmployeeContactsFromPeopleLinks,
|
|
38
|
+
buildPeopleLinkReadParams,
|
|
39
|
+
} from '../components/tabs/employeeContacts';
|
|
40
|
+
import styles from './details.page.styles';
|
|
41
|
+
|
|
42
|
+
import {
|
|
43
|
+
inlineStyle_299_16,
|
|
44
|
+
inlineStyle_317_16,
|
|
45
|
+
inlineStyle_334_16,
|
|
46
|
+
inlineStyle_342_16,
|
|
47
|
+
} from './details.styles';
|
|
48
|
+
|
|
49
|
+
import ClientDetailsSkeleton from './ClientDetailsSkeleton';
|
|
50
|
+
import {
|
|
51
|
+
resolveContextKey,
|
|
52
|
+
normalizeCollection,
|
|
53
|
+
PERSON_PHOTO_MEDIA_TYPES,
|
|
54
|
+
COMPANY_ICON_MEDIA_TYPES,
|
|
55
|
+
extractId,
|
|
56
|
+
buildClientTabDefs,
|
|
57
|
+
resolveInitialTabIndex,
|
|
58
|
+
resolveRouteClientId,
|
|
59
|
+
resolveRouteClientSeed,
|
|
60
|
+
} from './clientDetailsHelpers';
|
|
61
|
+
|
|
62
|
+
const ClientDetails = ({ route, navigation }) => {
|
|
63
|
+
const routeParams = route.params || {};
|
|
64
|
+
const routeClientSeed = resolveRouteClientSeed(routeParams);
|
|
65
|
+
const clientId = resolveRouteClientId(routeParams);
|
|
66
|
+
const detailContext = resolveContextKey(routeParams?.contextKey);
|
|
67
|
+
const requestedInitialTab = String(route.params?.initialTab || '').trim();
|
|
68
|
+
const peopleStore = useStores(state => state.people) || {};
|
|
69
|
+
const peopleLinkStore = useStore('people_link');
|
|
70
|
+
const themeStore = useStore('theme');
|
|
71
|
+
const themeColors = themeStore.getters.colors;
|
|
72
|
+
const peopleActions = peopleStore?.actions || {};
|
|
73
|
+
const peopleGetters = peopleStore?.getters || {};
|
|
74
|
+
const getPeople = peopleActions?.get;
|
|
75
|
+
const getPeopleItems = peopleActions?.getItems;
|
|
76
|
+
const getPeopleLinks = peopleLinkStore?.actions?.getItems;
|
|
77
|
+
const savePeople = peopleActions?.save;
|
|
78
|
+
const detailsStyles = useMemo(() => createDetailsStyles(themeColors), [themeColors]);
|
|
79
|
+
|
|
80
|
+
const parentCompanyId = extractId(routeParams?.parentCompanyId);
|
|
81
|
+
const parentCompanyIri = parentCompanyId ? `/people/${parentCompanyId}` : '';
|
|
82
|
+
const initialContactLinkType = String(routeParams?.linkType || '')
|
|
83
|
+
.trim()
|
|
84
|
+
.toLowerCase();
|
|
85
|
+
const cachedClient = useMemo(() => {
|
|
86
|
+
if (!clientId) {
|
|
87
|
+
return routeClientSeed;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (
|
|
91
|
+
routeClientSeed &&
|
|
92
|
+
extractId(routeClientSeed?.id || routeClientSeed?.['@id']) === clientId
|
|
93
|
+
) {
|
|
94
|
+
return routeClientSeed;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const currentItem = peopleGetters?.item;
|
|
98
|
+
if (extractId(currentItem?.id || currentItem?.['@id']) === clientId) {
|
|
99
|
+
return currentItem;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const items = Array.isArray(peopleGetters?.items) ? peopleGetters.items : [];
|
|
103
|
+
return (
|
|
104
|
+
items.find(item => extractId(item?.id || item?.['@id']) === clientId) || null
|
|
105
|
+
);
|
|
106
|
+
}, [clientId, peopleGetters?.item, peopleGetters?.items, routeClientSeed]);
|
|
107
|
+
const [client, setClient] = useState(cachedClient);
|
|
108
|
+
const [isLoadingClient, setIsLoadingClient] = useState(
|
|
109
|
+
Boolean(clientId) && !cachedClient,
|
|
110
|
+
);
|
|
111
|
+
const [activeTab, setActiveTab] = useState(0);
|
|
112
|
+
const [clientAvatarImageUrl, setClientAvatarImageUrl] = useState('');
|
|
113
|
+
const [mediaRefreshKey, setMediaRefreshKey] = useState(0);
|
|
114
|
+
|
|
115
|
+
useLayoutEffect(() => {
|
|
116
|
+
navigation.setOptions({
|
|
117
|
+
headerTitle: '',
|
|
118
|
+
headerShadowVisible: false,
|
|
119
|
+
headerStyle: { backgroundColor: '#F8FAFC' },
|
|
120
|
+
headerRight: () => null,
|
|
121
|
+
});
|
|
122
|
+
}, [navigation]);
|
|
123
|
+
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
let mounted = true;
|
|
126
|
+
const initialTabIndex = resolveInitialTabIndex({ requestedInitialTab, nextClient: cachedClient, detailContext });
|
|
127
|
+
setActiveTab(initialTabIndex);
|
|
128
|
+
|
|
129
|
+
if (!clientId || !getPeople) {
|
|
130
|
+
setIsLoadingClient(false);
|
|
131
|
+
return () => {
|
|
132
|
+
mounted = false;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!cachedClient) {
|
|
137
|
+
setIsLoadingClient(true);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const loadClientById = async () => {
|
|
141
|
+
const storeMeta = {
|
|
142
|
+
dedupeKey: `client-details-${clientId}`,
|
|
143
|
+
preserveItem: true,
|
|
144
|
+
skipSystemError: true,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
return await getPeople({
|
|
149
|
+
id: clientId,
|
|
150
|
+
__storeMeta: storeMeta,
|
|
151
|
+
});
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (typeof getPeopleItems !== 'function') {
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const response = await getPeopleItems({
|
|
158
|
+
id: clientId,
|
|
159
|
+
itemsPerPage: 1,
|
|
160
|
+
__storeMeta: storeMeta,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
normalizeCollection(response).find(
|
|
165
|
+
item => extractId(item?.id || item?.['@id']) === clientId,
|
|
166
|
+
) ||
|
|
167
|
+
cachedClient ||
|
|
168
|
+
null
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const loadClient = async () => {
|
|
174
|
+
if (detailContext !== 'contacts' || !parentCompanyIri) {
|
|
175
|
+
return loadClientById();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const [fullClient, peopleLinkResponse] = await Promise.all([
|
|
179
|
+
loadClientById(),
|
|
180
|
+
getPeopleLinks
|
|
181
|
+
? getPeopleLinks(
|
|
182
|
+
buildPeopleLinkReadParams({
|
|
183
|
+
companyId: parentCompanyId,
|
|
184
|
+
peopleId: clientId,
|
|
185
|
+
}),
|
|
186
|
+
).catch(() => null)
|
|
187
|
+
: Promise.resolve(null),
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
const linkedContact = buildEmployeeContactsFromPeopleLinks(
|
|
191
|
+
peopleLinkResponse,
|
|
192
|
+
{
|
|
193
|
+
parentPeopleId: parentCompanyId,
|
|
194
|
+
},
|
|
195
|
+
)[0];
|
|
196
|
+
|
|
197
|
+
if (!linkedContact) {
|
|
198
|
+
return fullClient;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
...(fullClient || {}),
|
|
203
|
+
linkType: linkedContact.linkType || initialContactLinkType,
|
|
204
|
+
peopleLink: linkedContact.peopleLink,
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
loadClient()
|
|
209
|
+
.then(fullClient => {
|
|
210
|
+
if (!mounted || !fullClient) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
setClient(previousClient => {
|
|
215
|
+
const nextClient = { ...(previousClient || cachedClient || {}), ...fullClient };
|
|
216
|
+
const nextTabIndex = resolveInitialTabIndex({ requestedInitialTab, nextClient, detailContext });
|
|
217
|
+
setActiveTab(nextTabIndex);
|
|
218
|
+
return nextClient;
|
|
219
|
+
});
|
|
220
|
+
})
|
|
221
|
+
.catch(() => {
|
|
222
|
+
if (!mounted) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
setClient(cachedClient || null);
|
|
226
|
+
})
|
|
227
|
+
.finally(() => {
|
|
228
|
+
if (mounted) {
|
|
229
|
+
setIsLoadingClient(false);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
return () => {
|
|
234
|
+
mounted = false;
|
|
235
|
+
};
|
|
236
|
+
}, [
|
|
237
|
+
clientId,
|
|
238
|
+
detailContext,
|
|
239
|
+
getPeople,
|
|
240
|
+
getPeopleItems,
|
|
241
|
+
getPeopleLinks,
|
|
242
|
+
initialContactLinkType,
|
|
243
|
+
parentCompanyId,
|
|
244
|
+
parentCompanyIri,
|
|
245
|
+
requestedInitialTab,
|
|
246
|
+
]);
|
|
247
|
+
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
let mounted = true;
|
|
250
|
+
const currentClientId = extractId(client?.id || client?.['@id']);
|
|
251
|
+
const isCurrentClientCompany =
|
|
252
|
+
String(client?.peopleType || '').toUpperCase() === 'J';
|
|
253
|
+
const mediaTypes = isCurrentClientCompany
|
|
254
|
+
? COMPANY_ICON_MEDIA_TYPES
|
|
255
|
+
: PERSON_PHOTO_MEDIA_TYPES;
|
|
256
|
+
|
|
257
|
+
setClientAvatarImageUrl('');
|
|
258
|
+
|
|
259
|
+
if (!currentClientId) {
|
|
260
|
+
return () => {
|
|
261
|
+
mounted = false;
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
resolvePeopleMediaImageUrl({
|
|
266
|
+
peopleId: currentClientId,
|
|
267
|
+
mediaTypes,
|
|
268
|
+
})
|
|
269
|
+
.then(imageUrl => {
|
|
270
|
+
if (!mounted) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
setClientAvatarImageUrl(imageUrl);
|
|
275
|
+
})
|
|
276
|
+
.catch(() => {
|
|
277
|
+
if (mounted) {
|
|
278
|
+
setClientAvatarImageUrl('');
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
return () => {
|
|
283
|
+
mounted = false;
|
|
284
|
+
};
|
|
285
|
+
}, [client?.id, client?.['@id'], client?.peopleType, mediaRefreshKey]);
|
|
286
|
+
|
|
287
|
+
const updateClientData = (field, data) => {
|
|
288
|
+
setClient(prevClient => ({ ...prevClient, [field]: data }));
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const resolvePeopleMediaImageUrl = useCallback(
|
|
292
|
+
async ({peopleId, mediaTypes}) => {
|
|
293
|
+
for (const mediaType of mediaTypes) {
|
|
294
|
+
const response = await peopleActions.getPeopleMedia({
|
|
295
|
+
people: `/people/${peopleId}`,
|
|
296
|
+
'mediaType.type': mediaType,
|
|
297
|
+
itemsPerPage: 1,
|
|
298
|
+
}).catch(() => null);
|
|
299
|
+
const media = Array.isArray(response) ? response[0] : null;
|
|
300
|
+
const imageUrl = resolveFileImageUrl(media?.file);
|
|
301
|
+
|
|
302
|
+
if (imageUrl) {
|
|
303
|
+
return imageUrl;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return '';
|
|
308
|
+
},
|
|
309
|
+
[peopleActions],
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const handleClientMediaChanged = useCallback(() => {
|
|
313
|
+
setMediaRefreshKey(previousValue => previousValue + 1);
|
|
314
|
+
}, []);
|
|
315
|
+
|
|
316
|
+
const persistClientData = async partialData => {
|
|
317
|
+
const clientId = extractId(
|
|
318
|
+
client?.id || client?.['@id'],
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
if (!clientId || !savePeople) {
|
|
322
|
+
throw new Error('Nao foi possivel identificar o cliente para salvar.');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const payload = {
|
|
326
|
+
id: clientId,
|
|
327
|
+
...partialData,
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const saved = await savePeople(payload);
|
|
331
|
+
setClient(prev => ({ ...(prev || {}), ...(saved || {}), ...partialData }));
|
|
332
|
+
|
|
333
|
+
return saved;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const isPessoaJuridica = String(client?.peopleType || '').toUpperCase() === 'J';
|
|
337
|
+
const isProviderContext = ['provider', 'providers'].includes(detailContext);
|
|
338
|
+
|
|
339
|
+
const tabs = buildClientTabDefs({
|
|
340
|
+
isPessoaJuridica,
|
|
341
|
+
isProviderContext,
|
|
342
|
+
t: global.t,
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
useEffect(() => {
|
|
346
|
+
if (activeTab > tabs.length - 1) {
|
|
347
|
+
setActiveTab(0);
|
|
348
|
+
}
|
|
349
|
+
}, [activeTab, tabs.length]);
|
|
350
|
+
|
|
351
|
+
const handleTabPress = index => {
|
|
352
|
+
setActiveTab(index);
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
if (isLoadingClient || !client) {
|
|
356
|
+
return <ClientDetailsSkeleton tabs={tabs} />;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const tabProps = {
|
|
360
|
+
client,
|
|
361
|
+
customStyles: detailsStyles,
|
|
362
|
+
isEditing: true,
|
|
363
|
+
onUpdateClient: updateClientData,
|
|
364
|
+
onSaveClientData: persistClientData,
|
|
365
|
+
parentCompanyIri,
|
|
366
|
+
initialContactLinkType,
|
|
367
|
+
onChangeClientAvatar: null,
|
|
368
|
+
isSavingClientAvatar: false,
|
|
369
|
+
};
|
|
370
|
+
const activeTabKey = tabs[activeTab]?.key || 'general';
|
|
371
|
+
const activeTabContent = (() => {
|
|
372
|
+
if (activeTabKey === 'general') {
|
|
373
|
+
return <GeneralTab {...tabProps} />;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (activeTabKey === 'fiscal') {
|
|
377
|
+
return (
|
|
378
|
+
<ScrollView
|
|
379
|
+
style={styles.tabScroll}
|
|
380
|
+
contentContainerStyle={styles.tabScrollContent}
|
|
381
|
+
showsVerticalScrollIndicator={false}>
|
|
382
|
+
<FiscalTab {...tabProps} navigation={navigation} />
|
|
383
|
+
</ScrollView>
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (activeTabKey === 'media') {
|
|
388
|
+
return (
|
|
389
|
+
<ScrollView
|
|
390
|
+
style={styles.tabScroll}
|
|
391
|
+
contentContainerStyle={inlineStyle_342_16}
|
|
392
|
+
nestedScrollEnabled
|
|
393
|
+
showsVerticalScrollIndicator={false}>
|
|
394
|
+
<MediaTab client={client} onChanged={handleClientMediaChanged} />
|
|
395
|
+
</ScrollView>
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
if (activeTabKey === 'sellers' || activeTabKey === 'users') {
|
|
401
|
+
return (
|
|
402
|
+
<ScrollView
|
|
403
|
+
style={styles.tabScroll}
|
|
404
|
+
contentContainerStyle={inlineStyle_299_16}
|
|
405
|
+
nestedScrollEnabled
|
|
406
|
+
showsVerticalScrollIndicator={false}>
|
|
407
|
+
{isPessoaJuridica ? (
|
|
408
|
+
<SalesmanTab
|
|
409
|
+
{...tabProps}
|
|
410
|
+
title="Vendedores"
|
|
411
|
+
linkType="sellers-client"
|
|
412
|
+
emptyText="Nenhum vendedor vinculado"
|
|
413
|
+
errorText="Nao foi possivel carregar os vendedores vinculados."
|
|
414
|
+
/>
|
|
415
|
+
) : (
|
|
416
|
+
<UsersTab {...tabProps} />
|
|
417
|
+
)}
|
|
418
|
+
</ScrollView>
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (activeTabKey === 'franchise') {
|
|
423
|
+
return (
|
|
424
|
+
<ScrollView
|
|
425
|
+
style={styles.tabScroll}
|
|
426
|
+
contentContainerStyle={inlineStyle_299_16}
|
|
427
|
+
nestedScrollEnabled
|
|
428
|
+
showsVerticalScrollIndicator={false}>
|
|
429
|
+
<FranchiseLinksTab
|
|
430
|
+
{...tabProps}
|
|
431
|
+
emptyText="Nenhuma franquia ou filial vinculada"
|
|
432
|
+
errorText="Não foi possível carregar os vínculos de franquia/filial."
|
|
433
|
+
/>
|
|
434
|
+
</ScrollView>
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (activeTabKey === 'contacts') {
|
|
439
|
+
return (
|
|
440
|
+
<ScrollView
|
|
441
|
+
style={styles.tabScroll}
|
|
442
|
+
contentContainerStyle={inlineStyle_317_16}
|
|
443
|
+
nestedScrollEnabled
|
|
444
|
+
showsVerticalScrollIndicator={false}>
|
|
445
|
+
<EmployeesTab
|
|
446
|
+
{...tabProps}
|
|
447
|
+
title="Contatos"
|
|
448
|
+
emptyText="Nenhum contato vinculado"
|
|
449
|
+
errorText="Nao foi possivel carregar os contatos vinculados."
|
|
450
|
+
createTitle="Adicionar Contato"
|
|
451
|
+
requiredErrorText="Nome e apelido do contato sao obrigatorios."
|
|
452
|
+
createSuccessText="Contato cadastrado com sucesso."
|
|
453
|
+
createErrorText="Nao foi possivel cadastrar o contato."
|
|
454
|
+
/>
|
|
455
|
+
</ScrollView>
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (activeTabKey === 'products') {
|
|
460
|
+
return (
|
|
461
|
+
<ScrollView
|
|
462
|
+
style={styles.tabScroll}
|
|
463
|
+
contentContainerStyle={inlineStyle_334_16}
|
|
464
|
+
nestedScrollEnabled
|
|
465
|
+
showsVerticalScrollIndicator={false}>
|
|
466
|
+
<ProductsTab {...tabProps} />
|
|
467
|
+
</ScrollView>
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return (
|
|
472
|
+
<ScrollView
|
|
473
|
+
style={styles.tabScroll}
|
|
474
|
+
contentContainerStyle={inlineStyle_342_16}
|
|
475
|
+
nestedScrollEnabled
|
|
476
|
+
showsVerticalScrollIndicator={false}>
|
|
477
|
+
<ContractsTab {...tabProps} />
|
|
478
|
+
</ScrollView>
|
|
479
|
+
);
|
|
480
|
+
})();
|
|
481
|
+
|
|
482
|
+
return (
|
|
483
|
+
<SafeAreaView style={styles.container} edges={['bottom']}>
|
|
484
|
+
<View style={styles.headerProfile}>
|
|
485
|
+
<View style={styles.avatarContainer}>
|
|
486
|
+
<PeopleAvatar
|
|
487
|
+
people={client}
|
|
488
|
+
imageUrl={clientAvatarImageUrl}
|
|
489
|
+
name={String(client?.name || '').replace(/\s+/g, ' ').trim()}
|
|
490
|
+
size={64}
|
|
491
|
+
backgroundColor={themeColors.buttonBackground}
|
|
492
|
+
borderColor={themeColors.buttonText}
|
|
493
|
+
borderWidth={3}
|
|
494
|
+
textColor={themeColors.buttonText}
|
|
495
|
+
useGravatar={false}
|
|
496
|
+
usePeopleImage={false}
|
|
497
|
+
/>
|
|
498
|
+
</View>
|
|
499
|
+
<Text style={styles.profileName} numberOfLines={1} ellipsizeMode="tail">
|
|
500
|
+
{String(client?.name || '').replace(/\s+/g, ' ').trim()}
|
|
501
|
+
</Text>
|
|
502
|
+
|
|
503
|
+
<Text style={styles.profileId}>{`ID: ${client.id}`}</Text>
|
|
504
|
+
</View>
|
|
505
|
+
<View style={styles.tabsHeader}>
|
|
506
|
+
{tabs.map(tab => (
|
|
507
|
+
<TouchableOpacity
|
|
508
|
+
key={tab.key}
|
|
509
|
+
style={[styles.tabButton, activeTab === tabs.findIndex(item => item.key === tab.key) && styles.tabButtonActive]}
|
|
510
|
+
onPress={() => handleTabPress(tabs.findIndex(item => item.key === tab.key))}>
|
|
511
|
+
<Text
|
|
512
|
+
style={[
|
|
513
|
+
styles.tabButtonText,
|
|
514
|
+
activeTab === tabs.findIndex(item => item.key === tab.key) && styles.tabButtonTextActive,
|
|
515
|
+
]}>
|
|
516
|
+
{tab.label}
|
|
517
|
+
</Text>
|
|
518
|
+
{activeTab === tabs.findIndex(item => item.key === tab.key) && <View style={styles.activeIndicator} />}
|
|
519
|
+
</TouchableOpacity>
|
|
520
|
+
))}
|
|
521
|
+
</View>
|
|
522
|
+
<View style={styles.contentContainer}>
|
|
523
|
+
{activeTabContent}
|
|
524
|
+
</View>
|
|
525
|
+
</SafeAreaView>
|
|
526
|
+
);
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
export default ClientDetails;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { colors } from '@controleonline/../../src/styles/colors';
|
|
3
|
+
|
|
4
|
+
const styles = StyleSheet.create({
|
|
5
|
+
container: {
|
|
6
|
+
flex: 1,
|
|
7
|
+
backgroundColor: '#F8FAFC',
|
|
8
|
+
},
|
|
9
|
+
headerProfile: {
|
|
10
|
+
alignItems: 'center',
|
|
11
|
+
paddingVertical: 20,
|
|
12
|
+
backgroundColor: '#F8FAFC',
|
|
13
|
+
},
|
|
14
|
+
avatarContainer: {
|
|
15
|
+
position: 'relative',
|
|
16
|
+
marginBottom: 12,
|
|
17
|
+
},
|
|
18
|
+
profileName: {
|
|
19
|
+
fontSize: 20,
|
|
20
|
+
fontWeight: '700',
|
|
21
|
+
color: '#0F172A',
|
|
22
|
+
marginBottom: 4,
|
|
23
|
+
textAlign: 'center',
|
|
24
|
+
maxWidth: '86%',
|
|
25
|
+
},
|
|
26
|
+
profileId: {
|
|
27
|
+
fontSize: 14,
|
|
28
|
+
color: '#64748B',
|
|
29
|
+
fontWeight: '500',
|
|
30
|
+
},
|
|
31
|
+
tabsHeader: {
|
|
32
|
+
flexDirection: 'row',
|
|
33
|
+
backgroundColor: '#fff',
|
|
34
|
+
borderBottomWidth: 1,
|
|
35
|
+
borderBottomColor: '#F1F5F9',
|
|
36
|
+
},
|
|
37
|
+
tabButton: {
|
|
38
|
+
flex: 1,
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
paddingVertical: 16,
|
|
41
|
+
position: 'relative',
|
|
42
|
+
},
|
|
43
|
+
tabButtonText: {
|
|
44
|
+
fontSize: 14,
|
|
45
|
+
fontWeight: '600',
|
|
46
|
+
color: '#64748B',
|
|
47
|
+
},
|
|
48
|
+
tabButtonTextActive: {
|
|
49
|
+
color: colors.primary,
|
|
50
|
+
fontWeight: '700',
|
|
51
|
+
},
|
|
52
|
+
activeIndicator: {
|
|
53
|
+
position: 'absolute',
|
|
54
|
+
bottom: 0,
|
|
55
|
+
width: '60%',
|
|
56
|
+
height: 3,
|
|
57
|
+
backgroundColor: colors.primary,
|
|
58
|
+
borderTopLeftRadius: 3,
|
|
59
|
+
borderTopRightRadius: 3,
|
|
60
|
+
},
|
|
61
|
+
contentContainer: {
|
|
62
|
+
flex: 1,
|
|
63
|
+
},
|
|
64
|
+
scrollContent: {
|
|
65
|
+
flexGrow: 1,
|
|
66
|
+
},
|
|
67
|
+
tabPane: {
|
|
68
|
+
height: '100%',
|
|
69
|
+
},
|
|
70
|
+
tabScroll: {
|
|
71
|
+
flex: 1,
|
|
72
|
+
},
|
|
73
|
+
skeletonContent: {
|
|
74
|
+
paddingHorizontal: 16,
|
|
75
|
+
paddingTop: 16,
|
|
76
|
+
},
|
|
77
|
+
skeletonCircle: {
|
|
78
|
+
backgroundColor: '#E2E8F0',
|
|
79
|
+
},
|
|
80
|
+
skeletonLine: {
|
|
81
|
+
backgroundColor: '#E2E8F0',
|
|
82
|
+
borderRadius: 8,
|
|
83
|
+
},
|
|
84
|
+
skeletonTab: {
|
|
85
|
+
flex: 1,
|
|
86
|
+
height: 36,
|
|
87
|
+
marginHorizontal: 8,
|
|
88
|
+
marginVertical: 10,
|
|
89
|
+
borderRadius: 10,
|
|
90
|
+
backgroundColor: '#E2E8F0',
|
|
91
|
+
},
|
|
92
|
+
skeletonCard: {
|
|
93
|
+
backgroundColor: '#FFFFFF',
|
|
94
|
+
borderRadius: 14,
|
|
95
|
+
padding: 16,
|
|
96
|
+
marginBottom: 12,
|
|
97
|
+
shadowColor: '#000',
|
|
98
|
+
shadowOffset: { width: 0, height: 2 },
|
|
99
|
+
shadowOpacity: 0.06,
|
|
100
|
+
shadowRadius: 6,
|
|
101
|
+
elevation: 2,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export default styles;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const inlineStyle_299_16 = {
|
|
2
|
+
paddingBottom: 80,
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export const inlineStyle_317_16 = {
|
|
6
|
+
paddingBottom: 80,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const inlineStyle_334_16 = {
|
|
10
|
+
paddingBottom: 80,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const inlineStyle_342_16 = {
|
|
14
|
+
paddingBottom: 80,
|
|
15
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import People from '@controleonline/ui-people/src/react/pages/People';
|
|
3
|
+
import {
|
|
4
|
+
normalizePeopleContextType,
|
|
5
|
+
} from '@controleonline/ui-people/src/react/utils/peopleContext';
|
|
6
|
+
import { normalizeEntityId } from '@controleonline/ui-people/src/react/utils/peopleLinkFilters';
|
|
7
|
+
|
|
8
|
+
const normalizePeopleType = value =>
|
|
9
|
+
String(value ?? '')
|
|
10
|
+
.trim()
|
|
11
|
+
.toUpperCase();
|
|
12
|
+
|
|
13
|
+
export const buildEmployeesContext = routeParams => {
|
|
14
|
+
const normalizedSelectedContext = normalizePeopleContextType(routeParams?.selectedContext);
|
|
15
|
+
const normalizedDefaultContext = normalizePeopleContextType(routeParams?.defaultContext);
|
|
16
|
+
const normalizedContext = Array.isArray(routeParams?.context)
|
|
17
|
+
? routeParams.context
|
|
18
|
+
: routeParams?.context
|
|
19
|
+
? [routeParams.context]
|
|
20
|
+
: normalizedSelectedContext
|
|
21
|
+
? [normalizedSelectedContext]
|
|
22
|
+
: ['all', 'employee', 'owner', 'courier'];
|
|
23
|
+
|
|
24
|
+
const defaultContext =
|
|
25
|
+
normalizedDefaultContext ||
|
|
26
|
+
normalizedSelectedContext ||
|
|
27
|
+
normalizePeopleContextType(normalizedContext[0]) ||
|
|
28
|
+
'all';
|
|
29
|
+
|
|
30
|
+
const modalTitleByType = {
|
|
31
|
+
employee: 'Cadastro de Funcionario',
|
|
32
|
+
owner: 'Cadastro de Proprietario',
|
|
33
|
+
courier: 'Cadastro de Entregador',
|
|
34
|
+
...(routeParams?.modalTitleByType || {}),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
context: normalizedContext.map(type => normalizePeopleContextType(type)).filter(Boolean),
|
|
39
|
+
defaultContext,
|
|
40
|
+
selectedContext: normalizedSelectedContext || defaultContext,
|
|
41
|
+
defaultPeopleType:
|
|
42
|
+
normalizePeopleType(routeParams?.defaultPeopleType) ||
|
|
43
|
+
(defaultContext === 'courier' ? 'F' : 'J'),
|
|
44
|
+
title: routeParams?.title || global.t?.t('people', 'label', defaultContext),
|
|
45
|
+
searchPlaceholder:
|
|
46
|
+
routeParams?.searchPlaceholder || global.t?.t('people', 'label', defaultContext),
|
|
47
|
+
modalTitleByType,
|
|
48
|
+
detailsRouteName: 'EmployeeDetails',
|
|
49
|
+
useStoreExternalFilter: true,
|
|
50
|
+
detailsRouteParams: (person, selectedLinkType) => ({
|
|
51
|
+
employeeId: normalizeEntityId(person?.id ?? person?.['@id']),
|
|
52
|
+
contextKey: selectedLinkType === 'all' ? '' : String(selectedLinkType || ''),
|
|
53
|
+
}),
|
|
54
|
+
typeSelectorLabel:
|
|
55
|
+
routeParams?.typeSelectorLabel || global.t?.t('people', 'label', 'contactRole'),
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const Employees = ({route}) => {
|
|
60
|
+
const context = buildEmployeesContext(route?.params || {});
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<People
|
|
64
|
+
context={context}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default Employees;
|