@controleonline/ui-common 1.2.98 → 1.2.100
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.
- package/docs/homepage-1056.md +12 -0
- package/package.json +1 -1
- package/src/api/index.js +40 -54
- package/src/react/components/AnimatedModal.js +6 -1
- package/src/react/components/BottomNavigationBar.js +19 -8
- package/src/react/components/BottomNavigationBar.soft.styles.js +29 -0
- package/src/react/components/DefaultProvider.styles.js +4 -0
- package/src/react/components/DefaultProvider.web.js +6 -3
- package/src/react/components/ProductCatalogCacheService.js +53 -45
- package/src/react/components/RuntimeBottomNavigationBar.js +12 -3
- package/src/react/components/RuntimeInfoFooter.home.spacing.js +5 -0
- package/src/react/components/RuntimeInfoFooter.js +8 -2
- package/src/react/components/UserAvatar.js +5 -14
- package/src/react/hooks/useBottomNavigationPreference.js +49 -0
- package/src/react/pages/Devices/deviceTypes/DeviceGroupCard.js +6 -4
- package/src/react/pages/Devices/deviceTypes/shared.js +9 -4
- package/src/react/pages/Devices.js +184 -183
- package/src/react/pages/IntegrationConfigPage.js +59 -13
- package/src/react/pages/IntegrationConfigPage.utils.js +17 -0
- package/src/react/pages/integrationsCatalog.js +71 -0
- package/src/react/utils/productCatalogCacheRevision.js +13 -0
- package/src/tests/react/utils/runtimeFooter.test.js +189 -189
- package/src/tests/react/utils/storedSessionToken.test.js +22 -0
- package/src/utils/integrationConfigs.js +23 -0
- package/src/utils/storedSessionToken.js +18 -0
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
parseConfigsObject,
|
|
25
25
|
tt,
|
|
26
26
|
} from './deviceListHelpers';
|
|
27
|
-
import
|
|
27
|
+
import legacyStyles from '../../Devices.styles';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Normalize DefaultTable card `item` into a device group shape.
|
|
@@ -65,6 +65,7 @@ const normalizeDeviceGroup = item => {
|
|
|
65
65
|
export default function DeviceGroupCard({
|
|
66
66
|
item,
|
|
67
67
|
brandColors,
|
|
68
|
+
themedStyles = null,
|
|
68
69
|
creatingPdv,
|
|
69
70
|
goToDetail,
|
|
70
71
|
handleCreateCurrentPdv,
|
|
@@ -74,6 +75,7 @@ export default function DeviceGroupCard({
|
|
|
74
75
|
runtimeDeviceType,
|
|
75
76
|
showCurrentPdvSetup,
|
|
76
77
|
}) {
|
|
78
|
+
const styles = themedStyles || legacyStyles;
|
|
77
79
|
const deviceGroup = normalizeDeviceGroup(item);
|
|
78
80
|
if (!deviceGroup) {
|
|
79
81
|
return null;
|
|
@@ -116,7 +118,7 @@ export default function DeviceGroupCard({
|
|
|
116
118
|
deviceGroup.device?.id ||
|
|
117
119
|
deviceGroup.key,
|
|
118
120
|
);
|
|
119
|
-
const accent = getDeviceTypeAccent(normalizedType);
|
|
121
|
+
const accent = brandColors.deviceAccent || getDeviceTypeAccent(normalizedType);
|
|
120
122
|
const metaChips = [];
|
|
121
123
|
|
|
122
124
|
if (isPdv && primaryConfig) {
|
|
@@ -196,7 +198,7 @@ export default function DeviceGroupCard({
|
|
|
196
198
|
<View style={styles.deviceConfigRow}>
|
|
197
199
|
{deviceConfigs.map(deviceConfig => {
|
|
198
200
|
const configType = getDeviceConfigType(deviceConfig);
|
|
199
|
-
const configAccent = getDeviceTypeAccent(configType);
|
|
201
|
+
const configAccent = brandColors.deviceAccent || getDeviceTypeAccent(configType);
|
|
200
202
|
const isSessionConfig =
|
|
201
203
|
isCurrentDevice &&
|
|
202
204
|
runtimeDeviceType &&
|
|
@@ -297,5 +299,5 @@ export default function DeviceGroupCard({
|
|
|
297
299
|
</View>
|
|
298
300
|
</View>
|
|
299
301
|
);
|
|
300
|
-
|
|
302
|
+
|
|
301
303
|
}
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
prioritizeCurrentDeviceGroups,
|
|
37
37
|
readStoredRuntimeDevice,
|
|
38
38
|
} from '../currentDevice';
|
|
39
|
-
import
|
|
39
|
+
import legacyStyles, {createStyles} from '../../Devices.styles';
|
|
40
40
|
import DeviceGroupCard from './DeviceGroupCard';
|
|
41
41
|
import {useDeviceNetworkConnectivity} from './useDeviceNetworkConnectivity';
|
|
42
42
|
import {
|
|
@@ -89,7 +89,7 @@ export const createDeviceTypeTab = ({
|
|
|
89
89
|
clientFilter = null,
|
|
90
90
|
offerCurrentPdvSetup = false,
|
|
91
91
|
}) => {
|
|
92
|
-
const DeviceTypeTab = () => {
|
|
92
|
+
const DeviceTypeTab = ({themePalette = null} = {}) => {
|
|
93
93
|
const navigation = useNavigation();
|
|
94
94
|
const peopleStore = useStore('people');
|
|
95
95
|
const deviceStore = useStore('device');
|
|
@@ -101,13 +101,15 @@ export const createDeviceTypeTab = ({
|
|
|
101
101
|
|
|
102
102
|
const brandColors = useMemo(
|
|
103
103
|
() =>
|
|
104
|
-
resolveThemePalette(
|
|
104
|
+
themePalette || resolveThemePalette(
|
|
105
105
|
{...themeColors, ...(currentCompany?.theme?.colors || {})},
|
|
106
106
|
colors,
|
|
107
107
|
),
|
|
108
|
-
[themeColors, currentCompany?.id],
|
|
108
|
+
[themePalette, themeColors, currentCompany?.id],
|
|
109
109
|
);
|
|
110
110
|
|
|
111
|
+
const styles = useMemo(() => themePalette ? createStyles(brandColors) : legacyStyles, [themePalette, brandColors]);
|
|
112
|
+
|
|
111
113
|
const [deviceConfigs, setDeviceConfigs] = useState([]);
|
|
112
114
|
const [loading, setLoading] = useState(false);
|
|
113
115
|
const [refreshing, setRefreshing] = useState(false);
|
|
@@ -372,6 +374,7 @@ export const createDeviceTypeTab = ({
|
|
|
372
374
|
<DeviceGroupCard
|
|
373
375
|
{...cardProps}
|
|
374
376
|
brandColors={brandColors}
|
|
377
|
+
themedStyles={themePalette ? styles : null}
|
|
375
378
|
creatingPdv={creatingPdv}
|
|
376
379
|
goToDetail={goToDetail}
|
|
377
380
|
handleCreateCurrentPdv={handleCreateCurrentPdv}
|
|
@@ -384,6 +387,8 @@ export const createDeviceTypeTab = ({
|
|
|
384
387
|
),
|
|
385
388
|
[
|
|
386
389
|
brandColors,
|
|
390
|
+
styles,
|
|
391
|
+
themePalette,
|
|
387
392
|
creatingPdv,
|
|
388
393
|
goToDetail,
|
|
389
394
|
handleCreateCurrentPdv,
|
|
@@ -1,183 +1,184 @@
|
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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 = ({themePalette = null} = {}) => {
|
|
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
|
+
themePalette || resolveThemePalette(
|
|
39
|
+
{...themeColors, ...(currentCompany?.theme?.colors || {})},
|
|
40
|
+
colors,
|
|
41
|
+
),
|
|
42
|
+
[themePalette, 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
|
+
themePalette={themePalette}
|
|
177
|
+
key={`${activeFilter}-${currentCompany?.id || 'company'}`}
|
|
178
|
+
/>
|
|
179
|
+
) : null}
|
|
180
|
+
</SafeAreaView>
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export default Devices;
|
|
@@ -14,6 +14,7 @@ import { fetchPeopleConfigs } from './fetchPeopleConfigs';
|
|
|
14
14
|
import IntegrationConfigFields from './IntegrationConfigFields';
|
|
15
15
|
import {
|
|
16
16
|
ROUTE_PROVIDER_MAP,
|
|
17
|
+
expandBundledConfig,
|
|
17
18
|
extractAuthorizationUrl,
|
|
18
19
|
formatApiError,
|
|
19
20
|
formatUberOAuthError,
|
|
@@ -112,6 +113,8 @@ export default function IntegrationConfigPage({ route, embedded = false, fiscalT
|
|
|
112
113
|
const allFieldKeysSig = useMemo(() => allFieldKeys.join('|'), [allFieldKeys]);
|
|
113
114
|
// Pure config providers (e.g. receita-federal) do not use marketplace/integrations.
|
|
114
115
|
const needsMarketplaceIntegration = Boolean(providerConfig?.oauthConnect);
|
|
116
|
+
const bundleKey = normalizeTextValue(providerConfig?.bundleKey);
|
|
117
|
+
const configVisibility = normalizeTextValue(providerConfig?.visibility) || 'public';
|
|
115
118
|
|
|
116
119
|
useEffect(() => {
|
|
117
120
|
setConfigValues({});
|
|
@@ -135,14 +138,28 @@ export default function IntegrationConfigPage({ route, embedded = false, fiscalT
|
|
|
135
138
|
}
|
|
136
139
|
if (showLoading) setLoading(true);
|
|
137
140
|
try {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const
|
|
141
|
+
if (bundleKey) {
|
|
142
|
+
const configItems = await fetchPeopleConfigs({
|
|
143
|
+
peopleIri: providerIri,
|
|
144
|
+
configKeys: [bundleKey],
|
|
145
|
+
});
|
|
146
|
+
const bundled = configItems.find(
|
|
147
|
+
item => String(item?.configKey || '').trim() === bundleKey,
|
|
148
|
+
);
|
|
144
149
|
const mergeFields = allConfigFields.length ? allConfigFields : visibleFields;
|
|
145
|
-
setConfigValues(current =>
|
|
150
|
+
setConfigValues(current =>
|
|
151
|
+
expandBundledConfig(mergeFields, bundled?.configValue, current),
|
|
152
|
+
);
|
|
153
|
+
} else {
|
|
154
|
+
const keys = allFieldKeys.length
|
|
155
|
+
? allFieldKeys
|
|
156
|
+
: visibleFields.map(field => field.key).filter(Boolean);
|
|
157
|
+
|
|
158
|
+
if (keys.length) {
|
|
159
|
+
const configItems = await fetchPeopleConfigs({ peopleIri: providerIri, configKeys: keys });
|
|
160
|
+
const mergeFields = allConfigFields.length ? allConfigFields : visibleFields;
|
|
161
|
+
setConfigValues(current => mergeTabValues(mergeFields, configItems, current));
|
|
162
|
+
}
|
|
146
163
|
}
|
|
147
164
|
|
|
148
165
|
if (needsMarketplaceIntegration) {
|
|
@@ -170,6 +187,7 @@ export default function IntegrationConfigPage({ route, embedded = false, fiscalT
|
|
|
170
187
|
allConfigFields,
|
|
171
188
|
allFieldKeys,
|
|
172
189
|
allFieldKeysSig,
|
|
190
|
+
bundleKey,
|
|
173
191
|
needsMarketplaceIntegration,
|
|
174
192
|
providerConfig,
|
|
175
193
|
providerIri,
|
|
@@ -246,16 +264,44 @@ export default function IntegrationConfigPage({ route, embedded = false, fiscalT
|
|
|
246
264
|
showError('Nao foi possivel identificar a integracao selecionada.');
|
|
247
265
|
return;
|
|
248
266
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
267
|
+
|
|
268
|
+
let configs;
|
|
269
|
+
if (bundleKey) {
|
|
270
|
+
const payloadObject = {};
|
|
271
|
+
(visibleFields || []).forEach(field => {
|
|
272
|
+
payloadObject[field.key] = normalizeTextValue(configValues[field.key]);
|
|
273
|
+
});
|
|
274
|
+
configs = [
|
|
275
|
+
{
|
|
276
|
+
configKey: bundleKey,
|
|
277
|
+
configValue: toConfigRequestValue(payloadObject),
|
|
278
|
+
},
|
|
279
|
+
];
|
|
280
|
+
} else {
|
|
281
|
+
configs = visibleFields.map(field => ({
|
|
282
|
+
configKey: field.key,
|
|
283
|
+
configValue: toConfigRequestValue(normalizeTextValue(configValues[field.key])),
|
|
284
|
+
}));
|
|
285
|
+
}
|
|
286
|
+
|
|
253
287
|
try {
|
|
254
|
-
await persistConfigs({ configs, people: providerIri, module: 4, visibility:
|
|
288
|
+
await persistConfigs({ configs, people: providerIri, module: 4, visibility: configVisibility });
|
|
255
289
|
showSuccess(`${activeTabDef?.label || providerConfig.label} salvo com sucesso.`);
|
|
256
290
|
await loadPageData({ showLoading: false });
|
|
257
291
|
} catch (error) { showError(error?.message || 'Nao foi possivel salvar a integracao.'); }
|
|
258
|
-
}, [
|
|
292
|
+
}, [
|
|
293
|
+
activeTabDef,
|
|
294
|
+
bundleKey,
|
|
295
|
+
configValues,
|
|
296
|
+
configVisibility,
|
|
297
|
+
loadPageData,
|
|
298
|
+
persistConfigs,
|
|
299
|
+
providerConfig,
|
|
300
|
+
providerIri,
|
|
301
|
+
showError,
|
|
302
|
+
showSuccess,
|
|
303
|
+
visibleFields,
|
|
304
|
+
]);
|
|
259
305
|
|
|
260
306
|
if (!providerConfig) return <CenterState icon="alert-triangle" iconColor="#e67e22" title="Integracao indisponivel" text="A tela solicitada nao possui configuracao cadastrada." />;
|
|
261
307
|
if (!providerId) return <CenterState icon="building" iconColor="#94A3B8" title="Empresa nao identificada" text="Abra a ficha da empresa para gravar as configuracoes fiscais nela." />;
|
|
@@ -5,6 +5,7 @@ export const ROUTE_PROVIDER_MAP = {
|
|
|
5
5
|
AsaasIntegrationPage: 'asaas',
|
|
6
6
|
ClickSignIntegrationPage: 'clicksign',
|
|
7
7
|
ReceitaFederalIntegrationPage: 'receita-federal',
|
|
8
|
+
SemPararIntegrationPage: 'sem-parar',
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
export const routeNameToPath = routeName =>
|
|
@@ -53,6 +54,22 @@ export const mergeTabValues = (fields, source, current = {}) => {
|
|
|
53
54
|
return next;
|
|
54
55
|
};
|
|
55
56
|
|
|
57
|
+
/** Expand a single JSON config (bundleKey) into flat field values. */
|
|
58
|
+
export const expandBundledConfig = (fields, rawValue, current = {}) => {
|
|
59
|
+
let parsed = rawValue;
|
|
60
|
+
if (typeof rawValue === 'string') {
|
|
61
|
+
try {
|
|
62
|
+
parsed = JSON.parse(rawValue);
|
|
63
|
+
} catch {
|
|
64
|
+
parsed = {};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
68
|
+
parsed = {};
|
|
69
|
+
}
|
|
70
|
+
return mergeTabValues(fields, parsed, current);
|
|
71
|
+
};
|
|
72
|
+
|
|
56
73
|
export const buildFieldValues = (providerConfig, source) =>
|
|
57
74
|
mergeTabValues(getConfigFields(providerConfig), source, {});
|
|
58
75
|
|