@controleonline/ui-common 1.2.71 → 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 (150) 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 +3 -2
  11. package/src/api/fetch.js +1 -1
  12. package/src/api/index.js +92 -22
  13. package/src/react/components/AddImportModal.js +94 -110
  14. package/src/react/components/BottomNavigationBar.config.js +211 -231
  15. package/src/react/components/CategoryForm.js +52 -35
  16. package/src/react/components/DefaultProvider.native.js +131 -95
  17. package/src/react/components/DefaultProvider.web.js +148 -74
  18. package/src/react/components/NetworkPrinterPreviewModal.js +249 -0
  19. package/src/react/components/NetworkPrinterPreviewModal.styles.js +159 -0
  20. package/src/react/components/RuntimeInfoFooter.js +100 -36
  21. package/src/react/components/RuntimeInfoFooter.styles.js +3 -0
  22. package/src/react/components/UnifiedPaymentBar.js +7 -4
  23. package/src/react/components/UserAvatar.js +160 -0
  24. package/src/react/components/integrations/IntegrationHero/index.js +48 -0
  25. package/src/react/components/integrations/IntegrationHero/styles.js +57 -0
  26. package/src/react/components/integrations/IntegrationTabs/index.js +89 -0
  27. package/src/react/components/integrations/IntegrationTabs/styles.js +44 -0
  28. package/src/react/config/deviceConfigBootstrap.js +5 -3
  29. package/src/react/pages/Connections.js +229 -0
  30. package/src/react/pages/Connections.styles.js +222 -0
  31. package/src/react/pages/DeviceDetailPage.js +2814 -0
  32. package/src/react/pages/DeviceDetailPage.styles.js +308 -0
  33. package/src/react/pages/Devices/currentDevice.js +194 -0
  34. package/src/react/pages/Devices/deviceTypes/DeviceGroupCard.js +257 -0
  35. package/src/react/pages/Devices/deviceTypes/all.js +23 -0
  36. package/src/react/pages/Devices/deviceTypes/device.js +25 -0
  37. package/src/react/pages/Devices/deviceTypes/deviceListHelpers.js +178 -0
  38. package/src/react/pages/Devices/deviceTypes/display.js +26 -0
  39. package/src/react/pages/Devices/deviceTypes/index.js +145 -0
  40. package/src/react/pages/Devices/deviceTypes/ipCamera.js +35 -0
  41. package/src/react/pages/Devices/deviceTypes/pdv.js +88 -0
  42. package/src/react/pages/Devices/deviceTypes/printer.js +37 -0
  43. package/src/react/pages/Devices/deviceTypes/shared.js +453 -0
  44. package/src/react/pages/Devices/deviceTypes/useDeviceNetworkConnectivity.js +145 -0
  45. package/src/react/pages/Devices.js +183 -0
  46. package/src/react/pages/Devices.styles.js +353 -0
  47. package/src/react/pages/Food99IntegrationPage/components/Food99CatalogTab.js +219 -0
  48. package/src/react/pages/Food99IntegrationPage/components/Food99OverviewTab.js +134 -0
  49. package/src/react/pages/Food99IntegrationPage/components/Food99PreviewModal.js +173 -0
  50. package/src/react/pages/Food99IntegrationPage/components/Food99ProductCard.js +1 -0
  51. package/src/react/pages/Food99IntegrationPage/components/Food99QuickWalletModal.js +181 -0
  52. package/src/react/pages/Food99IntegrationPage/components/Food99SettingsTab.js +306 -0
  53. package/src/react/pages/Food99IntegrationPage/components/Food99StoreTab.js +417 -0
  54. package/src/react/pages/Food99IntegrationPage/index.js +1530 -0
  55. package/src/react/pages/Food99IntegrationPage/styles.js +792 -0
  56. package/src/react/pages/Food99IntegrationPage/utils.js +155 -0
  57. package/src/react/pages/Food99IntegrationPage.js +1 -0
  58. package/src/react/pages/Food99IntegrationPage.styles.js +1 -0
  59. package/src/react/pages/GenericLogPage.js +42 -167
  60. package/src/react/pages/IFoodIntegrationPage/components/IFoodCatalogTab.js +8 -0
  61. package/src/react/pages/IFoodIntegrationPage/components/IFoodOperationsTab.js +420 -0
  62. package/src/react/pages/IFoodIntegrationPage/components/IFoodOverviewTab.js +118 -0
  63. package/src/react/pages/IFoodIntegrationPage/components/IFoodPreviewModal.js +103 -0
  64. package/src/react/pages/IFoodIntegrationPage/components/IFoodProductCard.js +1 -0
  65. package/src/react/pages/IFoodIntegrationPage/components/IFoodStoreTab.js +150 -0
  66. package/src/react/pages/IFoodIntegrationPage/index.js +1298 -0
  67. package/src/react/pages/IFoodIntegrationPage/styles.js +764 -0
  68. package/src/react/pages/IFoodIntegrationPage/utils.js +91 -0
  69. package/src/react/pages/IFoodIntegrationPage.js +1 -0
  70. package/src/react/pages/IFoodIntegrationPage.styles.js +1 -0
  71. package/src/react/pages/Imports.js +214 -166
  72. package/src/react/pages/IntegrationConfigPage.js +744 -0
  73. package/src/react/pages/IntegrationConfigPage.styles.js +269 -0
  74. package/src/react/pages/Integrations.js +299 -0
  75. package/src/react/pages/Integrations.styles.js +130 -0
  76. package/src/react/pages/ManagerOrderNotificationsPage.js +642 -0
  77. package/src/react/pages/MarketplaceIntegrationPage/components/MarketplaceProductCard.js +205 -0
  78. package/src/react/pages/MarketplaceIntegrationPage/components/RemoteMarketplaceMenuTab.js +335 -0
  79. package/src/react/pages/MarketplaceIntegrationPage/index.js +60 -0
  80. package/src/react/pages/MarketplaceIntegrationPage/utils.js +137 -0
  81. package/src/react/pages/MercadoLivreIntegrationPage.js +538 -0
  82. package/src/react/pages/PrinterDeviceDetailPage.js +1916 -0
  83. package/src/react/pages/PrinterDeviceDetailPage.styles.js +223 -0
  84. package/src/react/pages/PrinterDeviceFormPage.js +521 -0
  85. package/src/react/pages/PrinterDeviceFormPage.styles.js +159 -0
  86. package/src/react/pages/WhatsAppConnectionPage.js +459 -0
  87. package/src/react/pages/WhatsAppConnectionPage.styles.js +284 -0
  88. package/src/react/pages/integrationsCatalog.js +399 -0
  89. package/src/react/services/Cielo/Cielo.js +193 -193
  90. package/src/react/utils/categoryContexts.js +46 -0
  91. package/src/react/utils/entityDisplay.js +222 -217
  92. package/src/react/utils/fileUrl.js +39 -20
  93. package/src/react/utils/integrationPage.js +16 -0
  94. package/src/react/utils/runtimeFooter.js +40 -22
  95. package/src/react/utils/runtimeLanguage.js +50 -9
  96. package/src/react/utils/runtimeMenu.js +191 -131
  97. package/src/react/utils/runtimeZoom.js +43 -0
  98. package/src/react/utils/shopConfig.js +27 -15
  99. package/src/react/utils/shopFranchises.js +126 -1
  100. package/src/react/utils/storeColumns.js +23 -4
  101. package/src/react/utils/userAvatar.js +104 -0
  102. package/src/store/categories/index.js +48 -20
  103. package/src/store/device_config/index.js +62 -21
  104. package/src/store/entity_log/index.js +22 -1
  105. package/src/store/imports/customActions.js +51 -0
  106. package/src/store/imports/index.js +89 -88
  107. package/src/store/status/index.js +11 -11
  108. package/src/store/themes/index.js +22 -0
  109. package/src/store/timezones/index.js +3 -1
  110. package/src/tests/browser/manager/connections.spec.js +143 -0
  111. package/src/tests/browser/manager/devices-current.spec.js +343 -0
  112. package/src/tests/browser/manager/mercado-livre-integration.spec.js +268 -0
  113. package/src/tests/react/api/deviceHeaders.test.js +114 -0
  114. package/src/tests/react/components/AddImportModal.test.js +166 -0
  115. package/src/tests/react/components/BottomNavigationBar.config.test.js +20 -0
  116. package/src/tests/react/components/UserAvatar.test.js +52 -0
  117. package/src/tests/react/pages/DevicePickerStyles.test.js +47 -0
  118. package/src/tests/react/pages/Devices/deviceListHelpers.test.js +39 -0
  119. package/src/tests/react/pages/DevicesCurrentDevice.test.js +198 -0
  120. package/src/tests/react/pages/connectionsHelpers.test.js +34 -0
  121. package/src/tests/react/pages/mercadoLivreIntegrationPageContract.test.mjs +16 -0
  122. package/src/tests/react/services/Cielo.test.js +190 -190
  123. package/src/tests/react/store/categoriesOrdering.test.js +26 -0
  124. package/src/tests/react/utils/appDomain.test.js +62 -0
  125. package/src/tests/react/utils/entityDisplay.test.js +2 -2
  126. package/src/tests/react/utils/fileUrl.test.js +3 -3
  127. package/src/tests/react/utils/formatter.test.mjs +33 -0
  128. package/src/tests/react/utils/runtimeFooter.test.js +29 -4
  129. package/src/tests/react/utils/runtimeLanguage.test.js +79 -9
  130. package/src/tests/react/utils/runtimeMenu.test.js +198 -134
  131. package/src/tests/react/utils/runtimeZoom.test.js +38 -0
  132. package/src/tests/react/utils/shopConfig.test.js +10 -9
  133. package/src/tests/react/utils/shopFranchises.test.js +43 -0
  134. package/src/tests/react/utils/translate.discovery.test.mjs +170 -0
  135. package/src/tests/react/utils/translate.test.helpers.mjs +152 -0
  136. package/src/tests/react/utils/translate.test.mjs +141 -349
  137. package/src/tests/react/utils/userAvatar.test.js +63 -0
  138. package/src/utils/appDomain.js +15 -4
  139. package/src/utils/formatter.js +104 -16
  140. package/src/utils/integrationConfigs.js +80 -80
  141. package/src/utils/translate.js +222 -438
  142. package/src/utils/translateDiscovery.js +96 -0
  143. package/src/utils/translateNormalize.js +33 -0
  144. package/src/utils/translatePending.js +92 -0
  145. package/src/utils/translateStorage.js +142 -0
  146. package/.github/agents/developer.agent.md +0 -30
  147. package/.github/agents/devops.agent.md +0 -30
  148. package/.github/agents/qa.agent.md +0 -30
  149. package/.github/agents/security.agent.md +0 -30
  150. package/.github/workflows/npmjs.yml +0 -35
