@controleonline/ui-common 1.2.70 → 1.2.71

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 (61) hide show
  1. package/package.json +31 -28
  2. package/src/api/index.js +237 -37
  3. package/src/react/components/AddImportModal.js +1 -1
  4. package/src/react/components/AnimatedModal.js +171 -0
  5. package/src/react/components/AnimatedModal.styles.js +21 -0
  6. package/src/react/components/AppTypeSwitcher.js +164 -0
  7. package/src/react/components/AppTypeSwitcher.styles.js +109 -0
  8. package/src/react/components/BackgroundRuntimeBridge.js +5 -4
  9. package/src/react/components/BottomNavigationBar.js +86 -31
  10. package/src/react/components/BottomNavigationBar.styles.js +118 -110
  11. package/src/react/components/DefaultProvider.native.js +68 -66
  12. package/src/react/components/DefaultProvider.web.js +44 -42
  13. package/src/react/components/DeliveryPushBridge.native.js +2 -2
  14. package/src/react/components/DeviceAlertSoundService.js +3 -3
  15. package/src/react/components/KioskModeBridge.js +2 -2
  16. package/src/react/components/LauncherModeBridge.js +2 -2
  17. package/src/react/components/ManagerPushBridge.native.js +2 -2
  18. package/src/react/components/RemoteCheckoutService.js +28 -20
  19. package/src/react/components/RuntimeBottomNavigationBar.js +146 -119
  20. package/src/react/components/RuntimeInfoFooter.js +37 -9
  21. package/src/react/components/StateStore.js +258 -0
  22. package/src/react/components/WebsocketListener.native.js +11 -11
  23. package/src/react/config/deviceConfigBootstrap.js +66 -27
  24. package/src/react/css/orders.js +72 -0
  25. package/src/react/pages/SettingsPage/PaymentTypesByWalletTab.js +484 -454
  26. package/src/react/pages/SettingsPage/index.js +1266 -1167
  27. package/src/react/print/providers/local.js +1 -1
  28. package/src/react/router/publicRoutes.js +25 -0
  29. package/src/react/services/Cielo/Checkout.js +39 -0
  30. package/src/react/services/Cielo/Cielo.js +193 -0
  31. package/src/react/services/Cielo/Print.js +7 -0
  32. package/src/react/services/InfinitePay/Checkout.js +33 -0
  33. package/src/react/services/InfinitePay/InfinitePay.js +24 -0
  34. package/src/react/{utils → services}/paymentGatewayExecution.js +91 -91
  35. package/src/react/styles/global.js +115 -0
  36. package/src/react/utils/fileUrl.js +182 -16
  37. package/src/react/utils/menuNavigation.js +31 -0
  38. package/src/react/utils/orderIdentity.js +277 -0
  39. package/src/react/utils/remotePayment.js +115 -61
  40. package/src/react/utils/runtimeMenu.js +35 -3
  41. package/src/react/utils/shopConfig.js +50 -24
  42. package/src/react/utils/shopFranchises.js +56 -22
  43. package/src/react/utils/socketRuntimePipeline.js +14 -14
  44. package/src/store/configs/index.js +2 -2
  45. package/src/tests/react/api/loadSmokeIndex.test.js +75 -0
  46. package/src/tests/react/config/deviceConfigBootstrap.test.js +47 -0
  47. package/src/tests/react/print/localPrintProvider.test.js +1 -1
  48. package/src/tests/react/services/Cielo.test.js +190 -0
  49. package/src/tests/react/utils/fileUrl.test.js +62 -0
  50. package/src/tests/react/utils/importStatus.test.js +2 -2
  51. package/src/tests/react/utils/orderIdentity.test.js +139 -0
  52. package/src/tests/react/utils/remotePayment.test.js +62 -0
  53. package/src/tests/react/utils/runtimeFooter.test.js +4 -6
  54. package/src/tests/react/utils/runtimeLanguage.test.js +2 -2
  55. package/src/tests/react/utils/runtimeMenu.test.js +23 -3
  56. package/src/tests/react/utils/shopConfig.test.js +74 -0
  57. package/src/tests/react/utils/shopFranchises.test.js +79 -12
  58. package/src/tests/react/utils/translate.test.mjs +166 -102
  59. package/src/utils/formatter.js +18 -0
  60. package/src/utils/integrationConfigs.js +80 -79
  61. package/src/utils/translate.js +125 -30
