@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
|
@@ -5,6 +5,15 @@ import {getGravatarUrl, getUserInitials} from '../utils/userAvatar';
|
|
|
5
5
|
|
|
6
6
|
const normalizeUrl = value => String(value || '').trim();
|
|
7
7
|
|
|
8
|
+
const isGravatarUrl = uri =>
|
|
9
|
+
/gravatar\.com\/avatar\//i.test(String(uri || ''));
|
|
10
|
+
|
|
11
|
+
const isBackendDownloadUrl = uri =>
|
|
12
|
+
/\/files\/[^/?#]+\/download/i.test(String(uri || ''));
|
|
13
|
+
|
|
14
|
+
/** Session cache: gravatar URL → true (ok) | false (missing). Avoids repeat probes. */
|
|
15
|
+
const gravatarProbeCache = new Map();
|
|
16
|
+
|
|
8
17
|
const readSessionToken = () => {
|
|
9
18
|
if (typeof localStorage === 'undefined' || !localStorage?.getItem) {
|
|
10
19
|
return '';
|
|
@@ -18,51 +27,119 @@ const readSessionToken = () => {
|
|
|
18
27
|
}
|
|
19
28
|
};
|
|
20
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Probe Gravatar (d=404) via fetch BEFORE exposing URL to <Image>.
|
|
32
|
+
* Browser "Failed to load resource" for <img> 404 is avoided; XHR 404 is
|
|
33
|
+
* expected once per unique hash and cached for the session.
|
|
34
|
+
*/
|
|
35
|
+
const probeGravatar = async url => {
|
|
36
|
+
if (gravatarProbeCache.has(url)) {
|
|
37
|
+
return gravatarProbeCache.get(url);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
let response = await fetch(url, {
|
|
42
|
+
method: 'HEAD',
|
|
43
|
+
mode: 'cors',
|
|
44
|
+
cache: 'force-cache',
|
|
45
|
+
});
|
|
46
|
+
if (response.status === 405 || response.status === 501) {
|
|
47
|
+
response = await fetch(url, {
|
|
48
|
+
method: 'GET',
|
|
49
|
+
mode: 'cors',
|
|
50
|
+
cache: 'force-cache',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const ok = response.ok;
|
|
54
|
+
gravatarProbeCache.set(url, ok);
|
|
55
|
+
return ok;
|
|
56
|
+
} catch {
|
|
57
|
+
gravatarProbeCache.set(url, false);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolve display URI.
|
|
64
|
+
* - Backend /files/.../download on web: authenticated blob fetch.
|
|
65
|
+
* - Gravatar (d=404) on web: probe first; never set <img src> on expected 404.
|
|
66
|
+
* - Other URLs / native: pass through (native Image onError handles 404).
|
|
67
|
+
*/
|
|
21
68
|
const useDisplayUri = uri => {
|
|
22
|
-
const [displayUri, setDisplayUri] = useState(
|
|
69
|
+
const [displayUri, setDisplayUri] = useState('');
|
|
70
|
+
const [resolved, setResolved] = useState(false);
|
|
23
71
|
|
|
24
72
|
useEffect(() => {
|
|
25
73
|
let cancelled = false;
|
|
26
74
|
let objectUrl = '';
|
|
27
75
|
const next = normalizeUrl(uri);
|
|
28
76
|
|
|
77
|
+
setResolved(false);
|
|
78
|
+
setDisplayUri('');
|
|
79
|
+
|
|
29
80
|
const run = async () => {
|
|
30
81
|
if (!next) {
|
|
31
|
-
|
|
82
|
+
if (!cancelled) {
|
|
83
|
+
setDisplayUri('');
|
|
84
|
+
setResolved(true);
|
|
85
|
+
}
|
|
32
86
|
return;
|
|
33
87
|
}
|
|
34
88
|
|
|
35
|
-
const isBackendDownload = /\/files\/[^/?#]+\/download/i.test(next);
|
|
36
89
|
const token = readSessionToken();
|
|
37
90
|
|
|
38
|
-
|
|
39
|
-
|
|
91
|
+
// Authenticated backend download on web
|
|
92
|
+
if (isBackendDownloadUrl(next) && Platform.OS === 'web' && token) {
|
|
93
|
+
try {
|
|
94
|
+
const headers = {
|
|
95
|
+
Accept: '*/*',
|
|
96
|
+
'API-TOKEN': token,
|
|
97
|
+
};
|
|
98
|
+
const host =
|
|
99
|
+
normalizeUrl(APP_ENV?.DOMAIN) ||
|
|
100
|
+
(typeof location !== 'undefined' ? location.host : '');
|
|
101
|
+
if (host) {
|
|
102
|
+
headers['App-Domain'] = host;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const response = await fetch(next, {method: 'GET', headers});
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
if (!cancelled) {
|
|
108
|
+
setDisplayUri('');
|
|
109
|
+
setResolved(true);
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const blob = await response.blob();
|
|
115
|
+
objectUrl = URL.createObjectURL(blob);
|
|
116
|
+
if (!cancelled) {
|
|
117
|
+
setDisplayUri(objectUrl);
|
|
118
|
+
setResolved(true);
|
|
119
|
+
}
|
|
120
|
+
} catch {
|
|
121
|
+
if (!cancelled) {
|
|
122
|
+
setDisplayUri('');
|
|
123
|
+
setResolved(true);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
40
126
|
return;
|
|
41
127
|
}
|
|
42
128
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
normalizeUrl(APP_ENV?.DOMAIN) ||
|
|
50
|
-
(typeof location !== 'undefined' ? location.host : '');
|
|
51
|
-
if (host) {
|
|
52
|
-
headers['App-Domain'] = host;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const response = await fetch(next, {method: 'GET', headers});
|
|
56
|
-
if (!response.ok) {
|
|
57
|
-
if (!cancelled) setDisplayUri(next);
|
|
58
|
-
return;
|
|
129
|
+
// Gravatar d=404: probe on web so <img> never receives a known-missing URL
|
|
130
|
+
if (isGravatarUrl(next) && Platform.OS === 'web') {
|
|
131
|
+
const ok = await probeGravatar(next);
|
|
132
|
+
if (!cancelled) {
|
|
133
|
+
setDisplayUri(ok ? next : '');
|
|
134
|
+
setResolved(true);
|
|
59
135
|
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
60
138
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (!cancelled) setDisplayUri(next);
|
|
139
|
+
// Default: use as-is (native, or non-special web URLs)
|
|
140
|
+
if (!cancelled) {
|
|
141
|
+
setDisplayUri(next);
|
|
142
|
+
setResolved(true);
|
|
66
143
|
}
|
|
67
144
|
};
|
|
68
145
|
|
|
@@ -74,7 +151,7 @@ const useDisplayUri = uri => {
|
|
|
74
151
|
};
|
|
75
152
|
}, [uri]);
|
|
76
153
|
|
|
77
|
-
return displayUri;
|
|
154
|
+
return {displayUri, resolved};
|
|
78
155
|
};
|
|
79
156
|
|
|
80
157
|
const UserAvatar = ({
|
|
@@ -104,7 +181,22 @@ const UserAvatar = ({
|
|
|
104
181
|
}, [sources]);
|
|
105
182
|
|
|
106
183
|
const currentSource = sources[sourceIndex];
|
|
107
|
-
const displayUri = useDisplayUri(currentSource);
|
|
184
|
+
const {displayUri, resolved} = useDisplayUri(currentSource);
|
|
185
|
+
|
|
186
|
+
// After probe resolves empty for gravatar/backend → try next source or initials
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
if (!currentSource || !resolved || displayUri) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (
|
|
192
|
+
isGravatarUrl(currentSource) ||
|
|
193
|
+
isBackendDownloadUrl(currentSource)
|
|
194
|
+
) {
|
|
195
|
+
setSourceIndex(index =>
|
|
196
|
+
index + 1 < sources.length ? index + 1 : index,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}, [currentSource, displayUri, resolved, sources.length]);
|
|
108
200
|
|
|
109
201
|
const containerStyle = [
|
|
110
202
|
styles.container,
|