@@ -0,0 +1,89 @@
1
+ import React from 'react';
2
+ import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
3
+
4
+ import { colors } from '@controleonline/../../src/styles/colors';
5
+
6
+ import createStyles from './styles';
7
+
8
+ // Abas horizontais simples para reduzir densidade visual das páginas.
9
+ export default function IntegrationTabs({ tabs, activeKey, onChange, accentColor, palette = colors }) {
10
+ const styles = createStyles(palette);
11
+ const resolvedPrimaryBackground =
12
+ palette.buttonBackground ||
13
+ accentColor ||
14
+ palette.primary;
15
+ const resolvedPrimaryBorder =
16
+ palette.buttonBorder ||
17
+ resolvedPrimaryBackground;
18
+ const resolvedPrimaryText =
19
+ palette.buttonText ||
20
+ palette.white;
21
+ const resolvedSecondaryBackground =
22
+ palette.buttonBackgroundSecondary ||
23
+ palette.white;
24
+ const resolvedSecondaryBorder =
25
+ palette.buttonBorderSecondary ||
26
+ palette.border;
27
+ const resolvedSecondaryText =
28
+ palette.buttonTextSecondary ||
29
+ palette.textSecondary;
30
+ const resolvedSecondaryIcon =
31
+ palette.buttonIconSecondary ||
32
+ resolvedSecondaryText;
33
+
34
+ return (
35
+ <ScrollView
36
+ horizontal
37
+ showsHorizontalScrollIndicator={false}
38
+ contentContainerStyle={styles.scrollContent}>
39
+ {tabs.map(tab => {
40
+ const active = tab.key === activeKey;
41
+ const tabColors = active
42
+ ? {
43
+ backgroundColor: resolvedPrimaryBackground,
44
+ borderColor: resolvedPrimaryBorder,
45
+ textColor: resolvedPrimaryText,
46
+ badgeBackground: palette.buttonPressedBackground || resolvedPrimaryBorder,
47
+ badgeText: resolvedPrimaryText,
48
+ }
49
+ : {
50
+ backgroundColor: resolvedSecondaryBackground,
51
+ borderColor: resolvedSecondaryBorder,
52
+ textColor: resolvedSecondaryText,
53
+ badgeBackground: palette.badgeBackground || resolvedSecondaryBackground,
54
+ badgeText: resolvedSecondaryIcon,
55
+ };
56
+
57
+ return (
58
+ <TouchableOpacity
59
+ key={tab.key}
60
+ activeOpacity={0.85}
61
+ style={[
62
+ styles.tabButton,
63
+ {
64
+ backgroundColor: tabColors.backgroundColor,
65
+ borderColor: tabColors.borderColor,
66
+ },
67
+ ]}
68
+ onPress={() => onChange(tab.key)}>
69
+ <Text style={[styles.tabLabel, { color: tabColors.textColor }]}>
70
+ {tab.label}
71
+ </Text>
72
+
73
+ {tab.badge !== undefined && tab.badge !== null && tab.badge !== '' && (
74
+ <View
75
+ style={[
76
+ styles.tabBadge,
77
+ { backgroundColor: tabColors.badgeBackground },
78
+ ]}>
79
+ <Text style={[styles.tabBadgeText, { color: tabColors.badgeText }]}>
80
+ {tab.badge}
81
+ </Text>
82
+ </View>
83
+ )}
84
+ </TouchableOpacity>
85
+ );
86
+ })}
87
+ </ScrollView>
88
+ );
89
+ }
@@ -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;
@@ -27,6 +27,7 @@ export const DISPLAY_SIZE_MIN = 1;
27
27
  export const DISPLAY_SIZE_MAX = 10;
