@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,301 @@
1
+ import React, {useEffect, useMemo, useState} from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ ScrollView,
5
+ Text,
6
+ TextInput,
7
+ TouchableOpacity,
8
+ View,
9
+ } from 'react-native';
10
+ import AnimatedModal from '@controleonline/ui-common/src/react/components/AnimatedModal';
11
+ import Icon from 'react-native-vector-icons/MaterialIcons';
12
+ import {useStores} from '@store';
13
+ import {
14
+ extractCollectionItems,
15
+ extractId,
16
+ toTimezoneItem,
17
+ } from './usersTabHelpers';
18
+ import {
19
+ inlineStyle_174_6,
20
+ inlineStyle_175_12,
21
+ inlineStyle_188_14,
22
+ inlineStyle_197_16,
23
+ inlineStyle_200_49,
24
+ inlineStyle_208_10,
25
+ inlineStyle_213_20,
26
+ inlineStyle_214_22,
27
+ inlineStyle_216_18,
28
+ inlineStyle_224_20,
29
+ inlineStyle_225_22,
30
+ inlineStyle_226_22,
31
+ inlineStyle_228_20,
32
+ inlineStyle_239_20,
33
+ inlineStyle_248_20,
34
+ inlineStyle_249_22,
35
+ inlineStyle_250_22,
36
+ inlineStyle_252_20,
37
+ inlineStyle_263_20,
38
+ inlineStyle_275_20,
39
+ inlineStyle_276_22,
40
+ inlineStyle_288_20,
41
+ inlineStyle_289_22,
42
+ inlineStyle_290_22,
43
+ inlineStyle_292_20,
44
+ inlineStyle_303_20,
45
+ inlineStyle_312_20,
46
+ inlineStyle_313_22,
47
+ inlineStyle_314_22,
48
+ inlineStyle_316_20,
49
+ inlineStyle_327_20,
50
+ inlineStyle_339_16,
51
+ inlineStyle_341_14,
52
+ inlineStyle_348_20,
53
+ timezoneListStyle,
54
+ timezoneOptionStyle,
55
+ timezoneOptionTextStyle,
56
+ } from './UsersTab.styles';
57
+
58
+ const UserFormModal = ({
59
+ visible,
60
+ onClose,
61
+ editingItem,
62
+ formData,
63
+ setFormData,
64
+ showPassword,
65
+ setShowPassword,
66
+ showConfirmPassword,
67
+ setShowConfirmPassword,
68
+ onSave,
69
+ }) => {
70
+ const timezonesStore = useStores(state => state.timezones) || {};
71
+ const timezonesActions = timezonesStore.actions || {};
72
+ const timezoneFilters = timezonesStore.getters?.filters || {enabled: true};
73
+
74
+ const [timezones, setTimezones] = useState([]);
75
+ const [loadingTimezones, setLoadingTimezones] = useState(false);
76
+
77
+ useEffect(() => {
78
+ if (!visible || editingItem) {
79
+ return undefined;
80
+ }
81
+
82
+ let cancelled = false;
83
+ setLoadingTimezones(true);
84
+
85
+ const load = async () => {
86
+ try {
87
+ if (typeof timezonesActions.getItems !== 'function') {
88
+ if (!cancelled) setTimezones([]);
89
+ return;
90
+ }
91
+ const response = await timezonesActions.getItems(timezoneFilters);
92
+ if (cancelled) return;
93
+ const items = extractCollectionItems(response)
94
+ .map(toTimezoneItem)
95
+ .filter(Boolean);
96
+ setTimezones(items);
97
+ } catch {
98
+ if (!cancelled) setTimezones([]);
99
+ } finally {
100
+ if (!cancelled) setLoadingTimezones(false);
101
+ }
102
+ };
103
+
104
+ load();
105
+ return () => {
106
+ cancelled = true;
107
+ };
108
+ }, [visible, editingItem, timezonesActions, timezoneFilters]);
109
+
110
+ const selectedTimezoneId = extractId(formData?.timezoneId || formData?.timezone);
111
+
112
+ const timezoneOptions = useMemo(
113
+ () =>
114
+ timezones.map(tz => ({
115
+ id: tz.id,
116
+ label: tz.displayName || tz.name || tz.id,
117
+ })),
118
+ [timezones],
119
+ );
120
+
121
+ return (
122
+ <AnimatedModal visible={visible} onRequestClose={onClose} style={inlineStyle_174_6}>
123
+ <View style={inlineStyle_175_12}>
124
+ <View style={inlineStyle_188_14}>
125
+ <Text style={inlineStyle_197_16}>
126
+ {editingItem ? 'Editar Senha do Usuário' : 'Adicionar Usuário'}
127
+ </Text>
128
+ <TouchableOpacity onPress={onClose} style={inlineStyle_200_49}>
129
+ <Icon name="close" size={20} color="#64748B" />
130
+ </TouchableOpacity>
131
+ </View>
132
+
133
+ <ScrollView
134
+ style={inlineStyle_208_10}
135
+ keyboardShouldPersistTaps="handled"
136
+ keyboardDismissMode="on-drag">
137
+ {editingItem ? (
138
+ <View>
139
+ <View style={inlineStyle_213_20}>
140
+ <Text style={inlineStyle_214_22}>Usuário</Text>
141
+ <TextInput style={inlineStyle_216_18} value={formData.username} editable={false} />
142
+ </View>
143
+ <View style={inlineStyle_224_20}>
144
+ <Text style={inlineStyle_225_22}>Nova senha</Text>
145
+ <View style={inlineStyle_226_22}>
146
+ <TextInput
147
+ style={inlineStyle_228_20}
148
+ placeholder="Nova senha"
149
+ value={formData.password}
150
+ onChangeText={text => setFormData({...formData, password: text})}
151
+ secureTextEntry={!showPassword}
152
+ />
153
+ <TouchableOpacity
154
+ onPress={() => setShowPassword(prev => !prev)}
155
+ style={inlineStyle_239_20}>
156
+ <Icon
157
+ name={showPassword ? 'visibility-off' : 'visibility'}
158
+ size={20}
159
+ color="#6c757d"
160
+ />
161
+ </TouchableOpacity>
162
+ </View>
163
+ </View>
164
+ <View style={inlineStyle_248_20}>
165
+ <Text style={inlineStyle_249_22}>Confirmar nova senha</Text>
166
+ <View style={inlineStyle_250_22}>
167
+ <TextInput
168
+ style={inlineStyle_252_20}
169
+ placeholder="Confirmar nova senha"
170
+ value={formData.confirmPassword}
171
+ onChangeText={text => setFormData({...formData, confirmPassword: text})}
172
+ secureTextEntry={!showConfirmPassword}
173
+ />
174
+ <TouchableOpacity
175
+ onPress={() => setShowConfirmPassword(prev => !prev)}
176
+ style={inlineStyle_263_20}>
177
+ <Icon
178
+ name={showConfirmPassword ? 'visibility-off' : 'visibility'}
179
+ size={20}
180
+ color="#6c757d"
181
+ />
182
+ </TouchableOpacity>
183
+ </View>
184
+ </View>
185
+ </View>
186
+ ) : (
187
+ <View>
188
+ <View style={inlineStyle_275_20}>
189
+ <Text style={inlineStyle_276_22}>Nome de usuário *</Text>
190
+ <TextInput
191
+ style={inlineStyle_216_18}
192
+ placeholder="Nome de usuário"
193
+ value={formData.username}
194
+ onChangeText={text => setFormData({...formData, username: text})}
195
+ autoCapitalize="none"
196
+ />
197
+ </View>
198
+ <View style={inlineStyle_288_20}>
199
+ <Text style={inlineStyle_289_22}>Senha *</Text>
200
+ <View style={inlineStyle_290_22}>
201
+ <TextInput
202
+ style={inlineStyle_292_20}
203
+ placeholder="Senha"
204
+ value={formData.password}
205
+ onChangeText={text => setFormData({...formData, password: text})}
206
+ secureTextEntry={!showPassword}
207
+ />
208
+ <TouchableOpacity
209
+ onPress={() => setShowPassword(prev => !prev)}
210
+ style={inlineStyle_303_20}>
211
+ <Icon
212
+ name={showPassword ? 'visibility-off' : 'visibility'}
213
+ size={20}
214
+ color="#6c757d"
215
+ />
216
+ </TouchableOpacity>
217
+ </View>
218
+ </View>
219
+ <View style={inlineStyle_312_20}>
220
+ <Text style={inlineStyle_313_22}>Confirmar senha *</Text>
221
+ <View style={inlineStyle_314_22}>
222
+ <TextInput
223
+ style={inlineStyle_316_20}
224
+ placeholder="Confirmar senha"
225
+ value={formData.confirmPassword}
226
+ onChangeText={text => setFormData({...formData, confirmPassword: text})}
227
+ secureTextEntry={!showConfirmPassword}
228
+ />
229
+ <TouchableOpacity
230
+ onPress={() => setShowConfirmPassword(prev => !prev)}
231
+ style={inlineStyle_327_20}>
232
+ <Icon
233
+ name={showConfirmPassword ? 'visibility-off' : 'visibility'}
234
+ size={20}
235
+ color="#6c757d"
236
+ />
237
+ </TouchableOpacity>
238
+ </View>
239
+ </View>
240
+
241
+ <View style={inlineStyle_275_20}>
242
+ <Text style={inlineStyle_276_22}>Timezone *</Text>
243
+ {loadingTimezones ? (
244
+ <ActivityIndicator size="small" color="#0F172A" />
245
+ ) : (
246
+ <View style={timezoneListStyle}>
247
+ {timezoneOptions.length === 0 ? (
248
+ <Text style={[timezoneOptionTextStyle, {padding: 12}]}>
249
+ Nenhum timezone disponível. Tente novamente.
250
+ </Text>
251
+ ) : (
252
+ timezoneOptions.map((opt, index) => {
253
+ const selected = opt.id === selectedTimezoneId;
254
+ const isLast = index === timezoneOptions.length - 1;
255
+ return (
256
+ <TouchableOpacity
257
+ key={opt.id}
258
+ style={[
259
+ timezoneOptionStyle,
260
+ isLast && {borderBottomWidth: 0},
261
+ selected && {backgroundColor: '#E0F2FE'},
262
+ ]}
263
+ onPress={() => setFormData({...formData, timezoneId: opt.id})}>
264
+ <Text
265
+ style={[
266
+ timezoneOptionTextStyle,
267
+ selected && {color: '#0369A1', fontWeight: '600'},
268
+ ]}>
269
+ {opt.label}
270
+ </Text>
271
+ {selected ? <Icon name="check" size={18} color="#0284C7" /> : null}
272
+ </TouchableOpacity>
273
+ );
274
+ })
275
+ )}
276
+ </View>
277
+ )}
278
+ {!selectedTimezoneId && !loadingTimezones ? (
279
+ <Text style={{color: '#DC2626', marginTop: 6, fontSize: 13}}>
280
+ Selecione um timezone (obrigatório).
281
+ </Text>
282
+ ) : null}
283
+ </View>
284
+ </View>
285
+ )}
286
+ </ScrollView>
287
+
288
+ <View style={inlineStyle_339_16}>
289
+ <TouchableOpacity onPress={onClose} style={inlineStyle_341_14}>
290
+ <Text style={{color: '#64748B', fontWeight: '600'}}>Cancelar</Text>
291
+ </TouchableOpacity>
292
+ <TouchableOpacity onPress={onSave} style={inlineStyle_348_20}>
293
+ <Text style={{color: '#FFFFFF', fontWeight: '600'}}>Salvar</Text>
294
+ </TouchableOpacity>
295
+ </View>
296
+ </View>
297
+ </AnimatedModal>
298
+ );
299
+ };
300
+
301
+ export default UserFormModal;