@controleonline/ui-common 1.2.70 → 1.2.75

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 (188) hide show
  1. package/.github/actions/workers/manager/action.yml +147 -0
  2. package/.github/actions/workers/qa/action.yml +44 -0
  3. package/.github/actions/workers/security/action.yml +44 -0
  4. package/.github/actions/workers/technical-documenter/action.yml +51 -0
  5. package/.github/workflows/manager-worker.yml +168 -0
  6. package/.github/workflows/publish-npm.yml +45 -0
  7. package/.gitmodules +3 -0
  8. package/AGENTS.md +6 -0
  9. package/README.md +14 -0
  10. package/package.json +32 -28
  11. package/src/api/fetch.js +1 -1
  12. package/src/api/index.js +328 -58
  13. package/src/react/components/AddImportModal.js +95 -111
  14. package/src/react/components/AnimatedModal.js +171 -0
  15. package/src/react/components/AnimatedModal.styles.js +21 -0
  16. package/src/react/components/AppTypeSwitcher.js +164 -0
  17. package/src/react/components/AppTypeSwitcher.styles.js +109 -0
  18. package/src/react/components/BackgroundRuntimeBridge.js +5 -4
  19. package/src/react/components/BottomNavigationBar.config.js +211 -231
  20. package/src/react/components/BottomNavigationBar.js +86 -31
  21. package/src/react/components/BottomNavigationBar.styles.js +118 -110
  22. package/src/react/components/CategoryForm.js +52 -35
  23. package/src/react/components/DefaultProvider.native.js +194 -156
  24. package/src/react/components/DefaultProvider.web.js +188 -112
  25. package/src/react/components/DeliveryPushBridge.native.js +2 -2
  26. package/src/react/components/DeviceAlertSoundService.js +3 -3
  27. package/src/react/components/KioskModeBridge.js +2 -2
  28. package/src/react/components/LauncherModeBridge.js +2 -2
  29. package/src/react/components/ManagerPushBridge.native.js +2 -2
  30. package/src/react/components/NetworkPrinterPreviewModal.js +249 -0
  31. package/src/react/components/NetworkPrinterPreviewModal.styles.js +159 -0
  32. package/src/react/components/RemoteCheckoutService.js +28 -20
  33. package/src/react/components/RuntimeBottomNavigationBar.js +146 -119
  34. package/src/react/components/RuntimeInfoFooter.js +137 -45
  35. package/src/react/components/RuntimeInfoFooter.styles.js +3 -0
  36. package/src/react/components/StateStore.js +258 -0
  37. package/src/react/components/UnifiedPaymentBar.js +7 -4
  38. package/src/react/components/UserAvatar.js +160 -0
  39. package/src/react/components/WebsocketListener.native.js +11 -11
  40. package/src/react/components/integrations/IntegrationHero/index.js +48 -0
  41. package/src/react/components/integrations/IntegrationHero/styles.js +57 -0
  42. package/src/react/components/integrations/IntegrationTabs/index.js +89 -0
  43. package/src/react/components/integrations/IntegrationTabs/styles.js +44 -0
  44. package/src/react/config/deviceConfigBootstrap.js +71 -30
  45. package/src/react/css/orders.js +72 -0
  46. package/src/react/pages/Connections.js +229 -0
  47. package/src/react/pages/Connections.styles.js +222 -0
  48. package/src/react/pages/DeviceDetailPage.js +2814 -0
  49. package/src/react/pages/DeviceDetailPage.styles.js +308 -0
  50. package/src/react/pages/Devices/currentDevice.js +194 -0
  51. package/src/react/pages/Devices/deviceTypes/DeviceGroupCard.js +257 -0
  52. package/src/react/pages/Devices/deviceTypes/all.js +23 -0
  53. package/src/react/pages/Devices/deviceTypes/device.js +25 -0
  54. package/src/react/pages/Devices/deviceTypes/deviceListHelpers.js +178 -0
  55. package/src/react/pages/Devices/deviceTypes/display.js +26 -0
  56. package/src/react/pages/Devices/deviceTypes/index.js +145 -0
  57. package/src/react/pages/Devices/deviceTypes/ipCamera.js +35 -0
  58. package/src/react/pages/Devices/deviceTypes/pdv.js +88 -0
  59. package/src/react/pages/Devices/deviceTypes/printer.js +37 -0
  60. package/src/react/pages/Devices/deviceTypes/shared.js +453 -0
  61. package/src/react/pages/Devices/deviceTypes/useDeviceNetworkConnectivity.js +145 -0
  62. package/src/react/pages/Devices.js +183 -0
  63. package/src/react/pages/Devices.styles.js +353 -0
  64. package/src/react/pages/Food99IntegrationPage/components/Food99CatalogTab.js +219 -0
  65. package/src/react/pages/Food99IntegrationPage/components/Food99OverviewTab.js +134 -0
  66. package/src/react/pages/Food99IntegrationPage/components/Food99PreviewModal.js +173 -0
  67. package/src/react/pages/Food99IntegrationPage/components/Food99ProductCard.js +1 -0
  68. package/src/react/pages/Food99IntegrationPage/components/Food99QuickWalletModal.js +181 -0
  69. package/src/react/pages/Food99IntegrationPage/components/Food99SettingsTab.js +306 -0
  70. package/src/react/pages/Food99IntegrationPage/components/Food99StoreTab.js +417 -0
  71. package/src/react/pages/Food99IntegrationPage/index.js +1530 -0
  72. package/src/react/pages/Food99IntegrationPage/styles.js +792 -0
  73. package/src/react/pages/Food99IntegrationPage/utils.js +155 -0
  74. package/src/react/pages/Food99IntegrationPage.js +1 -0
  75. package/src/react/pages/Food99IntegrationPage.styles.js +1 -0
  76. package/src/react/pages/GenericLogPage.js +42 -167
  77. package/src/react/pages/IFoodIntegrationPage/components/IFoodCatalogTab.js +8 -0
  78. package/src/react/pages/IFoodIntegrationPage/components/IFoodOperationsTab.js +420 -0
  79. package/src/react/pages/IFoodIntegrationPage/components/IFoodOverviewTab.js +118 -0
  80. package/src/react/pages/IFoodIntegrationPage/components/IFoodPreviewModal.js +103 -0
  81. package/src/react/pages/IFoodIntegrationPage/components/IFoodProductCard.js +1 -0
  82. package/src/react/pages/IFoodIntegrationPage/components/IFoodStoreTab.js +150 -0
  83. package/src/react/pages/IFoodIntegrationPage/index.js +1298 -0
  84. package/src/react/pages/IFoodIntegrationPage/styles.js +764 -0
  85. package/src/react/pages/IFoodIntegrationPage/utils.js +91 -0
  86. package/src/react/pages/IFoodIntegrationPage.js +1 -0
  87. package/src/react/pages/IFoodIntegrationPage.styles.js +1 -0
  88. package/src/react/pages/Imports.js +214 -166
  89. package/src/react/pages/IntegrationConfigPage.js +744 -0
  90. package/src/react/pages/IntegrationConfigPage.styles.js +269 -0
  91. package/src/react/pages/Integrations.js +299 -0
  92. package/src/react/pages/Integrations.styles.js +130 -0
  93. package/src/react/pages/ManagerOrderNotificationsPage.js +642 -0
  94. package/src/react/pages/MarketplaceIntegrationPage/components/MarketplaceProductCard.js +205 -0
  95. package/src/react/pages/MarketplaceIntegrationPage/components/RemoteMarketplaceMenuTab.js +335 -0
  96. package/src/react/pages/MarketplaceIntegrationPage/index.js +60 -0
  97. package/src/react/pages/MarketplaceIntegrationPage/utils.js +137 -0
  98. package/src/react/pages/MercadoLivreIntegrationPage.js +538 -0
  99. package/src/react/pages/PrinterDeviceDetailPage.js +1916 -0
  100. package/src/react/pages/PrinterDeviceDetailPage.styles.js +223 -0
  101. package/src/react/pages/PrinterDeviceFormPage.js +521 -0
  102. package/src/react/pages/PrinterDeviceFormPage.styles.js +159 -0
  103. package/src/react/pages/SettingsPage/PaymentTypesByWalletTab.js +484 -454
  104. package/src/react/pages/SettingsPage/index.js +1266 -1167
  105. package/src/react/pages/WhatsAppConnectionPage.js +459 -0
  106. package/src/react/pages/WhatsAppConnectionPage.styles.js +284 -0
  107. package/src/react/pages/integrationsCatalog.js +399 -0
  108. package/src/react/print/providers/local.js +1 -1
  109. package/src/react/router/publicRoutes.js +25 -0
  110. package/src/react/services/Cielo/Checkout.js +39 -0
  111. package/src/react/services/Cielo/Cielo.js +193 -0
  112. package/src/react/services/Cielo/Print.js +7 -0
  113. package/src/react/services/InfinitePay/Checkout.js +33 -0
  114. package/src/react/services/InfinitePay/InfinitePay.js +24 -0
  115. package/src/react/{utils → services}/paymentGatewayExecution.js +91 -91
  116. package/src/react/styles/global.js +115 -0
  117. package/src/react/utils/categoryContexts.js +46 -0
  118. package/src/react/utils/entityDisplay.js +222 -217
  119. package/src/react/utils/fileUrl.js +201 -16
  120. package/src/react/utils/integrationPage.js +16 -0
  121. package/src/react/utils/menuNavigation.js +31 -0
  122. package/src/react/utils/orderIdentity.js +277 -0
  123. package/src/react/utils/remotePayment.js +115 -61
  124. package/src/react/utils/runtimeFooter.js +40 -22
  125. package/src/react/utils/runtimeLanguage.js +50 -9
  126. package/src/react/utils/runtimeMenu.js +190 -98
  127. package/src/react/utils/runtimeZoom.js +43 -0
  128. package/src/react/utils/shopConfig.js +72 -34
  129. package/src/react/utils/shopFranchises.js +182 -23
  130. package/src/react/utils/socketRuntimePipeline.js +14 -14
  131. package/src/react/utils/storeColumns.js +23 -4
  132. package/src/react/utils/userAvatar.js +104 -0
  133. package/src/store/categories/index.js +48 -20
  134. package/src/store/configs/index.js +2 -2
  135. package/src/store/device_config/index.js +62 -21
  136. package/src/store/entity_log/index.js +22 -1
  137. package/src/store/imports/customActions.js +51 -0
  138. package/src/store/imports/index.js +89 -88
  139. package/src/store/status/index.js +11 -11
  140. package/src/store/themes/index.js +22 -0
  141. package/src/store/timezones/index.js +3 -1
  142. package/src/tests/browser/manager/connections.spec.js +143 -0
  143. package/src/tests/browser/manager/devices-current.spec.js +343 -0
  144. package/src/tests/browser/manager/mercado-livre-integration.spec.js +268 -0
  145. package/src/tests/react/api/deviceHeaders.test.js +114 -0
  146. package/src/tests/react/api/loadSmokeIndex.test.js +75 -0
  147. package/src/tests/react/components/AddImportModal.test.js +166 -0
  148. package/src/tests/react/components/BottomNavigationBar.config.test.js +20 -0
  149. package/src/tests/react/components/UserAvatar.test.js +52 -0
  150. package/src/tests/react/config/deviceConfigBootstrap.test.js +47 -0
  151. package/src/tests/react/pages/DevicePickerStyles.test.js +47 -0
  152. package/src/tests/react/pages/Devices/deviceListHelpers.test.js +39 -0
  153. package/src/tests/react/pages/DevicesCurrentDevice.test.js +198 -0
  154. package/src/tests/react/pages/connectionsHelpers.test.js +34 -0
  155. package/src/tests/react/pages/mercadoLivreIntegrationPageContract.test.mjs +16 -0
  156. package/src/tests/react/print/localPrintProvider.test.js +1 -1
  157. package/src/tests/react/services/Cielo.test.js +190 -0
  158. package/src/tests/react/store/categoriesOrdering.test.js +26 -0
  159. package/src/tests/react/utils/appDomain.test.js +62 -0
  160. package/src/tests/react/utils/entityDisplay.test.js +2 -2
  161. package/src/tests/react/utils/fileUrl.test.js +62 -0
  162. package/src/tests/react/utils/formatter.test.mjs +33 -0
  163. package/src/tests/react/utils/importStatus.test.js +2 -2
  164. package/src/tests/react/utils/orderIdentity.test.js +139 -0
  165. package/src/tests/react/utils/remotePayment.test.js +62 -0
  166. package/src/tests/react/utils/runtimeFooter.test.js +32 -9
  167. package/src/tests/react/utils/runtimeLanguage.test.js +80 -10
  168. package/src/tests/react/utils/runtimeMenu.test.js +195 -111
  169. package/src/tests/react/utils/runtimeZoom.test.js +38 -0
  170. package/src/tests/react/utils/shopConfig.test.js +75 -0
  171. package/src/tests/react/utils/shopFranchises.test.js +122 -12
  172. package/src/tests/react/utils/translate.discovery.test.mjs +170 -0
  173. package/src/tests/react/utils/translate.test.helpers.mjs +152 -0
  174. package/src/tests/react/utils/translate.test.mjs +252 -396
  175. package/src/tests/react/utils/userAvatar.test.js +63 -0
  176. package/src/utils/appDomain.js +15 -4
  177. package/src/utils/formatter.js +122 -16
  178. package/src/utils/integrationConfigs.js +1 -0
  179. package/src/utils/translate.js +277 -398
  180. package/src/utils/translateDiscovery.js +96 -0
  181. package/src/utils/translateNormalize.js +33 -0
  182. package/src/utils/translatePending.js +92 -0
  183. package/src/utils/translateStorage.js +142 -0
  184. package/.github/agents/developer.agent.md +0 -30
  185. package/.github/agents/devops.agent.md +0 -30
  186. package/.github/agents/qa.agent.md +0 -30
  187. package/.github/agents/security.agent.md +0 -30
  188. package/.github/workflows/npmjs.yml +0 -35
