@controleonline/ui-common 1.2.80 → 1.2.81
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/AGENTS.md +8 -0
- package/package.json +9 -3
- package/src/api/fetch.js +7 -7
- package/src/react/components/AddImportModal.js +6 -2
- package/src/react/components/BackgroundRuntimeBridge.js +201 -215
- package/src/react/components/ConfirmModal.js +44 -9
- package/src/react/components/ConfirmModal.styles.js +55 -41
- package/src/react/components/CopyDeviceConfigModal.js +193 -0
- package/src/react/components/CopyDeviceConfigModal.styles.js +123 -0
- package/src/react/components/DefaultProvider.native.js +22 -0
- package/src/react/components/DefaultProvider.web.js +25 -1
- package/src/react/components/EntityLogBranch.js +482 -0
- package/src/react/components/EntityLogCards.js +369 -0
- package/src/react/components/EntityLogContent.js +113 -794
- package/src/react/components/LinkedOrderProductsTab.js +3 -3
- package/src/react/components/PrintService.js +130 -409
- package/src/react/components/RemoteCheckoutService.js +2 -2
- package/src/react/components/RuntimeBottomNavigationBar.js +141 -146
- package/src/react/components/RuntimeFooterMarqueeText.js +137 -0
- package/src/react/components/RuntimeInfoFooter.js +12 -15
- package/src/react/components/SystemErrorToast.js +49 -29
- package/src/react/components/SystemErrorToast.styles.js +43 -38
- package/src/react/components/UserAvatar.js +120 -28
- package/src/react/config/deviceConfigBootstrap.js +330 -304
- package/src/react/hooks/usePrintSpoolEffects.js +161 -0
- package/src/react/pages/DeviceDetailPage.js +6 -2882
- package/src/react/pages/Devices/detail/DeviceDetailAlertsCommandsSection.js +233 -0
- package/src/react/pages/Devices/detail/DeviceDetailHeader.js +138 -0
- package/src/react/pages/Devices/detail/DeviceDetailMovementSections.js +167 -0
- package/src/react/pages/Devices/detail/DeviceDetailOrdersPrintSection.js +216 -0
- package/src/react/pages/Devices/detail/DeviceDetailPaymentSection.js +40 -0
- package/src/react/pages/Devices/detail/DeviceDetailPdvConfigSection.js +390 -0
- package/src/react/pages/Devices/detail/DeviceDetailRenderers.js +135 -0
- package/src/react/pages/Devices/detail/DeviceDetailScreen.js +493 -0
- package/src/react/pages/Devices/detail/OptionButtonChip.js +65 -0
- package/src/react/pages/Devices/detail/deviceDetailConstants.js +36 -0
- package/src/react/pages/Devices/detail/deviceDetailHelpers.js +70 -0
- package/src/react/pages/Devices/detail/useDeviceDetailActions.js +483 -0
- package/src/react/pages/Devices/detail/useDeviceDetailCopyConfig.js +87 -0
- package/src/react/pages/Devices/detail/useDeviceDetailLoaders.js +327 -0
- package/src/react/pages/Devices/detail/useDeviceDetailSaves.js +429 -0
- package/src/react/pages/Devices/detail/useDeviceDetailStateA.js +484 -0
- package/src/react/pages/Devices/detail/useDeviceDetailStateB.js +71 -0
- package/src/react/pages/Devices/deviceTypes/index.js +2 -0
- package/src/react/pages/Devices/deviceTypes/pdv.js +11 -0
- package/src/react/pages/Imports.js +3 -2
- package/src/react/pages/IntegrationConfigFields.js +126 -0
- package/src/react/pages/IntegrationConfigPage.js +122 -591
- package/src/react/pages/IntegrationConfigPage.utils.js +95 -0
- package/src/react/print/usePrintButtonController.js +9 -1
- package/src/react/services/Getnet/Checkout.js +81 -0
- package/src/react/services/Getnet/Getnet.js +77 -0
- package/src/react/services/paymentGatewayExecution.js +20 -0
- package/src/react/utils/bottomNavigationToolbar.js +161 -0
- package/src/react/utils/commercialDocumentOrders.js +18 -6
- package/src/react/utils/copyDeviceConfigs.js +198 -0
- package/src/react/utils/fileUrl.js +43 -0
- package/src/react/utils/importStatus.js +19 -10
- package/src/react/utils/logSettings.js +7 -7
- package/src/react/utils/maintenanceSettings.js +16 -0
- package/src/react/utils/paymentDevices.js +6 -0
- package/src/react/utils/printManagedPrinter.js +45 -0
- package/src/react/utils/printRouting.js +63 -0
- package/src/react/utils/printSpoolUtils.js +119 -0
- package/src/react/utils/printerDevices.js +368 -368
- package/src/react/utils/runtimeFooter.js +10 -19
- package/src/react/utils/shopFranchises.js +186 -65
- package/src/react/utils/systemErrorMessage.js +71 -67
- package/src/store/device_config/customActions.js +119 -30
- package/src/tests/browser/manager/bottom-navigation-role-toolbar.spec.js +353 -0
- package/src/tests/browser/manager/device-detail-alias-save.spec.js +345 -0
- package/src/tests/browser/manager/device-detail-copy-config.spec.js +365 -0
- package/src/tests/browser/manager/runtime-footer-marquee.spec.js +262 -0
- package/src/tests/browser/manager/theme-confirm-toast.spec.js +236 -0
- package/src/tests/react/components/RuntimeFooterMarqueeText.test.js +171 -0
- package/src/tests/react/components/UserAvatar.test.js +6 -2
- package/src/tests/react/components/themeAwareModals.test.mjs +60 -0
- package/src/tests/react/config/deviceConfigBootstrap.test.js +104 -66
- package/src/tests/react/pages/deviceDetailDelete.test.js +78 -0
- package/src/tests/react/pages/receitaFederalIntegrationContract.test.mjs +48 -0
- package/src/tests/react/utils/bottomNavigationToolbar.test.js +222 -0
- package/src/tests/react/utils/copyDeviceConfigs.test.mjs +127 -0
- package/src/tests/react/utils/fileUrl.test.js +38 -0
- package/src/tests/react/utils/importStatus.test.js +26 -9
- package/src/tests/react/utils/passwordPolicy.test.js +27 -0
- package/src/tests/react/utils/printRouting.test.mjs +86 -0
- package/src/tests/react/utils/printSpoolUtils.test.mjs +52 -0
- package/src/tests/react/utils/runtimeFooter.test.js +7 -0
- package/src/tests/react/utils/shopFranchises.test.js +121 -115
- package/src/tests/react/utils/systemErrorMessage.test.js +32 -59
|
@@ -125,7 +125,7 @@ const Checkout = () => {
|
|
|
125
125
|
|
|
126
126
|
if (!paidStatusIri) {
|
|
127
127
|
invoiceActions.setError(
|
|
128
|
-
'
|
|
128
|
+
'Não foi possível resolver o status pago da invoice do PDV.',
|
|
129
129
|
);
|
|
130
130
|
return;
|
|
131
131
|
}
|
|
@@ -259,7 +259,7 @@ const Checkout = () => {
|
|
|
259
259
|
} catch (error) {
|
|
260
260
|
const errorMessage = normalizeGatewayPaymentError(
|
|
261
261
|
error,
|
|
262
|
-
'
|
|
262
|
+
'Não foi possível concluir o pagamento remoto.',
|
|
263
263
|
);
|
|
264
264
|
const wasCanceled = isRemotePaymentCancellation({
|
|
265
265
|
status: error?.status,
|
|
@@ -1,27 +1,23 @@
|
|
|
1
|
-
import React, {useEffect, useMemo, useState} from 'react';
|
|
2
|
-
import {useNavigationState} from '@react-navigation/native';
|
|
3
|
-
import {useStore} from '@store';
|
|
4
|
-
import {api} from '@controleonline/ui-common/src/api';
|
|
5
|
-
import {app_type} from '@appType';
|
|
6
|
-
import BottomNavigationBar from '@controleonline/ui-common/src/react/components/BottomNavigationBar';
|
|
1
|
+
import React, {useEffect, useMemo, useState} from 'react';
|
|
2
|
+
import {useNavigationState} from '@react-navigation/native';
|
|
3
|
+
import {useStore} from '@store';
|
|
4
|
+
import {api} from '@controleonline/ui-common/src/api';
|
|
5
|
+
import {app_type} from '@appType';
|
|
6
|
+
import BottomNavigationBar from '@controleonline/ui-common/src/react/components/BottomNavigationBar';
|
|
7
7
|
import {
|
|
8
8
|
getBottomNavigationPreset,
|
|
9
|
-
resolveBottomNavigationItems,
|
|
10
9
|
resolveBottomNavigationRoute,
|
|
11
10
|
} from '@controleonline/ui-common/src/react/components/BottomNavigationBar.config';
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return String(left?.label || '').localeCompare(String(right?.label || ''));
|
|
23
|
-
};
|
|
24
|
-
|
|
11
|
+
import {
|
|
12
|
+
normalizeAppType,
|
|
13
|
+
normalizeRuntimeMenuResponse,
|
|
14
|
+
resolveRuntimeMenuLabel,
|
|
15
|
+
} from '@controleonline/ui-common/src/react/utils/runtimeMenu';
|
|
16
|
+
const {
|
|
17
|
+
mapRuntimeMenusToNavItems,
|
|
18
|
+
resolveToolbarRuntimeMenus,
|
|
19
|
+
} = require('@controleonline/ui-common/src/react/utils/bottomNavigationToolbar');
|
|
20
|
+
|
|
25
21
|
const RuntimeBottomNavigationBar = ({
|
|
26
22
|
activeRouteName: activeRouteNameProp,
|
|
27
23
|
colors: colorsOverride,
|
|
@@ -30,15 +26,15 @@ const RuntimeBottomNavigationBar = ({
|
|
|
30
26
|
itemMapper,
|
|
31
27
|
menuType = 'toolbar',
|
|
32
28
|
navigation,
|
|
33
|
-
presetKey,
|
|
34
|
-
routeAliases: routeAliasesProp,
|
|
35
|
-
testID = 'bottom-navigation',
|
|
36
|
-
useModernWebChromeProps = false,
|
|
37
|
-
}) => {
|
|
38
|
-
const state = useNavigationState(current => current);
|
|
39
|
-
const activeRouteName =
|
|
40
|
-
activeRouteNameProp || state?.routes?.[state.index]?.name || 'HomePage';
|
|
41
|
-
|
|
29
|
+
presetKey,
|
|
30
|
+
routeAliases: routeAliasesProp,
|
|
31
|
+
testID = 'bottom-navigation',
|
|
32
|
+
useModernWebChromeProps = false,
|
|
33
|
+
}) => {
|
|
34
|
+
const state = useNavigationState(current => current);
|
|
35
|
+
const activeRouteName =
|
|
36
|
+
activeRouteNameProp || state?.routes?.[state.index]?.name || 'HomePage';
|
|
37
|
+
|
|
42
38
|
const themeStore = useStore('theme');
|
|
43
39
|
const peopleStore = useStore('people');
|
|
44
40
|
const themeGetters = themeStore?.getters || {};
|
|
@@ -50,129 +46,128 @@ const RuntimeBottomNavigationBar = ({
|
|
|
50
46
|
const routeAliases = routeAliasesProp || preset?.routeAliases || {};
|
|
51
47
|
const appType = normalizeAppType(app_type);
|
|
52
48
|
|
|
49
|
+
// Always consult menus-people for role-aware toolbar; preset is fallback only.
|
|
53
50
|
useEffect(() => {
|
|
54
51
|
if (!currentCompany?.id || !menuType) {
|
|
55
|
-
setRuntimeMenus(
|
|
52
|
+
setRuntimeMenus(
|
|
53
|
+
resolveToolbarRuntimeMenus({
|
|
54
|
+
apiMenus: [],
|
|
55
|
+
preset,
|
|
56
|
+
presetKey,
|
|
57
|
+
menuType,
|
|
58
|
+
translate: global.t?.t,
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
56
61
|
return undefined;
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
? resolveBottomNavigationItems(
|
|
62
|
-
preset.items.map((item, index) => ({
|
|
63
|
-
...item,
|
|
64
|
-
menuType,
|
|
65
|
-
sortOrder: Number(item?.sortOrder || (index + 1) * 10),
|
|
66
|
-
})),
|
|
67
|
-
global.t?.t,
|
|
68
|
-
)
|
|
69
|
-
: [];
|
|
64
|
+
let cancelled = false;
|
|
65
|
+
setRuntimeMenus([]);
|
|
70
66
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
67
|
+
api
|
|
68
|
+
.fetch('menus-people', {
|
|
69
|
+
params: {
|
|
70
|
+
myCompany: currentCompany.id,
|
|
71
|
+
appType,
|
|
72
|
+
menuType,
|
|
77
73
|
},
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
})
|
|
75
|
+
.then(result => {
|
|
76
|
+
if (cancelled) return;
|
|
77
|
+
const apiMenus = normalizeRuntimeMenuResponse(result, {
|
|
78
|
+
appType,
|
|
79
|
+
allowFallback: false,
|
|
80
|
+
});
|
|
81
|
+
setRuntimeMenus(
|
|
82
|
+
resolveToolbarRuntimeMenus({
|
|
83
|
+
apiMenus,
|
|
84
|
+
preset,
|
|
85
|
+
presetKey,
|
|
86
|
+
menuType,
|
|
87
|
+
translate: global.t?.t,
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
})
|
|
91
|
+
.catch(() => {
|
|
92
|
+
if (cancelled) return;
|
|
93
|
+
setRuntimeMenus(
|
|
94
|
+
resolveToolbarRuntimeMenus({
|
|
95
|
+
apiMenus: [],
|
|
96
|
+
preset,
|
|
97
|
+
presetKey,
|
|
98
|
+
menuType,
|
|
99
|
+
translate: global.t?.t,
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return () => {
|
|
105
|
+
cancelled = true;
|
|
106
|
+
};
|
|
107
|
+
}, [appType, currentCompany?.id, menuType, preset, presetKey]);
|
|
108
|
+
|
|
109
|
+
const colors = useMemo(() => {
|
|
110
|
+
if (colorsOverride) {
|
|
111
|
+
return colorsOverride;
|
|
80
112
|
}
|
|
81
113
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}, [
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
route: item.route,
|
|
143
|
-
icon: item.icon || 'circle',
|
|
144
|
-
label: resolveRuntimeMenuLabel(item, global.t?.t),
|
|
145
|
-
routeParams: item.routeParams,
|
|
146
|
-
menuType: item.menuType,
|
|
147
|
-
}))
|
|
148
|
-
.filter(item => Boolean(item.route));
|
|
149
|
-
}, [itemFilter, itemMapper, menuType, runtimeMenus]);
|
|
150
|
-
|
|
151
|
-
const resolvedActiveRoute = resolveBottomNavigationRoute(
|
|
152
|
-
routeAliases,
|
|
153
|
-
activeRouteName,
|
|
154
|
-
);
|
|
155
|
-
const knownRoute = navItems.some(item => item.route === resolvedActiveRoute);
|
|
156
|
-
const effectiveActiveRoute = knownRoute
|
|
157
|
-
? resolvedActiveRoute
|
|
158
|
-
: navItems[0]?.route || activeRouteName;
|
|
159
|
-
|
|
160
|
-
const isDisabled =
|
|
161
|
-
typeof disabledOverride === 'boolean'
|
|
162
|
-
? disabledOverride
|
|
163
|
-
: !currentCompany || Object.keys(currentCompany).length === 0;
|
|
164
|
-
|
|
165
|
-
return (
|
|
166
|
-
<BottomNavigationBar
|
|
167
|
-
activeRouteName={effectiveActiveRoute}
|
|
168
|
-
colors={colors}
|
|
169
|
-
disabled={isDisabled}
|
|
170
|
-
items={navItems}
|
|
171
|
-
navigation={navigation}
|
|
172
|
-
testID={testID}
|
|
173
|
-
useModernWebChromeProps={useModernWebChromeProps}
|
|
174
|
-
/>
|
|
175
|
-
);
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
export default RuntimeBottomNavigationBar;
|
|
114
|
+
return {
|
|
115
|
+
...themeColors,
|
|
116
|
+
...(currentCompany?.theme?.colors || {}),
|
|
117
|
+
};
|
|
118
|
+
}, [colorsOverride, currentCompany?.theme?.colors, themeColors]);
|
|
119
|
+
|
|
120
|
+
const navItems = useMemo(() => {
|
|
121
|
+
const mapper = typeof itemMapper === 'function' ? itemMapper : item => item;
|
|
122
|
+
const filter = typeof itemFilter === 'function' ? itemFilter : () => true;
|
|
123
|
+
const translate = global.t?.t;
|
|
124
|
+
|
|
125
|
+
return mapRuntimeMenusToNavItems(runtimeMenus, menuType)
|
|
126
|
+
.map(item => {
|
|
127
|
+
const label =
|
|
128
|
+
resolveRuntimeMenuLabel(
|
|
129
|
+
{menuKey: item.menuKey, label: item.label},
|
|
130
|
+
translate,
|
|
131
|
+
) ||
|
|
132
|
+
(typeof item.label === 'string' ? item.label : '') ||
|
|
133
|
+
item.menuKey ||
|
|
134
|
+
item.route;
|
|
135
|
+
|
|
136
|
+
return mapper({
|
|
137
|
+
...item,
|
|
138
|
+
label,
|
|
139
|
+
});
|
|
140
|
+
})
|
|
141
|
+
.filter(Boolean)
|
|
142
|
+
.filter(filter)
|
|
143
|
+
.filter(item => Boolean(item?.route));
|
|
144
|
+
}, [itemFilter, itemMapper, menuType, runtimeMenus]);
|
|
145
|
+
|
|
146
|
+
const resolvedActiveRoute = resolveBottomNavigationRoute(
|
|
147
|
+
routeAliases,
|
|
148
|
+
activeRouteName,
|
|
149
|
+
);
|
|
150
|
+
const knownRoute = navItems.some(item => item.route === resolvedActiveRoute);
|
|
151
|
+
const effectiveActiveRoute = knownRoute
|
|
152
|
+
? resolvedActiveRoute
|
|
153
|
+
: navItems[0]?.route || activeRouteName;
|
|
154
|
+
|
|
155
|
+
const isDisabled =
|
|
156
|
+
typeof disabledOverride === 'boolean'
|
|
157
|
+
? disabledOverride
|
|
158
|
+
: !currentCompany || Object.keys(currentCompany).length === 0;
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<BottomNavigationBar
|
|
162
|
+
activeRouteName={effectiveActiveRoute}
|
|
163
|
+
colors={colors}
|
|
164
|
+
disabled={isDisabled}
|
|
165
|
+
items={navItems}
|
|
166
|
+
navigation={navigation}
|
|
167
|
+
testID={testID}
|
|
168
|
+
useModernWebChromeProps={useModernWebChromeProps}
|
|
169
|
+
/>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export default RuntimeBottomNavigationBar;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const React = require('react');
|
|
2
|
+
const {useEffect, useRef, useState} = React;
|
|
3
|
+
const {Animated, Text, View} = require('react-native');
|
|
4
|
+
|
|
5
|
+
const GAP_PX = 32;
|
|
6
|
+
const MS_PER_PX = 28;
|
|
7
|
+
const MIN_DURATION_MS = 4000;
|
|
8
|
+
const HOLD_MS = 1200;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Single-line footer text. When content overflows the available width,
|
|
12
|
+
* scrolls horizontally in a continuous loop. Short text stays static.
|
|
13
|
+
*/
|
|
14
|
+
const RuntimeFooterMarqueeText = ({
|
|
15
|
+
text,
|
|
16
|
+
color,
|
|
17
|
+
style,
|
|
18
|
+
opacity,
|
|
19
|
+
testID = 'runtime-footer-marquee-text',
|
|
20
|
+
}) => {
|
|
21
|
+
const [containerWidth, setContainerWidth] = useState(0);
|
|
22
|
+
const [contentWidth, setContentWidth] = useState(0);
|
|
23
|
+
const translateX = useRef(new Animated.Value(0)).current;
|
|
24
|
+
const animationRef = useRef(null);
|
|
25
|
+
|
|
26
|
+
const shouldMarquee =
|
|
27
|
+
containerWidth > 0 && contentWidth > containerWidth + 2;
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (animationRef.current) {
|
|
31
|
+
animationRef.current.stop();
|
|
32
|
+
animationRef.current = null;
|
|
33
|
+
}
|
|
34
|
+
translateX.stopAnimation();
|
|
35
|
+
translateX.setValue(0);
|
|
36
|
+
|
|
37
|
+
if (!shouldMarquee) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const distance = contentWidth + GAP_PX;
|
|
42
|
+
const duration = Math.max(Math.round(distance * MS_PER_PX), MIN_DURATION_MS);
|
|
43
|
+
|
|
44
|
+
const loop = Animated.loop(
|
|
45
|
+
Animated.sequence([
|
|
46
|
+
Animated.delay(HOLD_MS),
|
|
47
|
+
Animated.timing(translateX, {
|
|
48
|
+
toValue: -distance,
|
|
49
|
+
duration,
|
|
50
|
+
useNativeDriver: true,
|
|
51
|
+
}),
|
|
52
|
+
Animated.timing(translateX, {
|
|
53
|
+
toValue: 0,
|
|
54
|
+
duration: 0,
|
|
55
|
+
useNativeDriver: true,
|
|
56
|
+
}),
|
|
57
|
+
]),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
animationRef.current = loop;
|
|
61
|
+
loop.start();
|
|
62
|
+
|
|
63
|
+
return () => {
|
|
64
|
+
loop.stop();
|
|
65
|
+
animationRef.current = null;
|
|
66
|
+
translateX.stopAnimation();
|
|
67
|
+
};
|
|
68
|
+
}, [shouldMarquee, contentWidth, containerWidth, text, translateX]);
|
|
69
|
+
|
|
70
|
+
const handleContainerLayout = event => {
|
|
71
|
+
const nextWidth = Math.round(event?.nativeEvent?.layout?.width || 0);
|
|
72
|
+
setContainerWidth(prev => (prev === nextWidth ? prev : nextWidth));
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleTextLayout = event => {
|
|
76
|
+
const nextWidth = Math.round(event?.nativeEvent?.layout?.width || 0);
|
|
77
|
+
setContentWidth(prev => (prev === nextWidth ? prev : nextWidth));
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const textStyle = [
|
|
81
|
+
style,
|
|
82
|
+
{
|
|
83
|
+
color,
|
|
84
|
+
flex: undefined,
|
|
85
|
+
flexShrink: 0,
|
|
86
|
+
flexGrow: 0,
|
|
87
|
+
},
|
|
88
|
+
shouldMarquee ? {textAlign: 'left'} : {textAlign: 'center'},
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
return React.createElement(
|
|
92
|
+
View,
|
|
93
|
+
{
|
|
94
|
+
testID,
|
|
95
|
+
style: {flex: 1, overflow: 'hidden', justifyContent: 'center'},
|
|
96
|
+
onLayout: handleContainerLayout,
|
|
97
|
+
},
|
|
98
|
+
React.createElement(
|
|
99
|
+
Animated.View,
|
|
100
|
+
{
|
|
101
|
+
style: {
|
|
102
|
+
flexDirection: 'row',
|
|
103
|
+
alignItems: 'center',
|
|
104
|
+
opacity,
|
|
105
|
+
transform: [{translateX}],
|
|
106
|
+
alignSelf: shouldMarquee ? 'flex-start' : 'stretch',
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
React.createElement(
|
|
110
|
+
Text,
|
|
111
|
+
{
|
|
112
|
+
numberOfLines: 1,
|
|
113
|
+
ellipsizeMode: 'clip',
|
|
114
|
+
onLayout: handleTextLayout,
|
|
115
|
+
style: textStyle,
|
|
116
|
+
},
|
|
117
|
+
text,
|
|
118
|
+
),
|
|
119
|
+
shouldMarquee
|
|
120
|
+
? React.createElement(
|
|
121
|
+
Text,
|
|
122
|
+
{
|
|
123
|
+
numberOfLines: 1,
|
|
124
|
+
ellipsizeMode: 'clip',
|
|
125
|
+
accessible: false,
|
|
126
|
+
importantForAccessibility: 'no',
|
|
127
|
+
style: [...textStyle, {marginLeft: GAP_PX}],
|
|
128
|
+
},
|
|
129
|
+
text,
|
|
130
|
+
)
|
|
131
|
+
: null,
|
|
132
|
+
),
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
module.exports = RuntimeFooterMarqueeText;
|
|
137
|
+
module.exports.default = RuntimeFooterMarqueeText;
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
getRuntimeFooterTextLines,
|
|
22
22
|
} from '@controleonline/ui-common/src/react/utils/runtimeFooter';
|
|
23
23
|
import styles from './RuntimeInfoFooter.styles';
|
|
24
|
+
import RuntimeFooterMarqueeText from './RuntimeFooterMarqueeText';
|
|
24
25
|
|
|
25
26
|
const ROTATION_INTERVAL_MS = 4000;
|
|
26
27
|
const FADE_DURATION_MS = 260;
|
|
@@ -256,21 +257,17 @@ const RuntimeInfoFooter = ({
|
|
|
256
257
|
]}
|
|
257
258
|
/>
|
|
258
259
|
</View>
|
|
259
|
-
<
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
},
|
|
271
|
-
]}>
|
|
272
|
-
{showDebugInfo ? inlineText || primaryText || device?.id || '--' : displayedText}
|
|
273
|
-
</Animated.Text>
|
|
260
|
+
<RuntimeFooterMarqueeText
|
|
261
|
+
text={
|
|
262
|
+
showDebugInfo
|
|
263
|
+
? inlineText || primaryText || device?.id || '--'
|
|
264
|
+
: displayedText
|
|
265
|
+
}
|
|
266
|
+
color={textColor}
|
|
267
|
+
opacity={showDebugInfo ? 1 : fadeOpacity}
|
|
268
|
+
style={styles.primaryText}
|
|
269
|
+
testID="runtime-footer-primary-text"
|
|
270
|
+
/>
|
|
274
271
|
<View style={styles.loadingWrap}>
|
|
275
272
|
{hasStoreLoading && (
|
|
276
273
|
<ActivityIndicator color={loadingColor} size="small" />
|
|
@@ -1,29 +1,49 @@
|
|
|
1
|
-
import React, {useMemo} from 'react';
|
|
2
|
-
import {Text, View} from 'react-native';
|
|
3
|
-
import Icon from 'react-native-vector-icons/MaterialIcons';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
import React, {useMemo} from 'react';
|
|
2
|
+
import {Text, View} from 'react-native';
|
|
3
|
+
import Icon from 'react-native-vector-icons/MaterialIcons';
|
|
4
|
+
import {useStore} from '@store';
|
|
5
|
+
|
|
6
|
+
import createStyles from './SystemErrorToast.styles';
|
|
7
|
+
import {resolveSystemErrorMessage} from '@controleonline/ui-common/src/react/utils/systemErrorMessage';
|
|
8
|
+
|
|
9
|
+
const SystemErrorToast = ({error}) => {
|
|
10
|
+
const themeStore = useStore('theme');
|
|
11
|
+
const themeColors = themeStore?.getters?.colors || {};
|
|
12
|
+
|
|
13
|
+
const palette = useMemo(
|
|
14
|
+
() => ({
|
|
15
|
+
toastDangerBackground: themeColors.toastDangerBackground,
|
|
16
|
+
toastDangerBorder: themeColors.toastDangerBorder,
|
|
17
|
+
toastDangerIcon: themeColors.toastDangerIcon,
|
|
18
|
+
toastDangerText: themeColors.toastDangerText,
|
|
19
|
+
}),
|
|
20
|
+
[
|
|
21
|
+
themeColors.toastDangerBackground,
|
|
22
|
+
themeColors.toastDangerBorder,
|
|
23
|
+
themeColors.toastDangerIcon,
|
|
24
|
+
themeColors.toastDangerText,
|
|
25
|
+
],
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const styles = useMemo(() => createStyles(palette), [palette]);
|
|
29
|
+
const message = useMemo(() => resolveSystemErrorMessage(error), [error]);
|
|
30
|
+
|
|
31
|
+
if (!message) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<View style={styles.container}>
|
|
37
|
+
<View style={styles.header}>
|
|
38
|
+
<View style={styles.iconWrap}>
|
|
39
|
+
<Icon name="error-outline" size={16} color={palette.toastDangerIcon} />
|
|
40
|
+
</View>
|
|
41
|
+
</View>
|
|
42
|
+
<View style={styles.copyWrap}>
|
|
43
|
+
<Text style={styles.text}>{message}</Text>
|
|
44
|
+
</View>
|
|
45
|
+
</View>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default SystemErrorToast;
|
|
@@ -1,38 +1,43 @@
|
|
|
1
|
-
import {StyleSheet} from 'react-native';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1
|
+
import {StyleSheet} from 'react-native';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Theme-aware styles for SystemErrorToast.
|
|
5
|
+
* Colors must come from themeStore.getters.colors (canonical tokens only).
|
|
6
|
+
*/
|
|
7
|
+
const createStyles = palette =>
|
|
8
|
+
StyleSheet.create({
|
|
9
|
+
container: {
|
|
10
|
+
flex: 1,
|
|
11
|
+
flexDirection: 'row',
|
|
12
|
+
alignItems: 'flex-start',
|
|
13
|
+
gap: 12,
|
|
14
|
+
paddingVertical: 2,
|
|
15
|
+
},
|
|
16
|
+
header: {
|
|
17
|
+
paddingTop: 1,
|
|
18
|
+
},
|
|
19
|
+
iconWrap: {
|
|
20
|
+
width: 26,
|
|
21
|
+
height: 26,
|
|
22
|
+
borderRadius: 13,
|
|
23
|
+
backgroundColor: palette.toastDangerBackground,
|
|
24
|
+
alignItems: 'center',
|
|
25
|
+
justifyContent: 'center',
|
|
26
|
+
flexShrink: 0,
|
|
27
|
+
borderWidth: 1,
|
|
28
|
+
borderColor: palette.toastDangerBorder,
|
|
29
|
+
},
|
|
30
|
+
copyWrap: {
|
|
31
|
+
flex: 1,
|
|
32
|
+
minWidth: 0,
|
|
33
|
+
paddingTop: 2,
|
|
34
|
+
},
|
|
35
|
+
text: {
|
|
36
|
+
color: palette.toastDangerText,
|
|
37
|
+
fontSize: 13,
|
|
38
|
+
fontWeight: '700',
|
|
39
|
+
lineHeight: 20,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export default createStyles;
|