@controleonline/ui-common 1.2.70
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/.github/agents/developer.agent.md +30 -0
- package/.github/agents/devops.agent.md +30 -0
- package/.github/agents/qa.agent.md +30 -0
- package/.github/agents/security.agent.md +30 -0
- package/.github/workflows/npmjs.yml +35 -0
- package/.github/workflows/pull-request-checks.yml +19 -0
- package/.scrutinizer.yml +24 -0
- package/FUNDING.yml +1 -0
- package/package.json +28 -0
- package/src/api/fetch.js +74 -0
- package/src/api/index.js +155 -0
- package/src/api/localDB.js +131 -0
- package/src/api/queue.js +66 -0
- package/src/react/assets/tap.mp3 +0 -0
- package/src/react/components/AddImportModal.js +157 -0
- package/src/react/components/AddImportModal.styles.js +81 -0
- package/src/react/components/BackgroundRuntimeBridge.js +219 -0
- package/src/react/components/BottomNavigationBar.config.js +238 -0
- package/src/react/components/BottomNavigationBar.js +120 -0
- package/src/react/components/BottomNavigationBar.styles.js +110 -0
- package/src/react/components/CategoryForm.js +572 -0
- package/src/react/components/CategoryForm.styles.js +334 -0
- package/src/react/components/ConfirmModal.js +24 -0
- package/src/react/components/ConfirmModal.styles.js +43 -0
- package/src/react/components/ContextHelpButton.js +69 -0
- package/src/react/components/ContextHelpButton.styles.js +62 -0
- package/src/react/components/DefaultProvider.native.js +848 -0
- package/src/react/components/DefaultProvider.styles.js +21 -0
- package/src/react/components/DefaultProvider.web.js +909 -0
- package/src/react/components/DeliveryPushBridge.native.js +314 -0
- package/src/react/components/DeliveryPushBridge.web.js +5 -0
- package/src/react/components/DeviceAlertSoundService.js +499 -0
- package/src/react/components/EntityLogContent.js +909 -0
- package/src/react/components/EntityLogContent.styles.js +400 -0
- package/src/react/components/EventBus.js +12 -0
- package/src/react/components/KioskModeBridge.js +65 -0
- package/src/react/components/LauncherModeBridge.js +47 -0
- package/src/react/components/LinkedOrderProductsTab.js +388 -0
- package/src/react/components/LinkedOrderProductsTab.styles.js +180 -0
- package/src/react/components/ManagerPushBridge.native.js +345 -0
- package/src/react/components/MessageService.js +314 -0
- package/src/react/components/Paywall/Google.js +89 -0
- package/src/react/components/PrintService.js +778 -0
- package/src/react/components/ProductCatalogCacheService.js +45 -0
- package/src/react/components/RemoteCheckoutService.js +292 -0
- package/src/react/components/RuntimeBottomNavigationBar.js +151 -0
- package/src/react/components/RuntimeInfoFooter.js +214 -0
- package/src/react/components/RuntimeInfoFooter.styles.js +56 -0
- package/src/react/components/SystemErrorToast.js +29 -0
- package/src/react/components/SystemErrorToast.styles.js +38 -0
- package/src/react/components/TouchFeedbackProvider.js +65 -0
- package/src/react/components/UnifiedPaymentBar.js +155 -0
- package/src/react/components/UnifiedPaymentBar.styles.js +174 -0
- package/src/react/components/WebsocketListener.native.js +487 -0
- package/src/react/components/WebsocketListener.web.js +102 -0
- package/src/react/components/inputs/IdInput.js +49 -0
- package/src/react/components/inputs/IdInput.styles.js +26 -0
- package/src/react/components/inputs/InvoiceDueDateFilterInput.js +74 -0
- package/src/react/components/inputs/InvoiceDueDateFilterInput.styles.js +34 -0
- package/src/react/components/inputs/InvoiceDueDateRangeFilterInput.js +115 -0
- package/src/react/components/inputs/InvoiceDueDateRangeFilterInput.styles.js +39 -0
- package/src/react/components/lists/CategoriesList.js +56 -0
- package/src/react/components/lists/PaymentTypeList.js +55 -0
- package/src/react/components/lists/ReceiverList.js +56 -0
- package/src/react/components/lists/SelectList.styles.js +18 -0
- package/src/react/components/lists/StatusList.js +55 -0
- package/src/react/components/lists/WalletList.js +54 -0
- package/src/react/components/radio/Spotify.js +144 -0
- package/src/react/components/radio/Spotify.styles.js +32 -0
- package/src/react/components/radio/YouTube.js +82 -0
- package/src/react/components/radio/YouTube.styles.js +16 -0
- package/src/react/components/toastConfig.js +16 -0
- package/src/react/config/deviceConfigBootstrap.js +571 -0
- package/src/react/index.js +4 -0
- package/src/react/localStorage.js +30 -0
- package/src/react/pages/EntityLogPage.js +124 -0
- package/src/react/pages/EntityLogPage.styles.js +17 -0
- package/src/react/pages/GenericLogPage.js +760 -0
- package/src/react/pages/GenericLogPage.styles.js +401 -0
- package/src/react/pages/Imports.js +361 -0
- package/src/react/pages/Imports.styles.js +223 -0
- package/src/react/pages/SettingsPage/PaymentTypesByWalletTab.js +462 -0
- package/src/react/pages/SettingsPage/index.js +1171 -0
- package/src/react/pages/SettingsPage/index.styles.js +122 -0
- package/src/react/print/jobs.js +147 -0
- package/src/react/print/providers/local.js +53 -0
- package/src/react/print/providers/remote.js +81 -0
- package/src/react/print/selection.js +296 -0
- package/src/react/print/usePrintButtonController.js +474 -0
- package/src/react/router/routes.js +42 -0
- package/src/react/services/NetworkPrinterService.native.js +391 -0
- package/src/react/services/NetworkPrinterService.web.js +15 -0
- package/src/react/stores/index.js +128 -0
- package/src/react/stores/storeErrorBridge.js +36 -0
- package/src/react/utils/backgroundRuntimeRegistration.js +69 -0
- package/src/react/utils/cashPayment.js +87 -0
- package/src/react/utils/commercialDocumentOrders.js +269 -0
- package/src/react/utils/dateRangeFilter.js +264 -0
- package/src/react/utils/deviceRuntime.js +193 -0
- package/src/react/utils/entityDisplay.js +231 -0
- package/src/react/utils/entityLog.js +649 -0
- package/src/react/utils/fileUrl.js +102 -0
- package/src/react/utils/frontendDebugLog.js +45 -0
- package/src/react/utils/googleMapsConfig.js +49 -0
- package/src/react/utils/importStatus.js +100 -0
- package/src/react/utils/invoiceDueDateMessages.js +14 -0
- package/src/react/utils/invoicePresentation.js +46 -0
- package/src/react/utils/logSettings.js +143 -0
- package/src/react/utils/maintenanceSettings.js +53 -0
- package/src/react/utils/managerOrderNotifications.js +523 -0
- package/src/react/utils/mapNavigation.js +76 -0
- package/src/react/utils/menuCatalogConfig.js +8 -0
- package/src/react/utils/menuCatalogDownload.js +157 -0
- package/src/react/utils/networkCameraDevices.js +175 -0
- package/src/react/utils/networkDeviceProfiles.js +135 -0
- package/src/react/utils/normalizedCatalogDownload.js +157 -0
- package/src/react/utils/notificationNavigation.js +94 -0
- package/src/react/utils/notificationSound.js +42 -0
- package/src/react/utils/paymentDevices.js +397 -0
- package/src/react/utils/paymentGatewayExecution.js +91 -0
- package/src/react/utils/paymentOptions.js +88 -0
- package/src/react/utils/peopleDisplay.js +39 -0
- package/src/react/utils/printerDevices.js +465 -0
- package/src/react/utils/remotePayment.js +61 -0
- package/src/react/utils/runtimeFooter.js +475 -0
- package/src/react/utils/runtimeLanguage.js +50 -0
- package/src/react/utils/runtimeMenu.js +224 -0
- package/src/react/utils/screenMetrics.js +30 -0
- package/src/react/utils/shopConfig.js +294 -0
- package/src/react/utils/shopFranchises.js +114 -0
- package/src/react/utils/socketRuntimePipeline.js +454 -0
- package/src/react/utils/spoolAckTracker.js +77 -0
- package/src/react/utils/storeColumns.js +427 -0
- package/src/react/utils/systemErrorChannel.js +109 -0
- package/src/react/utils/systemErrorMessage.js +67 -0
- package/src/react/utils/toastPresentation.js +53 -0
- package/src/react/utils/websocketSharedRuntime.js +826 -0
- package/src/store/acl/getters.js +4 -0
- package/src/store/acl/index.js +19 -0
- package/src/store/acl/mutation_types.js +4 -0
- package/src/store/acl/mutations.js +17 -0
- package/src/store/address/city.js +22 -0
- package/src/store/categories/category_file/index.js +25 -0
- package/src/store/categories/index.js +93 -0
- package/src/store/configs/customActions.js +131 -0
- package/src/store/configs/index.js +25 -0
- package/src/store/connections/customActions.js +26 -0
- package/src/store/connections/index.js +124 -0
- package/src/store/device/index.js +22 -0
- package/src/store/device_config/customActions.js +190 -0
- package/src/store/device_config/index.js +28 -0
- package/src/store/entity_log/customActions.js +63 -0
- package/src/store/entity_log/index.js +25 -0
- package/src/store/extra/data.js +85 -0
- package/src/store/extra/fields.js +88 -0
- package/src/store/file/actions.js +27 -0
- package/src/store/file/index.js +24 -0
- package/src/store/imports/index.js +88 -0
- package/src/store/print/customActions.js +251 -0
- package/src/store/print/index.js +28 -0
- package/src/store/printer/customActions.js +54 -0
- package/src/store/printer/index.js +26 -0
- package/src/store/runtime_debug/customActions.js +71 -0
- package/src/store/runtime_debug/index.js +30 -0
- package/src/store/status/index.js +65 -0
- package/src/store/timezones/index.js +43 -0
- package/src/store/websocket/customActions.js +24 -0
- package/src/store/websocket/index.js +28 -0
- package/src/tests/react/components/LauncherModeBridge.test.js +93 -0
- package/src/tests/react/config/deviceConfigBootstrap.test.js +337 -0
- package/src/tests/react/print/localPrintProvider.test.js +45 -0
- package/src/tests/react/utils/backgroundRuntimeRegistration.test.js +123 -0
- package/src/tests/react/utils/cashPayment.test.js +41 -0
- package/src/tests/react/utils/dateRangeFilter.test.mjs +29 -0
- package/src/tests/react/utils/entityDisplay.test.js +18 -0
- package/src/tests/react/utils/googleMapsConfig.test.js +32 -0
- package/src/tests/react/utils/importStatus.test.js +85 -0
- package/src/tests/react/utils/invoicePresentation.test.mjs +40 -0
- package/src/tests/react/utils/managerOrderNotifications.test.js +127 -0
- package/src/tests/react/utils/mapNavigation.test.js +58 -0
- package/src/tests/react/utils/notificationNavigation.test.js +39 -0
- package/src/tests/react/utils/notificationSound.test.js +27 -0
- package/src/tests/react/utils/paymentDevices.test.js +88 -0
- package/src/tests/react/utils/runtimeFooter.test.js +429 -0
- package/src/tests/react/utils/runtimeLanguage.test.js +80 -0
- package/src/tests/react/utils/runtimeMenu.test.js +174 -0
- package/src/tests/react/utils/shopFranchises.test.js +82 -0
- package/src/tests/react/utils/socketRuntimePipeline.test.js +124 -0
- package/src/tests/react/utils/spoolAckTracker.test.js +40 -0
- package/src/tests/react/utils/storeErrorBridge.test.js +66 -0
- package/src/tests/react/utils/systemErrorChannel.test.js +58 -0
- package/src/tests/react/utils/systemErrorMessage.test.js +59 -0
- package/src/tests/react/utils/toastPresentation.test.js +75 -0
- package/src/tests/react/utils/translate.test.mjs +396 -0
- package/src/tests/react/utils/websocketSharedRuntime.test.js +245 -0
- package/src/utils/apiEntryPoint.js +31 -0
- package/src/utils/appDomain.js +50 -0
- package/src/utils/config.js +14 -0
- package/src/utils/formatter.js +419 -0
- package/src/utils/integrationConfigs.js +79 -0
- package/src/utils/methods.js +23 -0
- package/src/utils/oauth.js +42 -0
- package/src/utils/translate.js +620 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import { getAllStores } from '@store';
|
|
2
|
+
import { formatHumanLabel } from '@controleonline/ui-common/src/react/utils/entityDisplay';
|
|
3
|
+
|
|
4
|
+
const normalizeText = value => String(value || '').trim();
|
|
5
|
+
|
|
6
|
+
const singularizeResourceName = resourceName => {
|
|
7
|
+
const normalized = normalizeText(resourceName).replace(/^\/+|\/+$/g, '');
|
|
8
|
+
if (!normalized) {
|
|
9
|
+
return '';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (normalized.endsWith('ies')) {
|
|
13
|
+
return `${normalized.slice(0, -3)}y`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (normalized.endsWith('sses') || normalized.endsWith('ses')) {
|
|
17
|
+
return normalized.slice(0, -2);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (normalized.endsWith('s') && !normalized.endsWith('ss')) {
|
|
21
|
+
return normalized.slice(0, -1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return normalized;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const normalizeKey = value =>
|
|
28
|
+
normalizeText(value)
|
|
29
|
+
.replace(/[^a-zA-Z0-9]/g, '')
|
|
30
|
+
.toLowerCase();
|
|
31
|
+
|
|
32
|
+
const extractEntityResource = entityIri => {
|
|
33
|
+
const normalizedIri = normalizeText(entityIri);
|
|
34
|
+
if (!normalizedIri) {
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const segments = normalizedIri.split('/').filter(Boolean);
|
|
39
|
+
if (segments.length < 2) {
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return normalizeText(segments[segments.length - 2]);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const getStoreColumns = store => {
|
|
47
|
+
const columns = store?.getters?.columns || store?.columns;
|
|
48
|
+
return Array.isArray(columns) ? columns.filter(Boolean) : [];
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getStoreEndpoint = store =>
|
|
52
|
+
normalizeText(store?.getters?.resourceEndpoint || store?.resourceEndpoint).replace(
|
|
53
|
+
/^\/+|\/+$/g,
|
|
54
|
+
'',
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const normalizeComparableValue = value => {
|
|
58
|
+
if (value === null || value === undefined || value === '') {
|
|
59
|
+
return '';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
63
|
+
return normalizeComparableValue(
|
|
64
|
+
value?.value ?? value?.['@id'] ?? value?.id ?? value?.label ?? '',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return normalizeText(value);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const buildRowContext = (row, fieldName, value) => {
|
|
72
|
+
if (!fieldName) {
|
|
73
|
+
return row || {};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
77
|
+
return {[fieldName]: value};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
...row,
|
|
82
|
+
[fieldName]: value,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const resolveListValue = ({column, value, row}) => {
|
|
87
|
+
if (!Array.isArray(column?.list) || !column.list.length) {
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const comparableValue = normalizeComparableValue(value);
|
|
92
|
+
const matchedItem = column.list.find(item => {
|
|
93
|
+
const currentValue =
|
|
94
|
+
item?.value ?? item?.['@id'] ?? item?.id ?? item?.label ?? item;
|
|
95
|
+
|
|
96
|
+
return normalizeComparableValue(currentValue) === comparableValue;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (!matchedItem) {
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (typeof column.formatList === 'function') {
|
|
104
|
+
const formattedItem = column.formatList(matchedItem, column, row);
|
|
105
|
+
if (formattedItem && typeof formattedItem === 'object' && !Array.isArray(formattedItem)) {
|
|
106
|
+
return formattedItem?.label ?? formattedItem?.value ?? matchedItem?.label ?? value;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return formattedItem;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return matchedItem?.label ?? value;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const normalizeFormattedValue = value => {
|
|
116
|
+
if (value === null || value === undefined) {
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
121
|
+
return value?.label ?? value?.value ?? value?.name ?? value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return value;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const DEFAULT_TRANSLATABLE_FIELDS = new Set([
|
|
128
|
+
'active',
|
|
129
|
+
'app',
|
|
130
|
+
'channel',
|
|
131
|
+
'displaytype',
|
|
132
|
+
'featured',
|
|
133
|
+
'frequency',
|
|
134
|
+
'invoicetype',
|
|
135
|
+
'installments',
|
|
136
|
+
'ordertype',
|
|
137
|
+
'peopletype',
|
|
138
|
+
'pricecalculation',
|
|
139
|
+
'productcondition',
|
|
140
|
+
'realstatus',
|
|
141
|
+
'status',
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
const shouldTranslateColumnValue = ({column, storeName, value}) => {
|
|
145
|
+
if (column?.translate === false || typeof value !== 'string' || !normalizeText(storeName)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (column?.translate === true || Array.isArray(column?.list)) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const candidates = [column?.key, column?.name, column?.label]
|
|
154
|
+
.map(normalizeKey)
|
|
155
|
+
.filter(Boolean);
|
|
156
|
+
|
|
157
|
+
return candidates.some(candidate => DEFAULT_TRANSLATABLE_FIELDS.has(candidate));
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const translateColumnValue = ({column, storeName, value}) => {
|
|
161
|
+
if (!shouldTranslateColumnValue({column, storeName, value})) {
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const translateFn =
|
|
166
|
+
typeof globalThis !== 'undefined' && typeof globalThis?.t?.t === 'function'
|
|
167
|
+
? globalThis.t.t.bind(globalThis.t)
|
|
168
|
+
: null;
|
|
169
|
+
|
|
170
|
+
return translateFn ? translateFn(storeName, 'span', value) : value;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const translateColumnLabel = ({column, fieldName, storeName, fallbackLabel = ''}) => {
|
|
174
|
+
if (!normalizeText(storeName)) {
|
|
175
|
+
return fallbackLabel;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const translateFn =
|
|
179
|
+
typeof globalThis !== 'undefined' && typeof globalThis?.t?.t === 'function'
|
|
180
|
+
? globalThis.t.t.bind(globalThis.t)
|
|
181
|
+
: null;
|
|
182
|
+
if (!translateFn) {
|
|
183
|
+
return fallbackLabel;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const candidates = [
|
|
187
|
+
normalizeText(fieldName),
|
|
188
|
+
normalizeText(column?.name),
|
|
189
|
+
normalizeText(column?.key),
|
|
190
|
+
normalizeText(column?.label),
|
|
191
|
+
].filter(Boolean);
|
|
192
|
+
|
|
193
|
+
for (const candidate of candidates) {
|
|
194
|
+
const translated = translateFn(storeName, 'label', candidate);
|
|
195
|
+
if (normalizeText(translated) && normalizeText(translated) !== formatHumanLabel(candidate)) {
|
|
196
|
+
return translated;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return fallbackLabel;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export const resolveStoreConfigByName = storeName => {
|
|
204
|
+
const normalizedStoreName = normalizeText(storeName);
|
|
205
|
+
const stores = getAllStores();
|
|
206
|
+
const store = stores?.[normalizedStoreName];
|
|
207
|
+
const resourceEndpoint = getStoreEndpoint(store);
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
columns: getStoreColumns(store),
|
|
211
|
+
resourceEndpoint,
|
|
212
|
+
resourceName: resourceEndpoint,
|
|
213
|
+
store,
|
|
214
|
+
storeName: normalizedStoreName,
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export const resolveStoreConfigByClassName = className => {
|
|
219
|
+
const normalizedClassKey = normalizeKey(
|
|
220
|
+
String(className || '').split('\\').filter(Boolean).pop(),
|
|
221
|
+
);
|
|
222
|
+
if (!normalizedClassKey) {
|
|
223
|
+
return {
|
|
224
|
+
columns: [],
|
|
225
|
+
resourceEndpoint: '',
|
|
226
|
+
resourceName: '',
|
|
227
|
+
store: null,
|
|
228
|
+
storeName: '',
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const stores = getAllStores();
|
|
233
|
+
const candidates = Object.entries(stores || {})
|
|
234
|
+
.map(([storeName, store]) => {
|
|
235
|
+
const resourceEndpoint = getStoreEndpoint(store);
|
|
236
|
+
const normalizedEndpoint = singularizeResourceName(resourceEndpoint);
|
|
237
|
+
const columns = getStoreColumns(store);
|
|
238
|
+
const endpointKey = normalizeKey(normalizedEndpoint);
|
|
239
|
+
const resourceKey = normalizeKey(resourceEndpoint);
|
|
240
|
+
const storeKey = normalizeKey(storeName);
|
|
241
|
+
|
|
242
|
+
let score = 0;
|
|
243
|
+
if (endpointKey === normalizedClassKey) score += 4;
|
|
244
|
+
if (storeKey === normalizedClassKey) score += 3;
|
|
245
|
+
if (resourceKey === normalizedClassKey) score += 2;
|
|
246
|
+
if (!score) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (columns.length) score += 1;
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
columns,
|
|
254
|
+
resourceEndpoint,
|
|
255
|
+
resourceName: resourceEndpoint,
|
|
256
|
+
score,
|
|
257
|
+
store,
|
|
258
|
+
storeName,
|
|
259
|
+
};
|
|
260
|
+
})
|
|
261
|
+
.filter(Boolean)
|
|
262
|
+
.sort((left, right) => {
|
|
263
|
+
if (right.score !== left.score) {
|
|
264
|
+
return right.score - left.score;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return right.columns.length - left.columns.length;
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
return candidates[0] || {
|
|
271
|
+
columns: [],
|
|
272
|
+
resourceEndpoint: '',
|
|
273
|
+
resourceName: '',
|
|
274
|
+
store: null,
|
|
275
|
+
storeName: '',
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
export const resolveStoreConfigByEntity = ({entity = null, entityIri = ''} = {}) => {
|
|
280
|
+
const resolvedEntityIri =
|
|
281
|
+
normalizeText(entity?.['@id']) || normalizeText(entityIri);
|
|
282
|
+
const resourceName = extractEntityResource(resolvedEntityIri);
|
|
283
|
+
if (!resourceName) {
|
|
284
|
+
return {
|
|
285
|
+
columns: [],
|
|
286
|
+
resourceEndpoint: '',
|
|
287
|
+
resourceName: '',
|
|
288
|
+
store: null,
|
|
289
|
+
storeName: '',
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const normalizedResourceName = singularizeResourceName(resourceName);
|
|
294
|
+
const stores = getAllStores();
|
|
295
|
+
|
|
296
|
+
const candidates = Object.entries(stores || {})
|
|
297
|
+
.map(([storeName, store]) => {
|
|
298
|
+
const resourceEndpoint = getStoreEndpoint(store);
|
|
299
|
+
const normalizedEndpoint = singularizeResourceName(resourceEndpoint);
|
|
300
|
+
const columns = getStoreColumns(store);
|
|
301
|
+
|
|
302
|
+
if (
|
|
303
|
+
!resourceEndpoint ||
|
|
304
|
+
(resourceEndpoint !== resourceName && normalizedEndpoint !== normalizedResourceName)
|
|
305
|
+
) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
let score = 0;
|
|
310
|
+
if (resourceEndpoint === resourceName) score += 4;
|
|
311
|
+
if (storeName === resourceName) score += 3;
|
|
312
|
+
if (storeName === normalizedResourceName) score += 2;
|
|
313
|
+
if (resourceEndpoint === storeName) score += 1;
|
|
314
|
+
if (columns.length) score += 1;
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
columns,
|
|
318
|
+
resourceEndpoint,
|
|
319
|
+
resourceName,
|
|
320
|
+
score,
|
|
321
|
+
store,
|
|
322
|
+
storeName,
|
|
323
|
+
};
|
|
324
|
+
})
|
|
325
|
+
.filter(Boolean)
|
|
326
|
+
.sort((left, right) => {
|
|
327
|
+
if (right.score !== left.score) {
|
|
328
|
+
return right.score - left.score;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return right.columns.length - left.columns.length;
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
return candidates[0] || {
|
|
335
|
+
columns: [],
|
|
336
|
+
resourceEndpoint: '',
|
|
337
|
+
resourceName,
|
|
338
|
+
store: null,
|
|
339
|
+
storeName: '',
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
export const resolveStoreColumn = (columns = [], fieldName = '') => {
|
|
344
|
+
const normalizedFieldName = normalizeKey(fieldName);
|
|
345
|
+
if (!normalizedFieldName) {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return columns.find(column => {
|
|
350
|
+
const candidateNames = [
|
|
351
|
+
column?.key,
|
|
352
|
+
column?.name,
|
|
353
|
+
column?.label,
|
|
354
|
+
]
|
|
355
|
+
.map(normalizeKey)
|
|
356
|
+
.filter(Boolean);
|
|
357
|
+
|
|
358
|
+
return candidateNames.includes(normalizedFieldName);
|
|
359
|
+
}) || null;
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
export const formatStoreColumnLabel = ({
|
|
363
|
+
columns = [],
|
|
364
|
+
fieldName = '',
|
|
365
|
+
fallbackLabel = '',
|
|
366
|
+
storeName = '',
|
|
367
|
+
} = {}) => {
|
|
368
|
+
const column = resolveStoreColumn(columns, fieldName);
|
|
369
|
+
if (!column?.label) {
|
|
370
|
+
return fallbackLabel;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const normalizedLabel = normalizeText(column.label);
|
|
374
|
+
if (!normalizedLabel) {
|
|
375
|
+
return fallbackLabel;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const directTranslatedLabel = translateColumnLabel({
|
|
379
|
+
column,
|
|
380
|
+
fallbackLabel,
|
|
381
|
+
fieldName,
|
|
382
|
+
storeName,
|
|
383
|
+
});
|
|
384
|
+
if (directTranslatedLabel) {
|
|
385
|
+
return formatHumanLabel(directTranslatedLabel) || directTranslatedLabel;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const resolvedLabel = translateColumnValue({
|
|
389
|
+
column,
|
|
390
|
+
storeName,
|
|
391
|
+
value: normalizedLabel,
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
return formatHumanLabel(resolvedLabel) || resolvedLabel;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// Replica a logica central de columns do store para usos fora das listagens padrao.
|
|
398
|
+
export const formatStoreColumnValue = ({
|
|
399
|
+
columns = [],
|
|
400
|
+
fieldName = '',
|
|
401
|
+
row = null,
|
|
402
|
+
storeName = '',
|
|
403
|
+
value,
|
|
404
|
+
} = {}) => {
|
|
405
|
+
const column = resolveStoreColumn(columns, fieldName);
|
|
406
|
+
if (!column) {
|
|
407
|
+
return undefined;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const rowContext = buildRowContext(row, fieldName, value);
|
|
411
|
+
const resolvedValue = resolveListValue({
|
|
412
|
+
column,
|
|
413
|
+
row: rowContext,
|
|
414
|
+
value,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
let formattedValue = resolvedValue;
|
|
418
|
+
if (typeof column.format === 'function') {
|
|
419
|
+
formattedValue = column.format(resolvedValue, column, rowContext, false);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return translateColumnValue({
|
|
423
|
+
column,
|
|
424
|
+
storeName,
|
|
425
|
+
value: normalizeFormattedValue(formattedValue),
|
|
426
|
+
});
|
|
427
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {resolveSystemErrorMessage} from './systemErrorMessage'
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_SYSTEM_ERROR_DEDUPE_WINDOW_MS = 1200
|
|
4
|
+
|
|
5
|
+
const listeners = new Set()
|
|
6
|
+
const pendingPayloads = []
|
|
7
|
+
|
|
8
|
+
let lastFingerprint = ''
|
|
9
|
+
let lastPublishedAt = 0
|
|
10
|
+
|
|
11
|
+
const resolveDedupeWindow = options => {
|
|
12
|
+
const value = Number(options?.dedupeWindowMs)
|
|
13
|
+
if (Number.isFinite(value) && value >= 0) {
|
|
14
|
+
return value
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return DEFAULT_SYSTEM_ERROR_DEDUPE_WINDOW_MS
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const getSystemErrorFingerprint = (error, options = {}) => {
|
|
21
|
+
const message = resolveSystemErrorMessage(error)
|
|
22
|
+
|
|
23
|
+
if (!message) {
|
|
24
|
+
return ''
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return JSON.stringify({
|
|
28
|
+
message,
|
|
29
|
+
position: String(options?.position || ''),
|
|
30
|
+
providerKey: String(options?.providerKey || ''),
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const shouldSkipDuplicate = (fingerprint, options = {}) => {
|
|
35
|
+
if (!fingerprint || options?.dedupe === false) {
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const now = Date.now()
|
|
40
|
+
const dedupeWindowMs = resolveDedupeWindow(options)
|
|
41
|
+
|
|
42
|
+
if (
|
|
43
|
+
lastFingerprint === fingerprint &&
|
|
44
|
+
now - lastPublishedAt < dedupeWindowMs
|
|
45
|
+
) {
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
lastFingerprint = fingerprint
|
|
50
|
+
lastPublishedAt = now
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const emitPayload = payload => {
|
|
55
|
+
if (listeners.size === 0) {
|
|
56
|
+
pendingPayloads.push(payload)
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
listeners.forEach(listener => listener(payload))
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const publishSystemError = (error, options = {}) => {
|
|
65
|
+
const message = resolveSystemErrorMessage(error)
|
|
66
|
+
|
|
67
|
+
if (!message) {
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const fingerprint =
|
|
72
|
+
options?.dedupeKey || getSystemErrorFingerprint(message, options)
|
|
73
|
+
|
|
74
|
+
if (shouldSkipDuplicate(fingerprint, options)) {
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return emitPayload({
|
|
79
|
+
error,
|
|
80
|
+
message,
|
|
81
|
+
options,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const subscribeSystemErrors = listener => {
|
|
86
|
+
if (typeof listener !== 'function') {
|
|
87
|
+
return () => {}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
listeners.add(listener)
|
|
91
|
+
|
|
92
|
+
if (pendingPayloads.length > 0) {
|
|
93
|
+
const queuedPayloads = pendingPayloads.splice(0, pendingPayloads.length)
|
|
94
|
+
queuedPayloads.forEach(payload => listener(payload))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return () => {
|
|
98
|
+
listeners.delete(listener)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const resetSystemErrorChannel = () => {
|
|
103
|
+
listeners.clear()
|
|
104
|
+
pendingPayloads.length = 0
|
|
105
|
+
lastFingerprint = ''
|
|
106
|
+
lastPublishedAt = 0
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default publishSystemError
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const normalizeText = value => String(value || '').trim()
|
|
2
|
+
|
|
3
|
+
const resolveMessageList = items =>
|
|
4
|
+
(Array.isArray(items) ? items : [])
|
|
5
|
+
.map(item =>
|
|
6
|
+
normalizeText(
|
|
7
|
+
item?.['hydra:description'] ||
|
|
8
|
+
item?.['hydra:title'] ||
|
|
9
|
+
item?.message ||
|
|
10
|
+
item?.title ||
|
|
11
|
+
item?.detail ||
|
|
12
|
+
item?.description ||
|
|
13
|
+
item,
|
|
14
|
+
),
|
|
15
|
+
)
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
.join('\n')
|
|
18
|
+
.trim()
|
|
19
|
+
|
|
20
|
+
export const resolveSystemErrorMessage = error => {
|
|
21
|
+
if (error === undefined || error === null) {
|
|
22
|
+
return ''
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (typeof error === 'string') {
|
|
26
|
+
return error.trim()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (Array.isArray(error)) {
|
|
30
|
+
return resolveMessageList(error)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (Array.isArray(error?.message)) {
|
|
34
|
+
return resolveMessageList(error.message)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (Array.isArray(error?.violations)) {
|
|
38
|
+
return resolveMessageList(error.violations)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (error?.response?.data) {
|
|
42
|
+
const responseMessage = resolveSystemErrorMessage(error.response.data)
|
|
43
|
+
if (responseMessage) {
|
|
44
|
+
return responseMessage
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (error?.body) {
|
|
49
|
+
const bodyMessage = resolveSystemErrorMessage(error.body)
|
|
50
|
+
if (bodyMessage) {
|
|
51
|
+
return bodyMessage
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return normalizeText(
|
|
56
|
+
error?.['hydra:description'] ||
|
|
57
|
+
error?.['hydra:title'] ||
|
|
58
|
+
error?.detail ||
|
|
59
|
+
error?.description ||
|
|
60
|
+
error?.errmsg ||
|
|
61
|
+
error?.error ||
|
|
62
|
+
error?.message ||
|
|
63
|
+
error?.title,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default resolveSystemErrorMessage
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const TOAST_STYLE_KEYS = ['pressable', 'view', 'text', 'indicator']
|
|
2
|
+
|
|
3
|
+
const isPlainObject = value =>
|
|
4
|
+
value && typeof value === 'object' && !Array.isArray(value)
|
|
5
|
+
|
|
6
|
+
export const TOAST_ERROR_STYLES = {
|
|
7
|
+
pressable: {
|
|
8
|
+
backgroundColor: '#FFF7F7',
|
|
9
|
+
borderColor: '#FECACA',
|
|
10
|
+
borderWidth: 1,
|
|
11
|
+
},
|
|
12
|
+
indicator: {
|
|
13
|
+
backgroundColor: '#DC2626',
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const TOAST_WARNING_STYLES = {
|
|
18
|
+
indicator: {
|
|
19
|
+
backgroundColor: '#e67e22',
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const mergeToastStyles = (...styleSets) =>
|
|
24
|
+
styleSets.reduce((mergedStyles, currentStyleSet) => {
|
|
25
|
+
if (!isPlainObject(currentStyleSet)) {
|
|
26
|
+
return mergedStyles
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
TOAST_STYLE_KEYS.forEach(styleKey => {
|
|
30
|
+
if (!isPlainObject(currentStyleSet[styleKey])) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
mergedStyles[styleKey] = {
|
|
35
|
+
...(mergedStyles[styleKey] || {}),
|
|
36
|
+
...currentStyleSet[styleKey],
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
return mergedStyles
|
|
41
|
+
}, {})
|
|
42
|
+
|
|
43
|
+
export const withErrorToastStyles = (options = {}) => ({
|
|
44
|
+
...options,
|
|
45
|
+
styles: mergeToastStyles(TOAST_ERROR_STYLES, options?.styles),
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
export const withWarningToastStyles = (options = {}) => ({
|
|
49
|
+
...options,
|
|
50
|
+
styles: mergeToastStyles(TOAST_WARNING_STYLES, options?.styles),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
export default withErrorToastStyles
|