@@ -0,0 +1,44 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import {colors} from '@controleonline/../../src/styles/colors';
3
+
4
+ const createStyles = (palette = colors) =>
5
+ StyleSheet.create({
6
+ scrollContent: {
7
+ gap: 8,
8
+ paddingBottom: 4,
9
+ },
10
+ tabButton: {
11
+ minHeight: 40,
12
+ borderRadius: 999,
13
+ borderWidth: 1,
14
+ borderColor: palette.buttonBorderSecondary || palette.border,
15
+ backgroundColor: palette.buttonBackgroundSecondary || palette.white,
16
+ paddingHorizontal: 14,
17
+ paddingVertical: 8,
18
+ flexDirection: 'row',
19
+ alignItems: 'center',
20
+ justifyContent: 'center',
21
+ gap: 8,
22
+ },
23
+ tabLabel: {
24
+ fontSize: 12,
25
+ fontWeight: '700',
26
+ color: palette.buttonTextSecondary || palette.textSecondary,
27
+ },
28
+ tabBadge: {
29
+ minWidth: 22,
30
+ height: 22,
31
+ borderRadius: 999,
32
+ backgroundColor: palette.badgeBackground || palette.border,
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ paddingHorizontal: 6,
36
+ },
37
+ tabBadgeText: {
38
+ fontSize: 11,
39
+ fontWeight: '800',
40
+ color: palette.buttonIconSecondary || palette.textSecondary,
41
+ },
42
+ });
43
+
44
+ export default createStyles;
@@ -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'];
@@ -23,6 +27,7 @@ export const DISPLAY_SIZE_MIN = 1;
23
27
  export const DISPLAY_SIZE_MAX = 10;
