@controleonline/ui-common 1.2.71 → 1.2.76

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 +164 -104
  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 +2865 -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,219 @@
1
+ import React from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ Text,
5
+ TextInput,
6
+ TouchableOpacity,
7
+ View,
8
+ } from 'react-native';
9
+ import Icon from 'react-native-vector-icons/Feather';
10
+ import { colors } from '@controleonline/../../src/styles/colors';
11
+
12
+ import {withOpacity} from '@controleonline/../../src/styles/branding';
13
+ import CompactFilterSelector from '@controleonline/ui-default/src/react/components/filters/CompactFilterSelector';
14
+
15
+ import {
16
+ normalizeMarketplaceCatalogProduct,
17
+ normalizeMarketplaceCatalogTabProps,
18
+ } from '../../MarketplaceIntegrationPage/utils';
19
+ import MarketplaceProductCard from '../../MarketplaceIntegrationPage/components/MarketplaceProductCard';
20
+ import styles from '../styles';
21
+ import {
22
+ filterTabs,
23
+ MINIMUM_REQUIRED_ITEMS as DEFAULT_MINIMUM_REQUIRED_ITEMS,
24
+ } from '../utils';
25
+
26
+ const resolveButtonPalette = (palette, accentColor) => {
27
+ const primaryBackground =
28
+ palette.buttonBackground || accentColor || palette.primary;
29
+ const primaryBorder = palette.buttonBorder || primaryBackground;
30
+ const primaryText = palette.buttonText || palette.white;
31
+ const primaryIcon = palette.buttonIcon || primaryText;
32
+ const disabledBackground =
33
+ palette.buttonDisabledBackground || palette.border;
34
+ const disabledBorder = palette.buttonBorderSecondary || palette.border;
35
+ const disabledText = palette.buttonDisabledText || palette.textSecondary;
36
+
37
+ return {
38
+ primaryBackground,
39
+ primaryBorder,
40
+ primaryText,
41
+ primaryIcon,
42
+ disabledBackground,
43
+ disabledBorder,
44
+ disabledText,
45
+ };
46
+ };
47
+
48
+ // Aba separada para seleção e publicação do catálogo 99Food.
49
+ export default function Food99CatalogTab(props) {
50
+ const {
51
+ shadowStyle,
52
+ accentColor,
53
+ providerKey,
54
+ selectionSummaryTone,
55
+ search,
56
+ setSearch,
57
+ filterKey,
58
+ setFilterKey,
59
+ tabCounts,
60
+ productsResponse,
61
+ selectedEligibleProducts,
62
+ previewLoading,
63
+ onPreview,
64
+ filteredProducts,
65
+ selectedProductSet,
66
+ onToggleProduct,
67
+ onMarkCardPressHandled,
68
+ minimumRequiredItems,
69
+ palette = colors,
70
+ } = normalizeMarketplaceCatalogTabProps(props, {
71
+ providerKey: props?.providerKey || '99food',
72
+ defaultMinimumRequiredItems: DEFAULT_MINIMUM_REQUIRED_ITEMS,
73
+ });
74
+
75
+ const normalizedProducts = React.useMemo(
76
+ () =>
77
+ filteredProducts.map(product =>
78
+ normalizeMarketplaceCatalogProduct(product, providerKey),
79
+ ),
80
+ [filteredProducts, providerKey],
81
+ );
82
+
83
+ const filterOptions = filterTabs.map(tab => ({
84
+ key: tab.key,
85
+ label: `${tab.label} (${tabCounts[tab.key] || 0})`,
86
+ }));
87
+ const selectedFilterLabel =
88
+ filterOptions.find(option => option.key === filterKey)?.label ||
89
+ filterOptions[0]?.label ||
90
+ 'Todos';
91
+ const buttonPalette = resolveButtonPalette(palette, accentColor);
92
+ const previewDisabled =
93
+ previewLoading || selectedEligibleProducts.length < minimumRequiredItems;
94
+
95
+ return (
96
+ <View style={[styles.panel, shadowStyle]}>
97
+ <View style={styles.panelHeader}>
98
+ <View>
99
+ <Text style={styles.panelTitle}>Selecao de produtos</Text>
100
+ <Text style={styles.panelSubtitle}>
101
+ Escolha pelo menos {minimumRequiredItems} produtos elegiveis para o
102
+ catalogo.
103
+ </Text>
104
+ </View>
105
+ <View
106
+ style={[
107
+ styles.selectionBadge,
108
+ {backgroundColor: withOpacity(selectionSummaryTone, 0.12)},
109
+ ]}>
110
+ <Text
111
+ style={[styles.selectionBadgeText, {color: selectionSummaryTone}]}>
112
+ {selectedEligibleProducts.length}/{minimumRequiredItems}
113
+ </Text>
114
+ </View>
115
+ </View>
116
+
117
+ <View style={styles.searchBox}>
118
+ <Icon name="search" size={16} color={palette.textSecondary} />
119
+ <TextInput
120
+ value={search}
121
+ onChangeText={setSearch}
122
+ placeholder="Buscar produto, categoria ou codigo"
123
+ placeholderTextColor={palette.textSecondary}
124
+ style={styles.searchInput}
125
+ />
126
+ </View>
127
+
128
+ <CompactFilterSelector
129
+ icon="filter"
130
+ label={selectedFilterLabel}
131
+ title="Produtos do catalogo"
132
+ accentColor={accentColor}
133
+ active={filterKey !== filterTabs[0]?.key}
134
+ options={filterOptions}
135
+ selectedKey={filterKey}
136
+ onSelect={optionKey => {
137
+ setFilterKey(optionKey);
138
+ return true;
139
+ }}
140
+ />
141
+
142
+ <View style={styles.selectionSummaryRow}>
143
+ <Text style={styles.selectionSummaryText}>
144
+ {productsResponse?.eligible_product_count || 0} produtos aptos no
145
+ cadastro atual
146
+ </Text>
147
+ <TouchableOpacity
148
+ style={[
149
+ styles.previewButton,
150
+ {
151
+ borderWidth: 1,
152
+ borderColor: previewDisabled
153
+ ? buttonPalette.disabledBorder
154
+ : buttonPalette.primaryBorder,
155
+ },
156
+ {
157
+ backgroundColor:
158
+ previewDisabled
159
+ ? buttonPalette.disabledBackground
160
+ : buttonPalette.primaryBackground,
161
+ },
162
+ ]}
163
+ onPress={onPreview}
164
+ disabled={previewDisabled}>
165
+ {previewLoading ? (
166
+ <ActivityIndicator
167
+ size="small"
168
+ color={buttonPalette.disabledText}
169
+ />
170
+ ) : (
171
+ <>
172
+ <Icon
173
+ name="eye"
174
+ size={15}
175
+ color={
176
+ previewDisabled
177
+ ? buttonPalette.disabledText
178
+ : buttonPalette.primaryIcon
179
+ }
180
+ />
181
+ <Text
182
+ style={[
183
+ styles.previewButtonText,
184
+ {
185
+ color: previewDisabled
186
+ ? buttonPalette.disabledText
187
+ : buttonPalette.primaryText,
188
+ },
189
+ ]}>
190
+ Pre-visualizar menu
191
+ </Text>
192
+ </>
193
+ )}
194
+ </TouchableOpacity>
195
+ </View>
196
+
197
+ {normalizedProducts.length > 0 ? (
198
+ <View style={styles.productsList}>
199
+ {normalizedProducts.map(product => (
200
+ <MarketplaceProductCard
201
+ key={product.id}
202
+ product={product}
203
+ accentColor={accentColor}
204
+ selected={selectedProductSet.has(String(product.id))}
205
+ onPress={onToggleProduct}
206
+ onMarkCardPressHandled={onMarkCardPressHandled}
207
+ />
208
+ ))}
209
+ </View>
210
+ ) : (
211
+ <View style={styles.emptyProducts}>
212
+ <Text style={styles.emptyProductsText}>
213
+ Nenhum produto encontrado para este filtro.
214
+ </Text>
215
+ </View>
216
+ )}
217
+ </View>
218
+ );
219
+ }
@@ -0,0 +1,134 @@
1
+ import React from 'react';
2
+ import { Text, View } from 'react-native';
3
+ import Icon from 'react-native-vector-icons/Feather';
4
+ import { colors } from '@controleonline/../../src/styles/colors';
5
+
6
+ import { withOpacity } from '@controleonline/../../src/styles/branding';
7
+
8
+ import styles from '../styles';
9
+
10
+ // Aba inicial com leitura rápida da integração 99Food.
11
+ export default function Food99OverviewTab({
12
+ shadowStyle,
13
+ connected,
14
+ needsReconnect,
15
+ summaryCards,
16
+ lastMenuPublishState,
17
+ publicationTone,
18
+ publishStateLabel,
19
+ lastMenuTaskMessage,
20
+ lastErrorMessage,
21
+ palette = colors,
22
+ }) {
23
+ const reconnectRequired = Boolean(needsReconnect);
24
+ const pillBackgroundColor = reconnectRequired
25
+ ? withOpacity(palette.warning, 0.24)
26
+ : connected
27
+ ? withOpacity(palette.success, 0.18)
28
+ : withOpacity(palette.warning, 0.24);
29
+ const pillTextColor = reconnectRequired
30
+ ? palette.warning
31
+ : connected
32
+ ? palette.success
33
+ : palette.warning;
34
+ const pillLabel = reconnectRequired
35
+ ? 'Reconexão necessária'
36
+ : connected
37
+ ? '99Food conectado'
38
+ : 'Integração pendente';
39
+
40
+ return (
41
+ <>
42
+ <View style={styles.companyRow}>
43
+ <View
44
+ style={[
45
+ styles.statusPill,
46
+ { backgroundColor: pillBackgroundColor },
47
+ ]}>
48
+ <Text
49
+ style={[
50
+ styles.statusPillText,
51
+ { color: pillTextColor },
52
+ ]}>
53
+ {pillLabel}
54
+ </Text>
55
+ </View>
56
+ </View>
57
+
58
+ <View style={styles.summaryGrid}>
59
+ {summaryCards.map(card => (
60
+ <View key={card.key} style={[styles.summaryCard, shadowStyle]}>
61
+ <View
62
+ style={[
63
+ styles.summaryIcon,
64
+ { backgroundColor: palette.cardIconBackground || palette.iconBackground },
65
+ ]}
66
+ >
67
+ <Icon
68
+ name={card.icon}
69
+ size={18}
70
+ color={palette.cardIconColor || palette.iconColor || palette.text}
71
+ />
72
+ </View>
73
+ <Text style={styles.summaryValue}>{card.value}</Text>
74
+ <Text style={styles.summaryLabel}>{card.label}</Text>
75
+ </View>
76
+ ))}
77
+ </View>
78
+
79
+ <View style={[styles.panel, shadowStyle]}>
80
+ <View style={styles.panelHeader}>
81
+ <View>
82
+ <Text style={styles.panelTitle}>Leitura rapida</Text>
83
+ <Text style={styles.panelSubtitle}>
84
+ Use as abas seguintes para trabalhar conexao, configuracoes e catalogo separadamente.
85
+ </Text>
86
+ </View>
87
+ </View>
88
+
89
+ {!!lastMenuPublishState && (
90
+ <View style={[styles.infoBanner, { backgroundColor: withOpacity(publicationTone, 0.12) }]}>
91
+ <Icon
92
+ name={
93
+ lastMenuPublishState === 'failed'
94
+ ? 'alert-triangle'
95
+ : lastMenuPublishState === 'published'
96
+ ? 'check-circle'
97
+ : 'clock'
98
+ }
99
+ size={14}
100
+ color={publicationTone}
101
+ />
102
+ <Text style={[styles.infoBannerText, { color: publicationTone }]}>
103
+ {publishStateLabel}
104
+ {lastMenuTaskMessage ? ` • ${lastMenuTaskMessage}` : ''}
105
+ </Text>
106
+ </View>
107
+ )}
108
+
109
+ {!!lastErrorMessage && lastMenuPublishState !== 'failed' && (
110
+ <View style={styles.errorBanner}>
111
+ <Icon name="alert-circle" size={14} color={palette.error} />
112
+ <Text style={styles.errorBannerText}>{lastErrorMessage}</Text>
113
+ </View>
114
+ )}
115
+
116
+ {reconnectRequired && !lastMenuPublishState && !lastErrorMessage ? (
117
+ <View style={[styles.infoBanner, { backgroundColor: withOpacity(palette.warning, 0.24) }]}>
118
+ <Icon name="alert-triangle" size={14} color={palette.warning} />
119
+ <Text style={[styles.infoBannerText, { color: palette.warning }]}>
120
+ A loja está vinculada localmente, mas a conexão remota caiu. Use a aba Loja para reconectar e retomar os webhooks.
121
+ </Text>
122
+ </View>
123
+ ) : !lastMenuPublishState && !lastErrorMessage ? (
124
+ <View style={[styles.infoBanner, { backgroundColor: withOpacity(palette.info, 0.12) }]}>
125
+ <Icon name="info" size={14} color={palette.info} />
126
+ <Text style={[styles.infoBannerText, { color: palette.info }]}>
127
+ A aba de loja concentra status remoto e ações de conexão. A aba de cardápio cuida somente dos produtos.
128
+ </Text>
129
+ </View>
130
+ ) : null}
131
+ </View>
132
+ </>
133
+ );
134
+ }
@@ -0,0 +1,173 @@
1
+ import React from 'react';
2
+ import { ActivityIndicator, ScrollView, Text, TouchableOpacity, View } from 'react-native';
3
+ import Icon from 'react-native-vector-icons/Feather';
4
+ import { colors } from '@controleonline/../../src/styles/colors';
5
+
6
+ import AnimatedModal from '@controleonline/ui-common/src/react/components/AnimatedModal';
7
+
8
+ import styles from '../styles';
9
+ import { countCollection } from '@controleonline/ui-common/src/react/utils/integrationPage';
10
+
11
+ const resolveButtonPalette = (palette, accentColor) => {
12
+ const primaryBackground =
13
+ palette.buttonBackground || accentColor || palette.primary;
14
+ const primaryBorder = palette.buttonBorder || primaryBackground;
15
+ const primaryText = palette.buttonText || palette.white;
16
+ const primaryIcon = palette.buttonIcon || primaryText;
17
+ const secondaryBackground =
18
+ palette.buttonBackgroundSecondary || palette.white;
19
+ const secondaryBorder = palette.buttonBorderSecondary || palette.border;
20
+ const secondaryText = palette.buttonTextSecondary || palette.textSecondary;
21
+ const secondaryIcon = palette.buttonIconSecondary || secondaryText;
22
+ const disabledBackground =
23
+ palette.buttonDisabledBackground || secondaryBorder;
24
+ const disabledBorder = palette.buttonBorderSecondary || secondaryBorder;
25
+ const disabledText = palette.buttonDisabledText || secondaryText;
26
+
27
+ return {
28
+ primaryBackground,
29
+ primaryBorder,
30
+ primaryText,
31
+ primaryIcon,
32
+ secondaryBackground,
33
+ secondaryBorder,
34
+ secondaryText,
35
+ secondaryIcon,
36
+ disabledBackground,
37
+ disabledBorder,
38
+ disabledText,
39
+ };
40
+ };
41
+
42
+ // Modal de pré-visualização do menu antes do envio à 99Food.
43
+ export default function Food99PreviewModal({
44
+ visible,
45
+ previewData,
46
+ selectedEligibleProducts,
47
+ uploading,
48
+ accentColor,
49
+ palette = colors,
50
+ onClose,
51
+ onUpload,
52
+ }) {
53
+ const buttonPalette = resolveButtonPalette(palette, accentColor);
54
+
55
+ return (
56
+ <AnimatedModal visible={visible} onRequestClose={onClose}>
57
+ <View style={styles.modalShell}>
58
+ <View style={styles.modalCard}>
59
+ <View style={styles.modalHeader}>
60
+ <View>
61
+ <Text style={styles.modalTitle}>Pre-visualizacao do menu</Text>
62
+ <Text style={styles.modalSubtitle}>
63
+ {previewData?.eligible_product_count || selectedEligibleProducts.length} produtos prontos para upload
64
+ </Text>
65
+ </View>
66
+ <TouchableOpacity onPress={onClose} style={styles.modalCloseButton}>
67
+ <Icon name="x" size={18} color={palette.textSecondary} />
68
+ </TouchableOpacity>
69
+ </View>
70
+
71
+ <ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.modalContent}>
72
+ <View style={styles.modalSummaryGrid}>
73
+ <View style={styles.modalSummaryCard}>
74
+ <Text style={styles.modalSummaryValue}>{countCollection(previewData?.payload?.menus)}</Text>
75
+ <Text style={styles.modalSummaryLabel}>Menus</Text>
76
+ </View>
77
+ <View style={styles.modalSummaryCard}>
78
+ <Text style={styles.modalSummaryValue}>{countCollection(previewData?.payload?.categories)}</Text>
79
+ <Text style={styles.modalSummaryLabel}>Categorias</Text>
80
+ </View>
81
+ <View style={styles.modalSummaryCard}>
82
+ <Text style={styles.modalSummaryValue}>{countCollection(previewData?.payload?.items)}</Text>
83
+ <Text style={styles.modalSummaryLabel}>Itens</Text>
84
+ </View>
85
+ </View>
86
+
87
+ <View style={styles.previewSection}>
88
+ <Text style={styles.previewSectionTitle}>Categorias</Text>
89
+ {(previewData?.payload?.categories || []).map(category => (
90
+ <View key={category.app_category_id} style={styles.previewLine}>
91
+ <Text style={styles.previewLineTitle}>{category.category_name}</Text>
92
+ <Text style={styles.previewLineMeta}>
93
+ {countCollection(category.app_item_ids)} item(ns)
94
+ </Text>
95
+ </View>
96
+ ))}
97
+ </View>
98
+
99
+ <View style={styles.previewSection}>
100
+ <Text style={styles.previewSectionTitle}>Itens selecionados</Text>
101
+ {selectedEligibleProducts.map(product => (
102
+ <View key={product.id} style={styles.previewLine}>
103
+ <Text style={styles.previewLineTitle}>{product.name}</Text>
104
+ <Text style={styles.previewLineMeta}>
105
+ {product.category?.name || 'Sem categoria'} • R$ {Number(product.price || 0).toFixed(2)}
106
+ </Text>
107
+ </View>
108
+ ))}
109
+ </View>
110
+ </ScrollView>
111
+
112
+ <View style={styles.modalActions}>
113
+ <TouchableOpacity
114
+ style={[
115
+ styles.secondaryButton,
116
+ {
117
+ borderColor: buttonPalette.secondaryBorder,
118
+ backgroundColor: buttonPalette.secondaryBackground,
119
+ },
120
+ ]}
121
+ onPress={onClose}>
122
+ <Text
123
+ style={[
124
+ styles.secondaryButtonText,
125
+ { color: buttonPalette.secondaryText },
126
+ ]}>
127
+ Fechar
128
+ </Text>
129
+ </TouchableOpacity>
130
+ <TouchableOpacity
131
+ style={[
132
+ styles.primaryButton,
133
+ styles.modalPrimaryButton,
134
+ {
135
+ borderWidth: 1,
136
+ borderColor: uploading
137
+ ? buttonPalette.disabledBorder
138
+ : buttonPalette.primaryBorder,
139
+ backgroundColor: uploading
140
+ ? buttonPalette.disabledBackground
141
+ : buttonPalette.primaryBackground,
142
+ },
143
+ ]}
144
+ onPress={onUpload}
145
+ disabled={uploading}>
146
+ {uploading ? (
147
+ <ActivityIndicator
148
+ size="small"
149
+ color={buttonPalette.disabledText}
150
+ />
151
+ ) : (
152
+ <>
153
+ <Icon
154
+ name="upload-cloud"
155
+ size={16}
156
+ color={buttonPalette.primaryIcon}
157
+ />
158
+ <Text
159
+ style={[
160
+ styles.primaryButtonText,
161
+ { color: buttonPalette.primaryText },
162
+ ]}>
163
+ Publicar menu
164
+ </Text>
165
+ </>
166
+ )}
167
+ </TouchableOpacity>
168
+ </View>
169
+ </View>
170
+ </View>
171
+ </AnimatedModal>
172
+ );
173
+ }
@@ -0,0 +1 @@
1
+ export {default} from '../../MarketplaceIntegrationPage/components/MarketplaceProductCard';