@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,436 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+
3
+ import {
4
+ ActivityIndicator,
5
+ Text,
6
+ TextInput,
7
+ TouchableOpacity,
8
+ View,
9
+ } from 'react-native';
10
+
11
+ import FeatherIcon from 'react-native-vector-icons/Feather';
12
+ import { useNavigation } from '@react-navigation/native';
13
+ import { useStore } from '@store';
14
+ import PeopleAvatar from '@controleonline/ui-people/src/react/components/PeopleAvatar';
15
+ import { useMessage } from '@controleonline/ui-common/src/react/components/MessageService';
16
+ import {
17
+ buildPeopleLinkReadParams,
18
+ buildSalesmanLinksFromPeopleLinks,
19
+ } from './employeeContacts';
20
+ import {
21
+ buildCommissionSavePayload,
22
+ canEditSalesmanCommission,
23
+ extractId,
24
+ formatCommissionSubtitle,
25
+ indexDefaultSalesmanLinksByPeopleId,
26
+ resolveEffectiveCommission,
27
+ shouldDisplayCommission,
28
+ } from './salesmanTabHelpers';
29
+ import { inlineStyle_46_16 } from './SalesmanTab.styles';
30
+ import { normalizeSalesmanLink } from './salesmanTab.helpers';
31
+ import SalesmanManageModal from './SalesmanManageModal';
32
+ import { useSalesmanManage } from './useSalesmanManage';
33
+ import Icon from 'react-native-vector-icons/MaterialIcons';
34
+ import { colors } from '@controleonline/../../src/styles/colors';
35
+ import SalesmanCommissionBlock from './SalesmanCommissionBlock';
36
+
37
+ import {
38
+ COMPANY_ICON_MEDIA_TYPES,
39
+ fetchPeopleMediaUrls,
40
+ normalizeCollection,
41
+ } from './salesmanTabMedia';
42
+
43
+ import { resolveAppType, resolveSessionUser } from './salesmanTabSession';
44
+
45
+ const SalesmanTab = ({
46
+ client,
47
+ customStyles,
48
+ linkType,
49
+ emptyText,
50
+ errorText,
51
+ }) => {
52
+ const navigation = useNavigation();
53
+ const { showDialog, showError, showSuccess } = useMessage();
54
+
55
+ const peopleStore = useStore('people');
56
+ const peopleLinkStore = useStore('people_link');
57
+ const authStore = useStore('auth');
58
+ const peopleActions = peopleStore?.actions || {};
59
+ const peopleGetters = peopleStore?.getters || {};
60
+ const getPeopleLinks = peopleLinkStore?.actions?.getItems;
61
+ const savePeopleLink = peopleLinkStore?.actions?.save;
62
+ const removePeopleLink = peopleLinkStore?.actions?.remove;
63
+
64
+ const [clients, setClients] = useState([]);
65
+ const [defaultLinksBySalesmanId, setDefaultLinksBySalesmanId] = useState({});
66
+ const [isLoading, setIsLoading] = useState(false);
67
+ const [error, setError] = useState('');
68
+ const [editingLinkId, setEditingLinkId] = useState('');
69
+ const [editForm, setEditForm] = useState({ comission: '', minimumComission: '' });
70
+ const [isSaving, setIsSaving] = useState(false);
71
+
72
+ const clientId = extractId(client?.id || client?.['@id']);
73
+ const currentCompanyId = extractId(
74
+ peopleGetters?.currentCompany?.id ||
75
+ peopleGetters?.currentCompany?.['@id'] ||
76
+ peopleGetters?.defaultCompany?.id,
77
+ );
78
+
79
+ const appType = useMemo(() => resolveAppType(), []);
80
+ const displayCommission = shouldDisplayCommission(appType);
81
+ const sessionUser = useMemo(() => resolveSessionUser(authStore), [authStore]);
82
+ const canEdit =
83
+ displayCommission &&
84
+ canEditSalesmanCommission(sessionUser, peopleGetters?.currentCompany);
85
+
86
+ const loadDefaultSalesmanLinks = useCallback(
87
+ async salesmanIds => {
88
+ if (!displayCommission || !getPeopleLinks || !currentCompanyId) {
89
+ setDefaultLinksBySalesmanId({});
90
+ return;
91
+ }
92
+
93
+ const uniqueIds = [...new Set((salesmanIds || []).map(extractId).filter(Boolean))];
94
+ if (uniqueIds.length === 0) {
95
+ setDefaultLinksBySalesmanId({});
96
+ return;
97
+ }
98
+
99
+ try {
100
+ const response = await getPeopleLinks({
101
+ company: currentCompanyId,
102
+ // people_links.linkType is an array filter in the API contract.
103
+ linkType: ['salesman'],
104
+ itemsPerPage: Math.max(uniqueIds.length, 50),
105
+ });
106
+ const indexed = indexDefaultSalesmanLinksByPeopleId(response);
107
+ const filtered = {};
108
+ uniqueIds.forEach(id => {
109
+ if (indexed[id]) {
110
+ filtered[id] = indexed[id];
111
+ }
112
+ });
113
+ setDefaultLinksBySalesmanId(filtered);
114
+ } catch (_) {
115
+ setDefaultLinksBySalesmanId({});
116
+ }
117
+ },
118
+ [currentCompanyId, displayCommission, getPeopleLinks],
119
+ );
120
+
121
+ useEffect(() => {
122
+ let cancelled = false;
123
+
124
+ if (!clientId || !getPeopleLinks) {
125
+ setClients([]);
126
+ setError('');
127
+ return () => {
128
+ cancelled = true;
129
+ };
130
+ }
131
+
132
+ setIsLoading(true);
133
+ setError('');
134
+
135
+ getPeopleLinks(
136
+ buildPeopleLinkReadParams({
137
+ peopleId: clientId,
138
+ linkType,
139
+ }),
140
+ )
141
+ .then(async items => {
142
+ if (cancelled) {
143
+ return;
144
+ }
145
+ const nextClients = buildSalesmanLinksFromPeopleLinks(items, {
146
+ clientId,
147
+ linkType,
148
+ });
149
+ const mediaByPeopleId = await fetchPeopleMediaUrls({
150
+ peopleActions,
151
+ mediaTypes: COMPANY_ICON_MEDIA_TYPES,
152
+ peopleIds: nextClients.map(
153
+ item => item?.company?.id || item?.company?.['@id'],
154
+ ),
155
+ });
156
+
157
+ if (cancelled) {
158
+ return;
159
+ }
160
+
161
+ setClients(
162
+ nextClients.map(item => {
163
+ const companyImageUrl =
164
+ mediaByPeopleId[
165
+ extractId(item?.company?.id || item?.company?.['@id'])
166
+ ] || '';
167
+
168
+ return {
169
+ ...item,
170
+ companyImageUrl,
171
+ company: companyImageUrl
172
+ ? { ...item?.company, icon: companyImageUrl }
173
+ : item?.company,
174
+ };
175
+ }),
176
+ );
177
+
178
+ await loadDefaultSalesmanLinks(
179
+ nextClients.map(item => item?.company?.id || item?.company?.['@id']),
180
+ );
181
+ })
182
+ .catch(() => {
183
+ if (!cancelled) {
184
+ setClients([]);
185
+ setError(errorText);
186
+ }
187
+ })
188
+ .finally(() => {
189
+ if (!cancelled) {
190
+ setIsLoading(false);
191
+ }
192
+ });
193
+
194
+ return () => {
195
+ cancelled = true;
196
+ };
197
+ }, [clientId, errorText, getPeopleLinks, linkType, loadDefaultSalesmanLinks]);
198
+
199
+ const {
200
+ canManage,
201
+ linkedNormalized,
202
+ showManageModal,
203
+ editingManageLink,
204
+ manageForm,
205
+ setManageForm,
206
+ isManageSaving,
207
+ salesmanOptions,
208
+ openManageModal,
209
+ closeManageModal,
210
+ handleManageSave,
211
+ handleManageDelete,
212
+ } = useSalesmanManage({
213
+ appType,
214
+ client,
215
+ clientId,
216
+ currentCompanyId,
217
+ linkType,
218
+ clients,
219
+ setClients,
220
+ setIsLoading,
221
+ peopleActions,
222
+ getPeopleLinks,
223
+ savePeopleLink,
224
+ removePeopleLink,
225
+ loadDefaultSalesmanLinks,
226
+ showDialog,
227
+ showError,
228
+ showSuccess,
229
+ });
230
+
231
+ const openEdit = item => {
232
+ const id = extractId(item?.id || item?.['@id']);
233
+ const minRaw = item?.minimum_comission ?? item?.minimumComission;
234
+ setEditingLinkId(id);
235
+ setEditForm({
236
+ comission: item?.comission != null && item?.comission !== '' ? String(item.comission) : '',
237
+ minimumComission: minRaw != null && minRaw !== '' ? String(minRaw) : '',
238
+ });
239
+ };
240
+
241
+ const cancelEdit = () => {
242
+ setEditingLinkId('');
243
+ setEditForm({ comission: '', minimumComission: '' });
244
+ };
245
+
246
+ const saveEdit = async item => {
247
+ if (!savePeopleLink || isSaving) {
248
+ return;
249
+ }
250
+ const payload = buildCommissionSavePayload(item, {
251
+ comission: editForm.comission,
252
+ minimumComission: editForm.minimumComission,
253
+ });
254
+ if (!payload) {
255
+ showError?.('Vínculo inválido para salvar comissão.');
256
+ return;
257
+ }
258
+
259
+ setIsSaving(true);
260
+ try {
261
+ const saved = await savePeopleLink(payload);
262
+ const savedId = extractId(saved?.id || saved?.['@id'] || payload.id);
263
+ setClients(prev =>
264
+ prev.map(row => {
265
+ const rowId = extractId(row?.id || row?.['@id']);
266
+ if (rowId !== savedId) {
267
+ return row;
268
+ }
269
+ return {
270
+ ...row,
271
+ ...(saved || {}),
272
+ comission: payload.comission,
273
+ minimum_comission: payload.minimum_comission,
274
+ };
275
+ }),
276
+ );
277
+ showSuccess?.('Comissão atualizada.');
278
+ cancelEdit();
279
+ } catch (err) {
280
+ showError?.(err?.message || 'Não foi possível salvar a comissão.');
281
+ } finally {
282
+ setIsSaving(false);
283
+ }
284
+ };
285
+
286
+ const renderCommissionBlock = item => (
287
+ <SalesmanCommissionBlock
288
+ item={item}
289
+ displayCommission={displayCommission}
290
+ canEdit={canEdit}
291
+ editingLinkId={editingLinkId}
292
+ editForm={editForm}
293
+ setEditForm={setEditForm}
294
+ isSaving={isSaving}
295
+ defaultLinksBySalesmanId={defaultLinksBySalesmanId}
296
+ openEdit={openEdit}
297
+ cancelEdit={cancelEdit}
298
+ saveEdit={saveEdit}
299
+ customStyles={customStyles}
300
+ />
301
+ );
302
+
303
+ return (
304
+ <>
305
+ <View style={customStyles.tabContent}>
306
+ <View style={customStyles.section}>
307
+ {canManage ? (
308
+ <View style={{ flexDirection: 'row', justifyContent: 'flex-end', marginBottom: 8 }}>
309
+ <TouchableOpacity
310
+ onPress={() => openManageModal(null)}
311
+ accessibilityLabel="Vincular vendedor"
312
+ testID="salesman-manage-add-btn">
313
+ <Icon name="add" size={24} color={colors.primary} />
314
+ </TouchableOpacity>
315
+ </View>
316
+ ) : null}
317
+ {isLoading ? (
318
+ <View style={inlineStyle_46_16}>
319
+ <ActivityIndicator
320
+ size="small"
321
+ color={customStyles.loadingIndicator.color}
322
+ />
323
+ </View>
324
+ ) : error ? (
325
+ <Text style={customStyles.emptyText}>{errorText}</Text>
326
+ ) : !clients || clients?.length === 0 ? (
327
+ <Text style={customStyles.emptyText}>{emptyText}</Text>
328
+ ) : (
329
+ clients.map(item => {
330
+ const linkId = extractId(item?.id || item?.['@id']);
331
+ return (
332
+ <TouchableOpacity
333
+ key={String(item?.id || item?.['@id'])}
334
+ style={[
335
+ customStyles.listItem,
336
+ customStyles.listItemWithEndAction,
337
+ ]}
338
+ activeOpacity={0.8}
339
+ onPress={() => {
340
+ if (editingLinkId === linkId) {
341
+ return;
342
+ }
343
+ const targetId = extractId(
344
+ item?.company?.id || item?.company?.['@id'],
345
+ );
346
+ if (!targetId) {
347
+ return;
348
+ }
349
+
350
+ peopleActions?.setItem?.(item?.company);
351
+ navigation.push('ClientDetails', { clientId: targetId });
352
+ }}>
353
+ <View style={customStyles.itemContent}>
354
+ <PeopleAvatar
355
+ people={item?.company}
356
+ size={40}
357
+ backgroundColor={customStyles.listAvatarBrand.backgroundColor}
358
+ borderColor={customStyles.listAvatarBrand.borderColor}
359
+ borderWidth={2}
360
+ textColor={customStyles.listAvatarText.color}
361
+ style={customStyles.listAvatar}
362
+ />
363
+ <View style={{ flex: 1 }}>
364
+ <Text style={customStyles.itemText}>
365
+ {String(item?.company?.name || '-')}
366
+ </Text>
367
+ <Text style={customStyles.itemSubtext}>
368
+ {`ID: ${extractId(item?.company?.id || item?.company?.['@id']) || '-'}`}
369
+ {item?.company?.alias
370
+ ? ` - ${String(item?.company?.alias)}`
371
+ : ''}
372
+ </Text>
373
+ {renderCommissionBlock(item)}
374
+ </View>
375
+ </View>
376
+ {canManage ? (
377
+ <View style={{ flexDirection: 'row' }}>
378
+ <TouchableOpacity
379
+ onPress={() => {
380
+ const link = linkedNormalized.find(
381
+ entry =>
382
+ entry.id === linkId ||
383
+ entry.sellerId === extractId(item?.company?.id),
384
+ );
385
+ openManageModal(link || normalizeSalesmanLink(item));
386
+ }}
387
+ style={{ padding: 6 }}
388
+ testID={`salesman-manage-edit-${linkId}`}>
389
+ <Icon name="edit" size={18} color={colors.primary} />
390
+ </TouchableOpacity>
391
+ <TouchableOpacity
392
+ onPress={() => {
393
+ const link = linkedNormalized.find(
394
+ entry =>
395
+ entry.id === linkId ||
396
+ entry.sellerId === extractId(item?.company?.id),
397
+ );
398
+ handleManageDelete(link || { id: linkId });
399
+ }}
400
+ style={{ padding: 6 }}
401
+ testID={`salesman-manage-delete-${linkId}`}>
402
+ <Icon name="delete-outline" size={18} color="#DC2626" />
403
+ </TouchableOpacity>
404
+ </View>
405
+ ) : (
406
+ <View style={customStyles.iconButtonGhost}>
407
+ <FeatherIcon
408
+ name="chevron-right"
409
+ size={16}
410
+ color={customStyles.iconButtonGhostIcon.color}
411
+ />
412
+ </View>
413
+ )}
414
+ </TouchableOpacity>
415
+ );
416
+ })
417
+ )}
418
+ </View>
419
+ </View>
420
+ {canManage ? (
421
+ <SalesmanManageModal
422
+ visible={showManageModal}
423
+ onClose={closeManageModal}
424
+ editingLink={editingManageLink}
425
+ formData={manageForm}
426
+ setFormData={setManageForm}
427
+ salesmanOptions={salesmanOptions}
428
+ isSaving={isManageSaving}
429
+ onSave={handleManageSave}
430
+ />
431
+ ) : null}
432
+ </>
433
+ );
434
+ };
435
+
436
+ export default SalesmanTab;
@@ -0,0 +1,4 @@
1
+ export const inlineStyle_46_16 = {
2
+ paddingVertical: 20,
3
+ alignItems: 'center',
4
+ };
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import {Text, TouchableOpacity, ScrollView, TextInput, View, ActivityIndicator} from 'react-native';
3
+ import AnimatedModal from '@controleonline/ui-common/src/react/components/AnimatedModal';
4
+ import Icon from 'react-native-vector-icons/MaterialIcons';
5
+
6
+ import {
7
+ inlineStyle_174_6,
8
+ inlineStyle_175_12,
9
+ inlineStyle_188_14,
10
+ inlineStyle_197_16,
11
+ inlineStyle_200_49,
12
+ inlineStyle_208_10,
13
+ inlineStyle_213_20,
14
+ inlineStyle_214_22,
15
+ inlineStyle_216_18,
16
+ inlineStyle_248_20,
17
+ inlineStyle_249_22,
18
+ inlineStyle_252_20,
19
+ inlineStyle_339_16,
20
+ inlineStyle_341_14,
21
+ } from './UsersTab.styles';
22
+ import {apiKeyModalStyles, formatApiKeyPreview} from './usersTabHelpers';
23
+
24
+ const UserApiKeyModal = ({
25
+ visible,
26
+ onClose,
27
+ apiKeyItem,
28
+ isRefreshingApiKey,
29
+ onCopy,
30
+ onRefresh,
31
+ }) => (
32
+ <AnimatedModal visible={visible} onRequestClose={onClose} style={inlineStyle_174_6}>
33
+ <View style={inlineStyle_175_12}>
34
+ <View style={inlineStyle_188_14}>
35
+ <Text style={inlineStyle_197_16}>Chave de API</Text>
36
+ <TouchableOpacity onPress={onClose} style={inlineStyle_200_49}>
37
+ <Icon name="close" size={20} color="#64748B" />
38
+ </TouchableOpacity>
39
+ </View>
40
+
41
+ <ScrollView
42
+ style={inlineStyle_208_10}
43
+ keyboardShouldPersistTaps="handled"
44
+ keyboardDismissMode="on-drag">
45
+ <View style={inlineStyle_213_20}>
46
+ <Text style={inlineStyle_214_22}>Usuário</Text>
47
+ <TextInput
48
+ style={inlineStyle_216_18}
49
+ value={apiKeyItem?.username || apiKeyItem?.name || ''}
50
+ editable={false}
51
+ />
52
+ </View>
53
+
54
+ <View style={inlineStyle_248_20}>
55
+ <Text style={inlineStyle_249_22}>Chave atual</Text>
56
+ <TextInput
57
+ style={inlineStyle_252_20}
58
+ value={formatApiKeyPreview(apiKeyItem?.apiKey)}
59
+ editable={false}
60
+ multiline
61
+ />
62
+ </View>
63
+
64
+ <View style={apiKeyModalStyles.buttonRow}>
65
+ <TouchableOpacity style={apiKeyModalStyles.secondaryButton} onPress={onCopy}>
66
+ <Text style={apiKeyModalStyles.secondaryText}>Copiar</Text>
67
+ </TouchableOpacity>
68
+ <TouchableOpacity
69
+ style={apiKeyModalStyles.secondaryButton}
70
+ onPress={() => onRefresh?.(apiKeyItem)}
71
+ disabled={isRefreshingApiKey}>
72
+ {isRefreshingApiKey ? (
73
+ <ActivityIndicator size="small" color="#64748B" />
74
+ ) : (
75
+ <Text style={apiKeyModalStyles.secondaryText}>Renovar</Text>
76
+ )}
77
+ </TouchableOpacity>
78
+ </View>
79
+ </ScrollView>
80
+
81
+ <View style={inlineStyle_339_16}>
82
+ <TouchableOpacity onPress={onClose} style={inlineStyle_341_14}>
83
+ <Text style={{color: '#64748B', fontWeight: '600'}}>Fechar</Text>
84
+ </TouchableOpacity>
85
+ </View>
86
+ </View>
87
+ </AnimatedModal>
88
+ );
89
+
90
+ export default UserApiKeyModal;