24
28
  export const DISPLAY_SIZE_DEFAULT = 5;
25
29
  export const POS_OPERATION_MODE_CONFIG_KEY = 'pos-operation-mode';
30
+ export const POS_PRODUCT_SHOWCASE_CONFIG_KEY = 'pos-product-showcase-id';
26
31
  export const DEVICE_ANDROID_KIOSK_ENABLED_CONFIG_KEY =
27
32
  'android-kiosk-enabled';
28
33
  export const DEVICE_ANDROID_LAUNCHER_ENABLED_CONFIG_KEY =
@@ -38,9 +43,10 @@ export const POS_OPERATION_MODE_WAITER = 'waiter';
38
43
  export const POS_OPERATION_MODE_TOTEM = 'totem';
39
44
  export const POS_OPERATION_MODE_SINGLE_ITEM = 'single-item';
40
45
  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';
46
+ export const POS_CHECK_ORDER_TYPE_NONE = 'none';
47
+ export const POS_CHECK_ORDER_TYPE_TAB = 'tab';
48
+ export const POS_CHECK_ORDER_TYPE_TABLE = 'table';
49
+ export const POS_CHECK_ORDER_TYPE_STAMP = 'stamp';
44
50
  export const POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE = 'manage';
45
51
  export const POS_CHECK_ORDER_MANAGEMENT_MODE_EXISTING_ONLY = 'existing-only';