28
28
  export const DISPLAY_SIZE_DEFAULT = 5;
29
29
  export const POS_OPERATION_MODE_CONFIG_KEY = 'pos-operation-mode';
30
+ export const POS_PRODUCT_SHOWCASE_CONFIG_KEY = 'pos-product-showcase-id';
30
31
  export const DEVICE_ANDROID_KIOSK_ENABLED_CONFIG_KEY =
31
32
  'android-kiosk-enabled';
32
33
  export const DEVICE_ANDROID_LAUNCHER_ENABLED_CONFIG_KEY =
@@ -105,9 +106,10 @@ export const DEFAULT_DEVICE_CONFIGS = {
105
106
  [POS_CHECK_ORDER_MANAGEMENT_MODE_CONFIG_KEY]:
106
107
  POS_CHECK_ORDER_MANAGEMENT_MODE_MANAGE,
107
108
  [POS_OPERATION_MODE_CONFIG_KEY]: POS_OPERATION_MODE_DEFAULT,
108
- [POS_AUTO_PRINT_ENABLED_CONFIG_KEY]: '0',
109
- [POS_CASH_MANAGEMENT_MODE_CONFIG_KEY]: POS_CASH_MANAGEMENT_MODE_DEFAULT,
110
- };
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
+ };
111
113
 
112
114
  export const isTruthyValue = value =>
113
115
  value === true || value === '1' || value === 1 || value === 'true';
@@ -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;