@@ -0,0 +1,258 @@
1
+ import React from 'react';
2
+ import {ActivityIndicator, Text, View} from 'react-native';
3
+ import globalStyles from '../styles/global';
4
+ import {useStores} from '@store';
5
+
6
+ const resolveStateMessage = (value, fallback) => {
7
+ if (value === null || value === undefined || value === false) {
8
+ return '';
9
+ }
10
+
11
+ if (typeof value === 'boolean') {
12
+ return fallback;
13
+ }
14
+
15
+ if (typeof value === 'string') {
16
+ return value.trim() || fallback;
17
+ }
18
+
19
+ return fallback;
20
+ };
21
+
22
+ const StateStore = ({
23
+ store,
24
+ stores = [],
25
+ mode = 'default',
26
+ loading = false,
27
+ saving = false,
28
+ loadingText = 'Carregando...',
29
+ savingText = 'Salvando...',
30
+ title = '',
31
+ subtitle = '',
32
+ showSpinner = false,
33
+ spinnerColor = '#0EA5E9',
34
+ spinnerSize = 'small',
35
+ align = 'center',
36
+ compact = false,
37
+ containerStyle = null,
38
+ contentStyle = null,
39
+ messageStyle = null,
40
+ titleStyle = null,
41
+ subtitleStyle = null,
42
+ }) => {
43
+ const styles = globalStyles();
44
+ const allStores = typeof useStores === 'function' ? useStores(state => state) : null;
45
+ const storeNames = [
46
+ ...(Array.isArray(store) ? store : store ? [store] : []),
47
+ ...(Array.isArray(stores) ? stores : []),
48
+ ].filter(Boolean);
49
+ const isCompactMode = mode === 'compact';
50
+ const isDisplayMode = mode === 'display';
51
+ const isCompactLayout = compact || isCompactMode;
52
+ const containerBaseStyle = isCompactLayout
53
+ ? styles.state.compactContainer
54
+ : isDisplayMode
55
+ ? styles.state.displayContainer || styles.state.container
56
+ : styles.state.container;
57
+ const contentBaseStyle = isCompactLayout
58
+ ? styles.state.compactContent
59
+ : [
60
+ styles.state.content,
61
+ styles.state.loadingContainer,
62
+ isDisplayMode ? styles.state.displayContent : null,
63
+ ].filter(Boolean);
64
+ const textAlign = align === 'left' ? 'left' : 'center';
65
+ const alignItems = align === 'left' ? 'flex-start' : 'center';
66
+ const shouldShowSpinner = showSpinner || isCompactLayout || isDisplayMode;
67
+
68
+ if (!allStores) {
69
+ const runtimeLoadingText = resolveStateMessage(loading, loadingText);
70
+ const runtimeSavingText = resolveStateMessage(saving, savingText);
71
+ const runtimeEntries = [
72
+ runtimeLoadingText ? {kind: 'loading', label: runtimeLoadingText} : null,
73
+ runtimeSavingText ? {kind: 'saving', label: runtimeSavingText} : null,
74
+ ].filter(Boolean);
75
+
76
+ if (runtimeEntries.length === 0) {
77
+ return null;
78
+ }
79
+
80
+ return (
81
+ <View style={[containerBaseStyle, containerStyle]}>
82
+ <View style={[contentBaseStyle, {alignItems}, contentStyle]}>
83
+ {(showSpinner || title || subtitle) ? (
84
+ <View
85
+ style={{
86
+ alignItems: alignItems === 'flex-start' ? 'flex-start' : 'center',
87
+ flexDirection: 'row',
88
+ gap: 10,
89
+ justifyContent: alignItems === 'flex-start' ? 'flex-start' : 'center',
90
+ marginBottom: 12,
91
+ width: '100%',
92
+ }}
93
+ >
94
+ {showSpinner ? (
95
+ <ActivityIndicator size={spinnerSize} color={spinnerColor} />
96
+ ) : null}
97
+ {(title || subtitle) ? (
98
+ <View style={{flex: 1, gap: 2}}>
99
+ {title ? (
100
+ <Text
101
+ style={[
102
+ {
103
+ color: '#0F172A',
104
+ fontSize: 14,
105
+ fontWeight: '800',
106
+ textAlign,
107
+ },
108
+ titleStyle,
109
+ ]}
110
+ >
111
+ {title}
112
+ </Text>
113
+ ) : null}
114
+ {subtitle ? (
115
+ <Text
116
+ style={[
117
+ {
118
+ color: '#64748B',
119
+ fontSize: 12,
120
+ lineHeight: 16,
121
+ textAlign,
122
+ },
123
+ subtitleStyle,
124
+ ]}
125
+ >
126
+ {subtitle}
127
+ </Text>
128
+ ) : null}
129
+ </View>
130
+ ) : null}
131
+ </View>
132
+ ) : null}
133
+ {runtimeEntries.map(entry => (
134
+ <Text
135
+ key={`${entry.kind}-${entry.label}`}
136
+ style={[
137
+ styles.state.messageText,
138
+ {textAlign},
139
+ messageStyle,
140
+ ]}
141
+ >
142
+ {entry.label}
143
+ </Text>
144
+ ))}
145
+ </View>
146
+ </View>
147
+ );
148
+ }
149
+
150
+ const storeEntries = storeNames
151
+ .map(storeName => {
152
+ const currentStore = allStores?.[storeName] || {};
153
+ const getters = currentStore?.getters || {};
154
+
155
+ if (getters.isLoading === true) {
156
+ return {
157
+ kind: 'loading',
158
+ label: `Carregando${storeName ? `: ${storeName}` : ''}`,
159
+ };
160
+ }
161
+
162
+ if (getters.isSaving === true) {
163
+ return {
164
+ kind: 'saving',
165
+ label: `Salvando${storeName ? `: ${storeName}` : ''}`,
166
+ };
167
+ }
168
+
169
+ return null;
170
+ })
171
+ .filter(Boolean);
172
+
173
+ const runtimeEntries = [
174
+ resolveStateMessage(loading, loadingText)
175
+ ? {kind: 'loading', label: resolveStateMessage(loading, loadingText)}
176
+ : null,
177
+ resolveStateMessage(saving, savingText)
178
+ ? {kind: 'saving', label: resolveStateMessage(saving, savingText)}
179
+ : null,
180
+ ].filter(Boolean);
181
+
182
+ const entries = [...storeEntries, ...runtimeEntries];
183
+
184
+ if (entries.length === 0) {
185
+ return null;
186
+ }
187
+
188
+ return (
189
+ <View style={[containerBaseStyle, containerStyle]}>
190
+ <View style={[contentBaseStyle, {alignItems}, contentStyle]}>
191
+ {(shouldShowSpinner || title || subtitle) ? (
192
+ <View
193
+ style={{
194
+ alignItems: alignItems === 'flex-start' ? 'flex-start' : 'center',
195
+ flexDirection: 'row',
196
+ gap: 10,
197
+ justifyContent: alignItems === 'flex-start' ? 'flex-start' : 'center',
198
+ marginBottom: 12,
199
+ width: '100%',
200
+ }}
201
+ >
202
+ {shouldShowSpinner ? (
203
+ <ActivityIndicator size={spinnerSize} color={spinnerColor} />
204
+ ) : null}
205
+ {(title || subtitle) ? (
206
+ <View style={{flex: 1, gap: 2}}>
207
+ {title ? (
208
+ <Text
209
+ style={[
210
+ {
211
+ color: '#0F172A',
212
+ fontSize: 14,
213
+ fontWeight: '800',
214
+ textAlign,
215
+ },
216
+ titleStyle,
217
+ ]}
218
+ >
219
+ {title}
220
+ </Text>
221
+ ) : null}
222
+ {subtitle ? (
223
+ <Text
224
+ style={[
225
+ {
226
+ color: '#64748B',
227
+ fontSize: 12,
228
+ lineHeight: 16,
229
+ textAlign,
230
+ },
231
+ subtitleStyle,
232
+ ]}
233
+ >
234
+ {subtitle}
235
+ </Text>
236
+ ) : null}
237
+ </View>
238
+ ) : null}
239
+ </View>
240
+ ) : null}
241
+ {entries.map(entry => (
242
+ <Text
243
+ key={`${entry.kind}-${entry.label}`}
244
+ style={[
245
+ styles.state.messageText,
246
+ {textAlign},
247
+ messageStyle,
248
+ ]}
249
+ >
250
+ {entry.label}
251
+ </Text>
252
+ ))}
253
+ </View>
254
+ </View>
255
+ );
256
+ };
257
+
258
+ export default StateStore;
@@ -74,12 +74,12 @@ export const WebsocketListener = () => {
74
74
  : 'socket:open'
75
75
  : `socket:${normalizedStatus || 'off'}`;
76
76
 
77
- const indicatorColor =
78
- socketConnected && socketIdentified
79
- ? '#10b981'
80
- : (socketConnected || ['connecting', 'identifying', 'open', 'reconnecting'].includes(normalizedStatus))
81
- ? '#e67e22'
82
- : '#c10015';
77
+ const indicatorTone =
78
+ socketConnected && socketIdentified
79
+ ? 'success'
80
+ : (socketConnected || ['connecting', 'identifying', 'open', 'reconnecting'].includes(normalizedStatus))
81
+ ? 'warning'
82
+ : 'error';
83
83
  const lastSocketStores = Array.isArray(state?.lastStores)
84
84
  ? state.lastStores.filter(Boolean).join(', ')
85
85
  : '';
@@ -88,11 +88,11 @@ export const WebsocketListener = () => {
88
88
  : '';
89
89
 
90
90
  runtimeDebugActions.setFooterEntry({
91
- key: 'socket',
92
- order: 10,
93
- indicatorColor,
94
- updatedAt: state?.updatedAt || new Date().toISOString(),
95
- lines: [
91
+ key: 'socket',
92
+ order: 10,
93
+ indicatorTone,
94
+ updatedAt: state?.updatedAt || new Date().toISOString(),
95
+ lines: [
96
96
  `Realtime ${stateLabel} | empresa: ${normalizeText(
97
97
  currentCompany?.id,
98
98
  ) || '--'} | device: ${truncateText(state?.device || device?.id || '--', 34)}`,
@@ -1,7 +1,11 @@
1
- import {
2
- appendScreenMetrics,
3
- hasScreenMetricsChanges,
4
- } from '@controleonline/ui-common/src/react/utils/screenMetrics';
1
+ import {
2
+ appendScreenMetrics,
3
+ hasScreenMetricsChanges,
4
+ } from '@controleonline/ui-common/src/react/utils/screenMetrics';
5
+ import {
6
+ normalizeBooleanConfig,
7
+ SHOP_LOYALTY_COUPONS_ENABLED_CONFIG_KEY,
8
+ } from '@controleonline/ui-common/src/react/utils/shopConfig';
5
9
 
6
10
  export const CIELO_DEVICES = ['quantum', 'ingenico', 'positivo'];
7
11
  export const SUPPORTED_POS_GATEWAYS = ['cielo', 'infinite-pay'];
@@ -38,9 +42,10 @@ export const POS_OPERATION_MODE_WAITER = 'waiter';
38
42
  export const POS_OPERATION_MODE_TOTEM = 'totem';
39
43
  export const POS_OPERATION_MODE_SINGLE_ITEM = 'single-item';
40
44
  export const POS_OPERATION_MODE_CASHIER = 'cashier';
41
- export const POS_CHECK_ORDER_TYPE_NONE = 'none';
42
- export const POS_CHECK_ORDER_TYPE_TAB = 'tab';
43
- export const POS_CHECK_ORDER_TYPE_TABLE = 'table';
45
+ export const POS_CHECK_ORDER_TYPE_NONE = 'none';
46
+ export const POS_CHECK_ORDER_TYPE_TAB = 'tab';
47
+ export const POS_CHECK_ORDER_TYPE_TABLE = 'table';
48
+ export const POS_CHECK_ORDER_TYPE_STAMP = 'stamp';
44
49
  export const POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE = 'manage';
45
50
  export const POS_CHECK_ORDER_MANAGEMENT_MODE_EXISTING_ONLY = 'existing-only';
46
51
  export const POS_OPERATION_MODE_DEFAULT = POS_OPERATION_MODE_CASHIER;
@@ -237,31 +242,65 @@ export const normalizePosCheckOrderType = value => {
237
242
  .replace(/[\u0300-\u036f]/g, '')
238
243
  .replace(/[\s_]/g, '-');
239
244
 
240
- if (
241
- [
242
- POS_CHECK_ORDER_TYPE_TAB,
243
- 'tab',
244
- ].includes(normalizedValue)
245
+ if (
246
+ [
247
+ POS_CHECK_ORDER_TYPE_TAB,
248
+ 'tab',
249
+ ].includes(normalizedValue)
245
250
  ) {
246
251
  return POS_CHECK_ORDER_TYPE_TAB;
247
252
  }
248
253
 
249
- if (
250
- [
251
- POS_CHECK_ORDER_TYPE_TABLE,
252
- 'table',
253
- ].includes(normalizedValue)
254
- ) {
255
- return POS_CHECK_ORDER_TYPE_TABLE;
256
- }
257
-
258
- return POS_CHECK_ORDER_TYPE_NONE;
259
- };
254
+ if (
255
+ [
256
+ POS_CHECK_ORDER_TYPE_TABLE,
257
+ 'table',
258
+ ].includes(normalizedValue)
259
+ ) {
260
+ return POS_CHECK_ORDER_TYPE_TABLE;
261
+ }
262
+
263
+ if (
264
+ [
265
+ POS_CHECK_ORDER_TYPE_STAMP,
266
+ 'stamp',
267
+ 'carimbo',
268
+ ].includes(normalizedValue)
269
+ ) {
270
+ return POS_CHECK_ORDER_TYPE_STAMP;
271
+ }
272
+
273
+ return POS_CHECK_ORDER_TYPE_NONE;
274
+ };
260
275
 
261
- export const resolvePosCheckOrderType = configs =>
262
- normalizePosCheckOrderType(
263
- parseConfigsObject(configs)?.[POS_CHECK_ORDER_TYPE_CONFIG_KEY],
264
- );
276
+ export const resolvePosCheckOrderType = configs =>
277
+ normalizePosCheckOrderType(
278
+ parseConfigsObject(configs)?.[POS_CHECK_ORDER_TYPE_CONFIG_KEY],
279
+ );
280
+
281
+ export const resolvePosCheckOrderTypeForShop = (
282
+ deviceConfigs,
283
+ shopConfigs,
284
+ ) => {
285
+ const resolvedType = resolvePosCheckOrderType(deviceConfigs);
286
+ const parsedShopConfigs = parseConfigsObject(shopConfigs);
287
+ const hasLoyaltyCouponsEnabledKey = Object.prototype.hasOwnProperty.call(
288
+ parsedShopConfigs,
289
+ SHOP_LOYALTY_COUPONS_ENABLED_CONFIG_KEY,
290
+ );
291
+
292
+ if (
293
+ hasLoyaltyCouponsEnabledKey &&
294
+ resolvedType === POS_CHECK_ORDER_TYPE_STAMP &&
295
+ !normalizeBooleanConfig(
296
+ parsedShopConfigs?.[SHOP_LOYALTY_COUPONS_ENABLED_CONFIG_KEY],
297
+ )
298
+ ) {
299
+ return POS_CHECK_ORDER_TYPE_NONE;
300
+ }
301
+
302
+ return resolvedType;
303
+ };
265
304
 
266
305
  export const normalizePosCheckOrderManagementMode = value => {
267
306
  const normalizedValue = String(value || '')
@@ -0,0 +1,72 @@
1
+ import {StyleSheet} from 'react-native';
2
+ import {useStore} from '@store';
3
+ import globalStyles from '../styles/global';
4
+
5
+ const css = () => {
6
+ const themeStore = useStore('theme');
7
+ const getters = themeStore.getters;
8
+ const {colors} = getters;
9
+
10
+ const styles = StyleSheet.create({
11
+ Settings: {
12
+ container: {
13
+ flex: 1,
14
+ backgroundColor: '#fff',
15
+ },
16
+ scrollContent: {
17
+ paddingHorizontal: 20,
18
+ paddingBottom: 20,
19
+ },
20
+ mainContainer: {
21
+ flex: 1,
22
+ },
23
+ row: {
24
+ flexDirection: 'row',
25
+ alignItems: 'center',
26
+ marginBottom: 5,
27
+ paddingVertical: 5,
28
+ },
29
+ label: {
30
+ flex: 0.5,
31
+ fontSize: 14,
32
+ color: '#333',
33
+ },
34
+ value: {
35
+ flex: 0.5,
36
+ fontSize: 14,
37
+ color: '#000',
38
+ },
39
+ walletRow: {
40
+ flexDirection: 'row',
41
+ alignItems: 'center',
42
+ marginBottom: 5,
43
+ paddingVertical: 5,
44
+ },
45
+ walletValueContainer: {
46
+ flex: 0.5,
47
+ flexDirection: 'row',
48
+ alignItems: 'center',
49
+ },
50
+ walletValue: {
51
+ fontSize: 14,
52
+ color: '#000',
53
+ marginRight: 5,
54
+ },
55
+ picker: {
56
+ height: 50,
57
+ fontSize: 14,
58
+ color: '#000',
59
+ backgroundColor: '#f0f0f0',
60
+ borderRadius: 5,
61
+ marginTop: 5,
62
+ },
63
+ },
64
+ primary: {
65
+ color: colors.primary,
66
+ },
67
+ });
68
+
69
+ return {styles, globalStyles: globalStyles()};
70
+ };
71
+
72
+ export default css;