46
52
  export const POS_OPERATION_MODE_DEFAULT = POS_OPERATION_MODE_CASHIER;
@@ -100,9 +106,10 @@ export const DEFAULT_DEVICE_CONFIGS = {
100
106
  [POS_CHECK_ORDER_MANAGEMENT_MODE_CONFIG_KEY]:
101
107
  POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE,
102
108
  [POS_OPERATION_MODE_CONFIG_KEY]: POS_OPERATION_MODE_DEFAULT,
103
- [POS_AUTO_PRINT_ENABLED_CONFIG_KEY]: '0',
104
- [POS_CASH_MANAGEMENT_MODE_CONFIG_KEY]: POS_CASH_MANAGEMENT_MODE_DEFAULT,
105
- };
109
+ [POS_PRODUCT_SHOWCASE_CONFIG_KEY]: '',
110
+ [POS_AUTO_PRINT_ENABLED_CONFIG_KEY]: '0',
111
+ [POS_CASH_MANAGEMENT_MODE_CONFIG_KEY]: POS_CASH_MANAGEMENT_MODE_DEFAULT,
112
+ };
106
113
 
107
114
  export const isTruthyValue = value =>
108
115
  value === true || value === '1' || value === 1 || value === 'true';
@@ -237,31 +244,65 @@ export const normalizePosCheckOrderType = value => {
237
244
  .replace(/[\u0300-\u036f]/g, '')
238
245
  .replace(/[\s_]/g, '-');
239
246
 
240
- if (
241
- [
242
- POS_CHECK_ORDER_TYPE_TAB,
243
- 'tab',
244
- ].includes(normalizedValue)
247
+ if (
248
+ [
249
+ POS_CHECK_ORDER_TYPE_TAB,
250
+ 'tab',
251
+ ].includes(normalizedValue)
245
252
  ) {
246
253
  return POS_CHECK_ORDER_TYPE_TAB;
247
254
  }
248
255
 
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
- };
256
+ if (
257
+ [
258
+ POS_CHECK_ORDER_TYPE_TABLE,
259
+ 'table',
260
+ ].includes(normalizedValue)
261
+ ) {
262
+ return POS_CHECK_ORDER_TYPE_TABLE;
263
+ }
264
+
265
+ if (
266
+ [
267
+ POS_CHECK_ORDER_TYPE_STAMP,
268
+ 'stamp',
269
+ 'carimbo',
270
+ ].includes(normalizedValue)
271
+ ) {
272
+ return POS_CHECK_ORDER_TYPE_STAMP;
273
+ }
274
+
275
+ return POS_CHECK_ORDER_TYPE_NONE;
276
+ };
260
277
 
