@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,145 @@
1
+ import {useEffect, useState} from 'react';
2
+ import {
3
+ checkNetworkPrinterConnection,
4
+ isNetworkPrinterRuntimeSupported,
5
+ } from '@controleonline/ui-common/src/react/services/NetworkPrinterService';
6
+ import {
7
+ getDeviceConfigType,
8
+ isManagedNetworkDeviceType,
9
+ getPrinterHost,
10
+ normalizePrinterPort,
11
+ DEFAULT_NETWORK_PRINTER_PORT,
12
+ NETWORK_PRINTER_PORT_CONFIG_KEY,
13
+ } from '@controleonline/ui-common/src/react/utils/printerDevices';
14
+ import {normalizeDeviceId} from '@controleonline/ui-common/src/react/utils/paymentDevices';
15
+ import {parseConfigsObject} from '@controleonline/ui-common/src/react/config/deviceConfigBootstrap';
16
+
17
+ export function useDeviceNetworkConnectivity(deviceConfigs) {
18
+ const [networkConnectivityByDevice, setNetworkConnectivityByDevice] = useState({});
19
+
20
+ useEffect(() => {
21
+ const networkDeviceConfigs = deviceConfigs.filter(deviceConfig =>
22
+ isManagedNetworkDeviceType(getDeviceConfigType(deviceConfig)),
23
+ );
24
+
25
+ if (networkDeviceConfigs.length === 0) {
26
+ setNetworkConnectivityByDevice({});
27
+ return;
28
+ }
29
+
30
+ if (!isNetworkPrinterRuntimeSupported) {
31
+ setNetworkConnectivityByDevice(
32
+ networkDeviceConfigs.reduce((acc, deviceConfig) => {
33
+ const deviceKey = normalizeDeviceId(
34
+ deviceConfig?.device?.device || deviceConfig?.device?.id || deviceConfig?.id,
35
+ );
36
+
37
+ if (deviceKey) {
38
+ acc[deviceKey] = {status: 'unsupported'};
39
+ }
40
+
41
+ return acc;
42
+ }, {}),
43
+ );
44
+ return;
45
+ }
46
+
47
+ let cancelled = false;
48
+
49
+ setNetworkConnectivityByDevice(previousState => {
50
+ const nextState = {...previousState};
51
+
52
+ networkDeviceConfigs.forEach(deviceConfig => {
53
+ const deviceKey = normalizeDeviceId(
54
+ deviceConfig?.device?.device || deviceConfig?.device?.id || deviceConfig?.id,
55
+ );
56
+
57
+ if (deviceKey) {
58
+ nextState[deviceKey] = {
59
+ ...(nextState[deviceKey] || {}),
60
+ status: 'checking',
61
+ };
62
+ }
63
+ });
64
+
65
+ return nextState;
66
+ });
67
+
68
+ Promise.all(
69
+ networkDeviceConfigs.map(async deviceConfig => {
70
+ const deviceKey = normalizeDeviceId(
71
+ deviceConfig?.device?.device || deviceConfig?.device?.id || deviceConfig?.id,
72
+ );
73
+ const parsedConfigs = parseConfigsObject(deviceConfig?.configs);
74
+ const host = getPrinterHost({
75
+ ...(deviceConfig?.device || {}),
76
+ configs: parsedConfigs,
77
+ });
78
+ const port = normalizePrinterPort(
79
+ parsedConfigs?.[NETWORK_PRINTER_PORT_CONFIG_KEY] ||
80
+ DEFAULT_NETWORK_PRINTER_PORT,
81
+ );
82
+
83
+ if (!deviceKey || !host) {
84
+ return [
85
+ deviceKey,
86
+ {
87
+ status: 'offline',
88
+ error: 'IP ou hostname nao configurado.',
89
+ },
90
+ ];
91
+ }
92
+
93
+ try {
94
+ await checkNetworkPrinterConnection({host, port});
95
+
96
+ return [
97
+ deviceKey,
98
+ {
99
+ status: 'online',
100
+ host,
101
+ port,
102
+ checkedAt: Date.now(),
103
+ },
104
+ ];
105
+ } catch (connectError) {
106
+ return [
107
+ deviceKey,
108
+ {
109
+ status: 'offline',
110
+ host,
111
+ port,
112
+ checkedAt: Date.now(),
113
+ error:
114
+ connectError?.message || 'Falha ao conectar com o equipamento.',
115
+ },
116
+ ];
117
+ }
118
+ }),
119
+ ).then(results => {
120
+ if (cancelled) {
121
+ return;
122
+ }
123
+
124
+ setNetworkConnectivityByDevice(previousState => {
125
+ const nextState = {...previousState};
126
+
127
+ results.forEach(([deviceKey, statusEntry]) => {
128
+ if (!deviceKey) {
129
+ return;
130
+ }
131
+
132
+ nextState[deviceKey] = statusEntry;
133
+ });
134
+
135
+ return nextState;
136
+ });
137
+ });
138
+
139
+ return () => {
140
+ cancelled = true;
141
+ };
142
+ }, [deviceConfigs]);
143
+
144
+ return networkConnectivityByDevice;
145
+ }
@@ -0,0 +1,183 @@
1
+ import React, {useEffect, useMemo, useState} from 'react';
2
+ import {Text, TouchableOpacity, View} from 'react-native';
3
+ import {SafeAreaView} from 'react-native-safe-area-context';
4
+ import {useNavigation} from '@react-navigation/native';
5
+ import {useStore} from '@store';
6
+ import {resolveThemePalette} from '@controleonline/../../src/styles/branding';
7
+ import {colors} from '@controleonline/../../src/styles/colors';
8
+ import CompactFilterSelector from '@controleonline/ui-default/src/react/components/filters/CompactFilterSelector';
9
+ import {PDV_DEVICE_TYPE} from '@controleonline/ui-common/src/react/utils/printerDevices';
10
+ import Icon from 'react-native-vector-icons/Feather';
11
+ import {
12
+ ALL_DEVICE_FILTER,
13
+ getDeviceCreationAction,
14
+ getDeviceFilterAccent,
15
+ getDeviceFilterDefinition,
16
+ getDeviceFilterIcon,
17
+ getDeviceFilterOptions,
18
+ getPdvSubfilterOptions,
19
+ hasRegisteredDeviceFilter,
20
+ isPdvSubfilter,
21
+ } from './Devices/deviceTypes';
22
+ import { createStyles } from './Devices.styles';
23
+
24
+ const tt = (type, key) => global.t?.t('configs', type, key);
25
+
26
+ const Devices = () => {
27
+ const navigation = useNavigation();
28
+ const peopleStore = useStore('people');
29
+ const themeStore = useStore('theme');
30
+
31
+ const {currentCompany} = peopleStore.getters;
32
+ const {colors: themeColors} = themeStore.getters;
33
+
34
+ const [activeFilter, setActiveFilter] = useState(ALL_DEVICE_FILTER);
35
+
36
+ const brandColors = useMemo(
37
+ () =>
38
+ resolveThemePalette(
39
+ {...themeColors, ...(currentCompany?.theme?.colors || {})},
40
+ colors,
41
+ ),
42
+ [themeColors, currentCompany?.id],
43
+ );
44
+ const styles = useMemo(() => createStyles(brandColors), [brandColors]);
45
+ const accentPalette = useMemo(
46
+ () => ({
47
+ primary: brandColors.info,
48
+ success: brandColors.success,
49
+ danger: brandColors.error,
50
+ warning: brandColors.warning,
51
+ }),
52
+ [brandColors.error, brandColors.info, brandColors.success, brandColors.warning],
53
+ );
54
+
55
+ const filterOptions = useMemo(() => getDeviceFilterOptions(), []);
56
+ const pdvSubfilterOptions = useMemo(() => getPdvSubfilterOptions(), []);
57
+ const activeFilterDefinition = useMemo(
58
+ () => getDeviceFilterDefinition(activeFilter),
59
+ [activeFilter],
60
+ );
61
+ const topLevelActiveFilter = useMemo(
62
+ () => (isPdvSubfilter(activeFilter) ? PDV_DEVICE_TYPE : activeFilter),
63
+ [activeFilter],
64
+ );
65
+ const activeFilterAction = useMemo(
66
+ () => getDeviceCreationAction(topLevelActiveFilter),
67
+ [topLevelActiveFilter],
68
+ );
69
+ const deviceTypeSelectorOptions = useMemo(
70
+ () => filterOptions.map(option => ({
71
+ key: option.key,
72
+ label: option.label,
73
+ })),
74
+ [filterOptions],
75
+ );
76
+ const pdvGatewaySelectorOptions = useMemo(
77
+ () => [
78
+ {key: PDV_DEVICE_TYPE, label: tt('device_filter', 'all') || 'Todos'},
79
+ ...pdvSubfilterOptions.map(option => ({
80
+ key: option.key,
81
+ label: option.label,
82
+ })),
83
+ ],
84
+ [pdvSubfilterOptions],
85
+ );
86
+ const selectedDeviceTypeLabel = useMemo(
87
+ () =>
88
+ deviceTypeSelectorOptions.find(option => option.key === topLevelActiveFilter)?.label ||
89
+ tt('device_filter', 'allTypes') ||
90
+ 'Todos os tipos',
91
+ [deviceTypeSelectorOptions, topLevelActiveFilter],
92
+ );
93
+ const selectedPdvGatewayLabel = useMemo(
94
+ () =>
95
+ pdvGatewaySelectorOptions.find(option => option.key === activeFilter)?.label ||
96
+ tt('device_filter', 'all') ||
97
+ 'Todos',
98
+ [activeFilter, pdvGatewaySelectorOptions],
99
+ );
100
+ const ActiveTabComponent = activeFilterDefinition.TabComponent;
101
+
102
+ useEffect(() => {
103
+ if (!hasRegisteredDeviceFilter(activeFilter)) {
104
+ setActiveFilter(ALL_DEVICE_FILTER);
105
+ }
106
+ }, [activeFilter]);
107
+
108
+ return (
109
+ <SafeAreaView
110
+ style={[styles.container, {backgroundColor: brandColors.background}]}>
111
+ <View style={styles.filtersBlock}>
112
+ <Text style={styles.filtersLabel}>{tt('device_filter', 'filterByType') || 'Filtrar por tipo'}</Text>
113
+ <CompactFilterSelector
114
+ icon={getDeviceFilterIcon(topLevelActiveFilter)}
115
+ label={selectedDeviceTypeLabel}
116
+ title={tt('device_filter', 'filterByType') || 'Filtrar por tipo'}
117
+ accentColor={getDeviceFilterAccent(topLevelActiveFilter, {
118
+ brandColors,
119
+ hex: accentPalette,
120
+ })}
121
+ active={topLevelActiveFilter !== ALL_DEVICE_FILTER}
122
+ options={deviceTypeSelectorOptions}
123
+ selectedKey={topLevelActiveFilter}
124
+ onSelect={optionKey => {
125
+ setActiveFilter(optionKey);
126
+ return true;
127
+ }}
128
+ />
129
+ </View>
130
+
131
+ {topLevelActiveFilter === PDV_DEVICE_TYPE && pdvSubfilterOptions.length > 0 ? (
132
+ <View style={styles.filtersBlock}>
133
+ <Text style={styles.filtersLabel}>{tt('device_filter', 'pdvGateway') || 'Gateway do PDV'}</Text>
134
+ <CompactFilterSelector
135
+ icon={getDeviceFilterIcon(activeFilter)}
136
+ label={selectedPdvGatewayLabel}
137
+ title={tt('device_filter', 'pdvGateway') || 'Gateway do PDV'}
138
+ accentColor={getDeviceFilterAccent(activeFilter, {
139
+ brandColors,
140
+ hex: accentPalette,
141
+ })}
142
+ active={activeFilter !== PDV_DEVICE_TYPE}
143
+ options={pdvGatewaySelectorOptions}
144
+ selectedKey={activeFilter}
145
+ onSelect={optionKey => {
146
+ setActiveFilter(optionKey);
147
+ return true;
148
+ }}
149
+ />
150
+ </View>
151
+ ) : null}
152
+
153
+ {activeFilterAction ? (
154
+ <View style={styles.printerActionBlock}>
155
+ <TouchableOpacity
156
+ style={[
157
+ styles.createPrinterBtn,
158
+ {backgroundColor: brandColors.primary},
159
+ ]}
160
+ activeOpacity={0.86}
161
+ onPress={() => navigation.navigate(activeFilterAction.routeName)}>
162
+ <Icon name="plus-circle" size={16} color={brandColors.white} />
163
+ <Text style={styles.createPrinterBtnText}>
164
+ {activeFilterAction.label}
165
+ </Text>
166
+ </TouchableOpacity>
167
+
168
+ <Text style={styles.helperText}>
169
+ {activeFilterAction.helperText}
170
+ </Text>
171
+ </View>
172
+ ) : null}
173
+
174
+ {ActiveTabComponent ? (
175
+ <ActiveTabComponent
176
+ key={`${activeFilter}-${currentCompany?.id || 'company'}`}
177
+ />
178
+ ) : null}
179
+ </SafeAreaView>
180
+ );
181
+ };
182
+
183
+ export default Devices;
@@ -0,0 +1,353 @@
1
+ import { Platform, StyleSheet } from 'react-native';
2
+ import { colors } from '@controleonline/../../src/styles/colors';
3
+ import { withOpacity } from '@controleonline/../../src/styles/branding';
4
+
5
+ const buildCardShadow = palette =>
6
+ Platform.select({
7
+ ios: {
8
+ shadowColor: palette.text,
9
+ shadowOffset: { width: 0, height: 4 },
10
+ shadowOpacity: 0.06,
11
+ shadowRadius: 12,
12
+ },
13
+ android: { elevation: 2 },
14
+ web: { boxShadow: `0 4px 12px ${withOpacity(palette.text, 0.06)}` },
15
+ });
16
+
17
+ export const createStyles = (palette = colors) => {
18
+ const cardShadow = buildCardShadow(palette);
19
+ const cardBackground = palette.cardBackground || palette.white;
20
+ const cardBorder = palette.cardBorder || palette.border;
21
+ const cardText = palette.cardText || palette.text;
22
+ const selectedBackground =
23
+ palette.cardSelectedBackground || withOpacity(palette.primary, 0.07);
24
+ const selectedBorder = palette.cardSelectedBorder || palette.primary;
25
+ const selectedText = palette.cardSelectedText || cardText;
26
+ const selectedBadgeBackground =
27
+ palette.badgeSelectedBackground || withOpacity(palette.primary, 0.14);
28
+ const selectedBadgeText =
29
+ palette.badgeSelectedText || selectedText || palette.primary;
30
+ return StyleSheet.create({
31
+ container: {
32
+ flex: 1,
33
+ paddingHorizontal: 16,
34
+ paddingTop: 14,
35
+ },
36
+
37
+ summaryRow: {
38
+ flexDirection: 'row',
39
+ gap: 10,
40
+ marginBottom: 14,
41
+ },
42
+ summaryCard: {
43
+ flex: 1,
44
+ backgroundColor: cardBackground,
45
+ borderColor: cardBorder,
46
+ borderWidth: 1,
47
+ borderRadius: 14,
48
+ paddingHorizontal: 12,
49
+ paddingVertical: 10,
50
+ ...cardShadow,
51
+ },
52
+ summaryLabel: {
53
+ fontSize: 11,
54
+ fontWeight: '700',
55
+ textTransform: 'uppercase',
56
+ color: palette.textSecondary,
57
+ marginBottom: 4,
58
+ letterSpacing: 0.3,
59
+ },
60
+ summaryValue: {
61
+ fontSize: 22,
62
+ fontWeight: '800',
63
+ color: palette.text,
64
+ },
65
+ createPrinterBtn: {
66
+ minHeight: 46,
67
+ borderRadius: 14,
68
+ alignItems: 'center',
69
+ justifyContent: 'center',
70
+ flexDirection: 'row',
71
+ gap: 8,
72
+ marginBottom: 8,
73
+ ...cardShadow,
74
+ },
75
+ createPrinterBtnText: {
76
+ fontSize: 14,
77
+ fontWeight: '800',
78
+ color: palette.white,
79
+ },
80
+ printerActionBlock: {
81
+ marginBottom: 12,
82
+ },
83
+ helperText: {
84
+ fontSize: 12,
85
+ color: palette.textSecondary,
86
+ fontWeight: '600',
87
+ lineHeight: 18,
88
+ },
89
+ filtersBlock: {
90
+ marginBottom: 14,
91
+ },
92
+ filtersLabel: {
93
+ fontSize: 11,
94
+ fontWeight: '700',
95
+ color: palette.textSecondary,
96
+ textTransform: 'uppercase',
97
+ letterSpacing: 0.4,
98
+ marginBottom: 8,
99
+ },
100
+ loadingBox: {
101
+ flexDirection: 'row',
102
+ gap: 8,
103
+ alignItems: 'center',
104
+ marginBottom: 10,
105
+ },
106
+ loadingText: {
107
+ fontSize: 12,
108
+ color: palette.textSecondary,
109
+ fontWeight: '600',
110
+ },
111
+ tabContent: {
112
+ flex: 1,
113
+ },
114
+ tabList: {
115
+ flex: 1,
116
+ },
117
+ listMetaRow: {
118
+ flexDirection: 'row',
119
+ alignItems: 'center',
120
+ justifyContent: 'space-between',
121
+ marginBottom: 10,
122
+ },
123
+ listMetaTitle: {
124
+ fontSize: 14,
125
+ fontWeight: '800',
126
+ color: palette.text,
127
+ },
128
+ listMetaText: {
129
+ fontSize: 12,
130
+ fontWeight: '700',
131
+ color: palette.textSecondary,
132
+ },
133
+ inlineMessageBox: {
134
+ marginBottom: 10,
135
+ borderRadius: 14,
136
+ paddingHorizontal: 12,
137
+ paddingVertical: 10,
138
+ backgroundColor: withOpacity(palette.error, 0.12),
139
+ borderWidth: 1,
140
+ borderColor: withOpacity(palette.error, 0.3),
141
+ },
142
+ inlineMessageText: {
143
+ fontSize: 12,
144
+ fontWeight: '600',
145
+ color: palette.error,
146
+ lineHeight: 18,
147
+ },
148
+
149
+ listContent: {
150
+ gap: 10,
151
+ flexGrow: 1,
152
+ paddingBottom: 24,
153
+ },
154
+ listContentEmpty: {
155
+ justifyContent: 'center',
156
+ },
157
+ listFooterLoader: {
158
+ flexDirection: 'row',
159
+ gap: 8,
160
+ alignItems: 'center',
161
+ justifyContent: 'center',
162
+ paddingTop: 6,
163
+ paddingBottom: 18,
164
+ },
165
+
166
+ deviceCard: {
167
+ backgroundColor: cardBackground,
168
+ borderColor: cardBorder,
169
+ borderWidth: 1,
170
+ borderRadius: 16,
171
+ padding: 14,
172
+ flexDirection: 'row',
173
+ alignItems: 'center',
174
+ justifyContent: 'space-between',
175
+ ...cardShadow,
176
+ },
177
+ deviceCardCurrent: {
178
+ backgroundColor: selectedBackground,
179
+ borderColor: selectedBorder,
180
+ borderWidth: 2,
181
+ },
182
+ cardLeft: {
183
+ flexDirection: 'row',
184
+ alignItems: 'center',
185
+ gap: 12,
186
+ flex: 1,
187
+ },
188
+ cardTextWrap: {
189
+ flex: 1,
190
+ },
191
+ currentDeviceBadge: {
192
+ alignItems: 'center',
193
+ alignSelf: 'flex-start',
194
+ backgroundColor: selectedBadgeBackground,
195
+ borderColor: selectedBorder,
196
+ borderRadius: 999,
197
+ borderWidth: 1,
198
+ flexDirection: 'row',
199
+ gap: 4,
200
+ marginBottom: 5,
201
+ paddingHorizontal: 7,
202
+ paddingVertical: 3,
203
+ },
204
+ currentDeviceBadgeText: {
205
+ color: selectedBadgeText,
206
+ fontSize: 9,
207
+ fontWeight: '800',
208
+ letterSpacing: 0.2,
209
+ textTransform: 'uppercase',
210
+ },
211
+ iconBox: {
212
+ width: 40,
213
+ height: 40,
214
+ borderRadius: 10,
215
+ alignItems: 'center',
216
+ justifyContent: 'center',
217
+ },
218
+ deviceTitle: {
219
+ fontSize: 15,
220
+ fontWeight: '700',
221
+ color: cardText,
222
+ },
223
+ deviceSub: {
224
+ fontSize: 11,
225
+ color: palette.textSecondary,
226
+ marginTop: 2,
227
+ },
228
+ deviceMetaRow: {
229
+ flexDirection: 'row',
230
+ flexWrap: 'wrap',
231
+ gap: 6,
232
+ marginTop: 8,
233
+ },
234
+ deviceMetaChip: {
235
+ borderRadius: 999,
236
+ backgroundColor: palette.border,
237
+ paddingHorizontal: 8,
238
+ paddingVertical: 4,
239
+ },
240
+ deviceMetaChipText: {
241
+ fontSize: 10,
242
+ fontWeight: '700',
243
+ color: palette.textSecondary,
244
+ },
245
+ deviceConfigRow: {
246
+ alignItems: 'center',
247
+ flexDirection: 'row',
248
+ flexWrap: 'wrap',
249
+ gap: 7,
250
+ marginTop: 10,
251
+ },
252
+ deviceConfigChip: {
253
+ alignItems: 'center',
254
+ borderRadius: 999,
255
+ borderWidth: 1,
256
+ flexDirection: 'row',
257
+ gap: 5,
258
+ minHeight: 32,
259
+ paddingHorizontal: 10,
260
+ paddingVertical: 6,
261
+ },
262
+ deviceConfigChipActive: {
263
+ borderWidth: 2,
264
+ },
265
+ deviceConfigChipText: {
266
+ fontSize: 10,
267
+ fontWeight: '800',
268
+ letterSpacing: 0.2,
269
+ textTransform: 'uppercase',
270
+ },
271
+ deviceConfigChipActiveText: {
272
+ color: selectedText,
273
+ fontSize: 9,
274
+ fontWeight: '800',
275
+ textTransform: 'uppercase',
276
+ },
277
+ deviceConfigCreateChip: {
278
+ alignItems: 'center',
279
+ backgroundColor: palette.buttonBackground || palette.primary,
280
+ borderColor: palette.buttonBorder || palette.primary,
281
+ borderRadius: 999,
282
+ borderWidth: 1,
283
+ flexDirection: 'row',
284
+ gap: 6,
285
+ minHeight: 32,
286
+ paddingHorizontal: 11,
287
+ paddingVertical: 6,
288
+ },
289
+ deviceConfigCreateChipText: {
290
+ color: palette.buttonText || palette.white,
291
+ fontSize: 10,
292
+ fontWeight: '800',
293
+ },
294
+ cardRight: {
295
+ flexDirection: 'row',
296
+ alignItems: 'center',
297
+ },
298
+ badge: {
299
+ flexDirection: 'row',
300
+ alignItems: 'center',
301
+ gap: 5,
302
+ borderRadius: 999,
303
+ borderWidth: 1,
304
+ paddingHorizontal: 9,
305
+ paddingVertical: 4,
306
+ },
307
+ dot: {
308
+ width: 6,
309
+ height: 6,
310
+ borderRadius: 3,
311
+ },
312
+ badgeText: {
313
+ fontSize: 11,
314
+ fontWeight: '700',
315
+ textTransform: 'uppercase',
316
+ letterSpacing: 0.3,
317
+ },
318
+ chevronIcon: {
319
+ marginLeft: 8,
320
+ },
321
+
322
+ currentPdvSetupButtonDisabled: {
323
+ opacity: 0.6,
324
+ },
325
+
326
+ emptyBox: {
327
+ alignItems: 'center',
328
+ backgroundColor: cardBackground,
329
+ borderColor: cardBorder,
330
+ borderWidth: 1,
331
+ borderRadius: 16,
332
+ padding: 32,
333
+ ...cardShadow,
334
+ },
335
+ emptyIcon: {
336
+ marginBottom: 12,
337
+ },
338
+ emptyTitle: {
339
+ fontSize: 16,
340
+ fontWeight: '700',
341
+ color: palette.text,
342
+ marginBottom: 4,
343
+ },
344
+ emptySub: {
345
+ fontSize: 12,
346
+ color: palette.textSecondary,
347
+ textAlign: 'center',
348
+ },
349
+ });
350
+ };
351
+
352
+ const styles = createStyles(colors);
353
+ export default styles;