261
- export const resolvePosCheckOrderType = configs =>
262
- normalizePosCheckOrderType(
263
- parseConfigsObject(configs)?.[POS_CHECK_ORDER_TYPE_CONFIG_KEY],
264
- );
278
+ export const resolvePosCheckOrderType = configs =>
279
+ normalizePosCheckOrderType(
280
+ parseConfigsObject(configs)?.[POS_CHECK_ORDER_TYPE_CONFIG_KEY],
281
+ );
282
+
283
+ export const resolvePosCheckOrderTypeForShop = (
284
+ deviceConfigs,
285
+ shopConfigs,
286
+ ) => {
287
+ const resolvedType = resolvePosCheckOrderType(deviceConfigs);
288
+ const parsedShopConfigs = parseConfigsObject(shopConfigs);
289
+ const hasLoyaltyCouponsEnabledKey = Object.prototype.hasOwnProperty.call(
290
+ parsedShopConfigs,
291
+ SHOP_LOYALTY_COUPONS_ENABLED_CONFIG_KEY,
292
+ );
293
+
294
+ if (
295
+ hasLoyaltyCouponsEnabledKey &&
296
+ resolvedType === POS_CHECK_ORDER_TYPE_STAMP &&
297
+ !normalizeBooleanConfig(
298
+ parsedShopConfigs?.[SHOP_LOYALTY_COUPONS_ENABLED_CONFIG_KEY],
299
+ )
300
+ ) {
301
+ return POS_CHECK_ORDER_TYPE_NONE;
302
+ }
303
+
304
+ return resolvedType;
305
+ };
265
306
 
266
307
  export const normalizePosCheckOrderManagementMode = value => {
267
308
  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;
@@ -0,0 +1,229 @@
1
+ import React, { useCallback, useMemo } from 'react';
2
+ import { Platform, Text, TouchableOpacity, View } from 'react-native';
3
+ import { SafeAreaView } from 'react-native-safe-area-context';
4
+ import { useFocusEffect, useNavigation } from '@react-navigation/native';
5
+ import Icon from 'react-native-vector-icons/Feather';
6
+
7
+ import { useStore } from '@store';
8
+ import { colors } from '@controleonline/../../src/styles/colors';
9
+ import { createStyles } from './Connections.styles';
10
+ import {
11
+ resolveThemePalette,
12
+ withOpacity,
13
+ } from '@controleonline/../../src/styles/branding';
14
+ import DefaultTable from '@controleonline/ui-default/src/react/components/table/DefaultTable';
15
+
16
+ const tt = (type, key) => global.t?.t('configs', type, key);
17
+
18
+ const resolveShadowStyle = textColor =>
19
+ Platform.select({
20
+ ios: {
21
+ shadowColor: textColor,
22
+ shadowOffset: { width: 0, height: 8 },
23
+ shadowOpacity: 0.08,
24
+ shadowRadius: 16,
25
+ },
26
+ android: { elevation: 3 },
27
+ web: { boxShadow: `0 10px 24px ${withOpacity(textColor, 0.08)}` },
28
+ });
29
+
30
+ const formatPhoneLabel = phone => {
31
+ if (!phone) return tt('connections_label', 'notProvided') || 'Nao informado';
32
+ if (typeof phone === 'string') return phone;
33
+ const ddd = String(phone?.ddd || '').trim();
34
+ const digits = String(phone?.phone || '').replace(/\D/g, '');
35
+ if (!ddd && !digits) return tt('connections_label', 'notProvided') || 'Nao informado';
36
+ if (!digits) return `(${ddd})`;
37
+ const lastFour = digits.slice(-4);
38
+ const firstPart = digits.slice(0, -4);
39
+ if (!firstPart) return ddd ? `(${ddd}) ${lastFour}` : lastFour;
40
+ return ddd ? `(${ddd}) ${firstPart}-${lastFour}` : `${firstPart}-${lastFour}`;
41
+ };
42
+
43
+ const formatStatusLabel = status => {
44
+ if (!status) return tt('connections_status', 'unknown') || '—';
45
+ if (typeof status === 'string') return status;
46
+ return status?.status || status?.name || '—';
47
+ };
48
+
49
+ export default function Connections() {
50
+ const navigation = useNavigation();
51
+ const peopleStore = useStore('people');
52
+ const themeStore = useStore('theme');
53
+ const connectionsStore = useStore('connections');
54
+
55
+ const { currentCompany } = peopleStore.getters || {};
56
+ const { colors: themeColors } = themeStore.getters || {};
57
+ const items = connectionsStore.getters?.items;
58
+ const totalItems = connectionsStore.getters?.totalItems;
59
+
60
+ const brandColors = useMemo(
61
+ () =>
62
+ resolveThemePalette(
63
+ { ...themeColors, ...(currentCompany?.theme?.colors || {}) },
64
+ colors,
65
+ ),
66
+ [themeColors, currentCompany?.id],
67
+ );
68
+ const styles = useMemo(() => createStyles(brandColors), [brandColors]);
69
+ const shadowStyle = useMemo(
70
+ () => resolveShadowStyle(brandColors.text),
71
+ [brandColors.text],
72
+ );
73
+
74
+ const providerId = currentCompany?.id;
75
+ const peopleIri = useMemo(
76
+ () => (providerId ? `/people/${String(providerId).replace(/\D/g, '')}` : ''),
77
+ [providerId],
78
+ );
79
+
80
+ const requestParams = useMemo(
81
+ () => (peopleIri ? { people: peopleIri } : {}),
82
+ [peopleIri],
83
+ );
84
+
85
+ const connectionCount = useMemo(() => {
86
+ if (typeof totalItems === 'number' && totalItems > 0) return totalItems;
87
+ return Array.isArray(items) ? items.length : 0;
88
+ }, [items, totalItems]);
89
+
90
+ useFocusEffect(
91
+ useCallback(() => {
92
+ if (!peopleIri || typeof connectionsStore.actions?.getItems !== 'function') {
93
+ return;
94
+ }
95
+ connectionsStore.actions.getItems({ people: peopleIri });
96
+ }, [peopleIri]),
97
+ );
98
+
99
+ const openWhatsAppChannel = useCallback(() => {
100
+ navigation.navigate('WhatsAppConnectionPage');
101
+ }, [navigation]);
102
+
103
+ const renderConnectionCard = useCallback(
104
+ ({ item }) => {
105
+ if (!item) return null;
106
+ return (
107
+ <View>
108
+ <Text style={styles.previewName}>
109
+ {item.name || tt('connections_label', 'unnamed') || 'Sem nome'}
110
+ </Text>
111
+ <Text style={styles.previewPhone}>{formatPhoneLabel(item.phone)}</Text>
112
+ <Text style={styles.previewStatus}>{formatStatusLabel(item.status)}</Text>
113
+ </View>
114
+ );
115
+ },
116
+ [styles.previewName, styles.previewPhone, styles.previewStatus],
117
+ );
118
+
119
+ if (!providerId) {
120
+ return (
121
+ <SafeAreaView style={styles.container} edges={['bottom']}>
122
+ <View style={styles.centerState}>
123
+ <Icon name="building" size={32} color={brandColors.textSecondary} />
124
+ <Text style={styles.centerStateTitle}>
125
+ {tt('connections_title', 'selectCompany') || 'Selecione uma empresa'}
126
+ </Text>
127
+ <Text style={styles.centerStateText}>
128
+ {tt('connections_text', 'selectCompany') ||
129
+ 'As conexoes disponiveis dependem da empresa ativa.'}
130
+ </Text>
131
+ </View>
132
+ </SafeAreaView>
133
+ );
134
+ }
135
+
136
+ return (
137
+ <SafeAreaView
138
+ style={[styles.container, { backgroundColor: brandColors.background }]}
139
+ edges={['bottom']}>
140
+ <View style={[styles.heroCard, shadowStyle, { backgroundColor: brandColors.primary }]}>
141
+ <View style={styles.heroCopy}>
142
+ <Text style={styles.heroEyebrow}>
143
+ {tt('connections_eyebrow', 'communication') || 'COMUNICACAO'}
144
+ </Text>
145
+ <Text style={styles.heroTitle}>
146
+ {tt('connections_title', 'connections') || 'Conexoes'}
147
+ </Text>
148
+ <Text style={styles.heroText}>
149
+ {tt('connections_text', 'hero') ||
150
+ 'Gerencie os canais de comunicacao conectados com a sua operacao.'}
151
+ </Text>
152
+ </View>
153
+ <View style={styles.heroBadge}>
154
+ <Icon name="radio" size={22} color={brandColors.primary} />
155
+ </View>
156
+ </View>
157
+
158
+ <View style={styles.companyRow}>
159
+ <View style={styles.companyBadge}>
160
+ <Text style={styles.companyBadgeText}>
161
+ {`${connectionCount} ${tt('connections_label', 'connections') || 'conexoes'}`}
162
+ </Text>
163
+ </View>
164
+ </View>
165
+
166
+ <TouchableOpacity
167
+ activeOpacity={0.92}
168
+ onPress={openWhatsAppChannel}
169
+ style={[styles.channelCard, shadowStyle]}>
170
+ <View style={styles.channelTopRow}>
171
+ <View
172
+ style={[
173
+ styles.channelIconWrap,
174
+ { backgroundColor: withOpacity(brandColors.success, 0.12) },
175
+ ]}>
176
+ <Icon name="message-circle" size={20} color={brandColors.success} />
177
+ </View>
178
+ <View style={styles.channelStatusPill}>
179
+ <Text style={styles.channelStatusText}>
180
+ {connectionCount > 0
181
+ ? `${connectionCount} ${tt('connections_status', 'configured') || 'configurada(s)'}`
182
+ : tt('connections_status', 'readyToConfigure') || 'Pronta para configurar'}
183
+ </Text>
184
+ </View>
185
+ </View>
186
+ <Text style={styles.channelTitle}>
187
+ {tt('connections_label', 'whatsApp') || 'WhatsApp'}
188
+ </Text>
189
+ <Text style={styles.channelDescription}>
190
+ {tt('connections_text', 'whatsApp') ||
191
+ 'Conecte numeros, acompanhe status e gere o QR Code de autenticacao.'}
192
+ </Text>
193
+ <View style={styles.actionRow}>
194
+ <Text style={styles.actionText}>
195
+ {tt('connections_button', 'openChannel') || 'Abrir canal'}
196
+ </Text>
197
+ <Icon name="arrow-right" size={18} color={brandColors.primary} />
198
+ </View>
199
+ </TouchableOpacity>
200
+
201
+ <View style={{ flex: 1, marginTop: 12 }}>
202
+ <DefaultTable
203
+ storeName="connections"
204
+ requestParams={requestParams}
205
+ initialViewMode="cards"
206
+ forceCardsOnCompact
207
+ showToolbar
208
+ showRowActions={false}
209
+ showColumnFiltersButton
210
+ onRowPress={openWhatsAppChannel}
211
+ renderCard={renderConnectionCard}
212
+ searchProps={{
213
+ compact: true,
214
+ placeholder:
215
+ tt('connections_label', 'search') || 'Buscar conexao, telefone ou status',
216
+ searchKey: 'search',
217
+ storeName: 'connections',
218
+ }}
219
+ totalItemsLabel="connections"
220
+ visibleColumnsPreferenceKey="connections"
221
+ accentColor={brandColors.primary}
222
+ onAdd={openWhatsAppChannel}
223
+ add
224
+ addLabel={tt('connections_button', 'openChannel') || 'Abrir canal'}
225
+ />
226
+ </View>
227
+ </SafeAreaView>
228
+ );
229
+ }
@@ -0,0 +1,222 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { colors } from '@controleonline/../../src/styles/colors';
3
+ import { withOpacity } from '@controleonline/../../src/styles/branding';
4
+
5
+ export const createStyles = (palette = colors) =>
6
+ StyleSheet.create({
7
+ container: {
8
+ flex: 1,
9
+ backgroundColor: palette.background,
10
+ },
11
+ scroll: {
12
+ paddingHorizontal: 16,
13
+ paddingTop: 16,
14
+ paddingBottom: 32,
15
+ },
16
+ centerState: {
17
+ flex: 1,
18
+ justifyContent: 'center',
19
+ alignItems: 'center',
20
+ paddingHorizontal: 28,
21
+ },
22
+ centerStateTitle: {
23
+ marginTop: 16,
24
+ fontSize: 18,
25
+ fontWeight: '700',
26
+ color: palette.text,
27
+ textAlign: 'center',
28
+ },
29
+ centerStateText: {
30
+ marginTop: 8,
31
+ fontSize: 14,
32
+ lineHeight: 21,
33
+ color: palette.textSecondary,
34
+ textAlign: 'center',
35
+ },
36
+ heroCard: {
37
+ borderRadius: 28,
38
+ paddingHorizontal: 22,
39
+ paddingVertical: 24,
40
+ marginBottom: 18,
41
+ flexDirection: 'row',
42
+ alignItems: 'flex-start',
43
+ justifyContent: 'space-between',
44
+ },
45
+ heroCopy: {
46
+ flex: 1,
47
+ paddingRight: 16,
48
+ },
49
+ heroEyebrow: {
50
+ fontSize: 13,
51
+ fontWeight: '800',
52
+ letterSpacing: 1.4,
53
+ color: withOpacity(palette.white, 0.82),
54
+ marginBottom: 10,
55
+ },
56
+ heroTitle: {
57
+ fontSize: 22,
58
+ fontWeight: '800',
59
+ color: palette.white,
60
+ marginBottom: 8,
61
+ },
62
+ heroText: {
63
+ fontSize: 14,
64
+ lineHeight: 21,
65
+ color: withOpacity(palette.white, 0.88),
66
+ },
67
+ heroBadge: {
68
+ width: 54,
69
+ height: 54,
70
+ borderRadius: 18,
71
+ backgroundColor: palette.white,
72
+ alignItems: 'center',
73
+ justifyContent: 'center',
74
+ },
75
+ companyRow: {
76
+ flexDirection: 'row',
77
+ alignItems: 'flex-start',
78
+ justifyContent: 'space-between',
79
+ marginBottom: 18,
80
+ gap: 12,
81
+ },
82
+ sectionTitle: {
83
+ fontSize: 13,
84
+ fontWeight: '700',
85
+ color: palette.textSecondary,
86
+ marginBottom: 4,
87
+ },
88
+ companyName: {
89
+ fontSize: 21,
90
+ fontWeight: '800',
91
+ color: palette.text,
92
+ },
93
+ companyBadge: {
94
+ borderRadius: 999,
95
+ paddingHorizontal: 12,
96
+ paddingVertical: 8,
97
+ backgroundColor: withOpacity(palette.info, 0.12),
98
+ },
99
+ companyBadgeText: {
100
+ fontSize: 13,
101
+ fontWeight: '700',
102
+ color: palette.info,
103
+ },
104
+ channelCard: {
105
+ borderRadius: 28,
106
+ backgroundColor: palette.white,
107
+ padding: 18,
108
+ },
109
+ channelTopRow: {
110
+ flexDirection: 'row',
111
+ alignItems: 'center',
112
+ justifyContent: 'space-between',
113
+ marginBottom: 16,
114
+ gap: 12,
115
+ },
116
+ channelIconWrap: {
117
+ width: 56,
118
+ height: 56,
119
+ borderRadius: 18,
120
+ alignItems: 'center',
121
+ justifyContent: 'center',
122
+ },
123
+ channelStatusPill: {
124
+ borderRadius: 999,
125
+ backgroundColor: palette.background,
126
+ paddingHorizontal: 12,
127
+ paddingVertical: 8,
128
+ },
129
+ channelStatusText: {
130
+ fontSize: 13,
131
+ fontWeight: '700',
132
+ color: palette.textSecondary,
133
+ },
134
+ channelTitle: {
135
+ fontSize: 28,
136
+ fontWeight: '800',
137
+ color: palette.text,
138
+ marginBottom: 8,
139
+ },
140
+ channelDescription: {
141
+ fontSize: 14,
142
+ lineHeight: 21,
143
+ color: palette.textSecondary,
144
+ marginBottom: 18,
145
+ },
146
+ metaRow: {
147
+ flexDirection: 'row',
148
+ gap: 12,
149
+ marginBottom: 16,
150
+ },
151
+ metaItem: {
152
+ flex: 1,
153
+ borderRadius: 18,
154
+ backgroundColor: palette.background,
155
+ paddingHorizontal: 14,
156
+ paddingVertical: 14,
157
+ },
158
+ metaLabel: {
159
+ fontSize: 12,
160
+ fontWeight: '700',
161
+ color: palette.textSecondary,
162
+ marginBottom: 6,
163
+ },
164
+ metaValue: {
165
+ fontSize: 18,
166
+ fontWeight: '800',
167
+ color: palette.text,
168
+ },
169
+ previewList: {
170
+ gap: 12,
171
+ },
172
+ previewRow: {
173
+ flexDirection: 'row',
174
+ alignItems: 'center',
175
+ justifyContent: 'space-between',
176
+ borderRadius: 18,
177
+ backgroundColor: palette.background,
178
+ paddingHorizontal: 14,
179
+ paddingVertical: 14,
180
+ gap: 12,
181
+ },
182
+ previewName: {
183
+ fontSize: 15,
184
+ fontWeight: '700',
185
+ color: palette.text,
186
+ marginBottom: 4,
187
+ },
188
+ previewPhone: {
189
+ fontSize: 13,
190
+ color: palette.textSecondary,
191
+ },
192
+ previewStatus: {
193
+ fontSize: 13,
194
+ fontWeight: '700',
195
+ color: palette.info,
196
+ },
197
+ emptyInline: {
198
+ borderRadius: 18,
199
+ backgroundColor: palette.background,
200
+ paddingHorizontal: 14,
201
+ paddingVertical: 16,
202
+ },
203
+ emptyInlineText: {
204
+ fontSize: 14,
205
+ lineHeight: 21,
206
+ color: palette.textSecondary,
207
+ },
208
+ actionRow: {
209
+ marginTop: 18,
210
+ flexDirection: 'row',
211
+ alignItems: 'center',
212
+ justifyContent: 'space-between',
213
+ },
214
+ actionText: {
215
+ fontSize: 15,
216
+ fontWeight: '800',
217
+ color: palette.text,
218
+ },
219
+ });
220
+
221
+ const styles = createStyles(colors);
222
+ export default styles;