@controleonline/ui-common 1.2.79 → 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/passwordPolicy.js +73 -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 +6 -1
- 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 +27 -10
- 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 +10 -8
- package/src/tests/react/utils/shopFranchises.test.js +121 -115
- package/src/tests/react/utils/systemErrorMessage.test.js +32 -59
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { getDeviceTypeLabel } from '@controleonline/ui-common/src/react/utils/printerDevices';
|
|
2
2
|
|
|
3
3
|
const DEVICE_RUNTIME_FOOTER_TEXT_CONFIG_KEY = 'device-runtime-footer-text';
|
|
4
|
+
const RUNTIME_FOOTER_HIDDEN_ROUTES = new Set(['PaylistPage']);
|
|
5
|
+
|
|
6
|
+
const shouldShowRuntimeFooter = currentRouteName =>
|
|
7
|
+
!RUNTIME_FOOTER_HIDDEN_ROUTES.has(String(currentRouteName || '').trim());
|
|
4
8
|
|
|
5
9
|
const safeTrim = value => String(value || '').trim();
|
|
6
10
|
|
|
@@ -489,5 +493,6 @@ module.exports = {
|
|
|
489
493
|
getRuntimeFooterWebIdentifierCandidates,
|
|
490
494
|
getRuntimeFooterText,
|
|
491
495
|
getRuntimeFooterWebHost,
|
|
492
|
-
normalizeRuntimeFooterText,
|
|
496
|
+
normalizeRuntimeFooterText,
|
|
497
|
+
shouldShowRuntimeFooter,
|
|
493
498
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {api} from '@controleonline/ui-common/src/api';
|
|
2
|
-
import {
|
|
3
|
-
extractCollectionItems,
|
|
4
|
-
toEntityIri,
|
|
5
|
-
} from '@controleonline/ui-common/src/react/utils/commercialDocumentOrders';
|
|
1
|
+
import {api} from '@controleonline/ui-common/src/api';
|
|
2
|
+
import {
|
|
3
|
+
extractCollectionItems,
|
|
4
|
+
toEntityIri,
|
|
5
|
+
} from '@controleonline/ui-common/src/react/utils/commercialDocumentOrders';
|
|
6
6
|
import {normalizeShopEntityId} from '@controleonline/ui-common/src/react/utils/shopConfig';
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
export const SHOP_FRANCHISE_LINK_TYPE = 'franchisee';
|
|
9
9
|
export const SHOP_FRANCHISE_PAGE_SIZE = 50;
|
|
10
10
|
|
|
@@ -150,39 +150,113 @@ export const filterShopFranchiseDirectory = ({
|
|
|
150
150
|
})
|
|
151
151
|
.filter(Boolean);
|
|
152
152
|
};
|
|
153
|
-
|
|
154
|
-
export const resolveFranchiseCompanyLabel = company =>
|
|
155
|
-
String(company?.alias || company?.name || '').trim() ||
|
|
156
|
-
`Franquia #${normalizeShopEntityId(company) || ''}`.trim();
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
|
|
154
|
+
export const resolveFranchiseCompanyLabel = company =>
|
|
155
|
+
String(company?.alias || company?.name || '').trim() ||
|
|
156
|
+
`Franquia #${normalizeShopEntityId(company) || ''}`.trim();
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Resolve the franchise company entity from a people_link relative to the
|
|
160
|
+
* viewed franchisor/company id. DB rows may store the viewed company as
|
|
161
|
+
* `company` or as `people` (inverted association).
|
|
162
|
+
*/
|
|
163
|
+
export const extractFranchiseCompanyFromLink = (link, viewerCompanyId) => {
|
|
164
|
+
const viewerId = normalizeShopEntityId(viewerCompanyId);
|
|
165
|
+
const companySide = link?.company;
|
|
166
|
+
const peopleSide = link?.people;
|
|
167
|
+
const companyId = normalizeShopEntityId(companySide);
|
|
168
|
+
const peopleId = normalizeShopEntityId(peopleSide);
|
|
169
|
+
|
|
170
|
+
if (viewerId && companyId === viewerId) {
|
|
171
|
+
return peopleSide;
|
|
172
|
+
}
|
|
173
|
+
if (viewerId && peopleId === viewerId) {
|
|
174
|
+
return companySide;
|
|
175
|
+
}
|
|
176
|
+
// Fallback: franchisee is typically the `people` side of linkType=franchisee.
|
|
177
|
+
return peopleSide || companySide || null;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const fetchFranchiseLinksPage = async ({
|
|
159
181
|
companyId,
|
|
160
|
-
|
|
182
|
+
side,
|
|
161
183
|
page = 1,
|
|
162
184
|
itemsPerPage = SHOP_FRANCHISE_PAGE_SIZE,
|
|
163
|
-
|
|
164
|
-
}
|
|
185
|
+
search = '',
|
|
186
|
+
}) => {
|
|
187
|
+
const id = normalizeShopEntityId(companyId);
|
|
188
|
+
if (!id) {
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
|
|
165
192
|
const params = {
|
|
166
193
|
page: Math.max(1, Number(page) || 1),
|
|
167
194
|
itemsPerPage: normalizeItemsPerPage(itemsPerPage),
|
|
195
|
+
linkType: SHOP_FRANCHISE_LINK_TYPE,
|
|
196
|
+
[side]: toEntityIri(id, 'people'),
|
|
168
197
|
};
|
|
169
198
|
|
|
170
199
|
if (String(search || '').trim()) {
|
|
171
200
|
params.search = String(search).trim();
|
|
172
201
|
}
|
|
173
202
|
|
|
174
|
-
const response =
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
203
|
+
const response = await api.fetch('people_links', {params});
|
|
204
|
+
return extractCollectionItems(response);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Authenticated franchise directory via people_links (both sides).
|
|
209
|
+
* GET /people?link.company=… does not reliably return franchisee PJs.
|
|
210
|
+
*/
|
|
211
|
+
const fetchFranchiseCompaniesFromLinks = async ({
|
|
212
|
+
companyId,
|
|
213
|
+
search = '',
|
|
214
|
+
itemsPerPage = SHOP_FRANCHISE_PAGE_SIZE,
|
|
215
|
+
} = {}) => {
|
|
216
|
+
const viewerId = normalizeShopEntityId(companyId);
|
|
217
|
+
if (!viewerId) {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const pageSize = normalizeItemsPerPage(itemsPerPage);
|
|
222
|
+
const byId = new Map();
|
|
223
|
+
|
|
224
|
+
for (const side of ['company', 'people']) {
|
|
225
|
+
let page = 1;
|
|
226
|
+
while (true) {
|
|
227
|
+
const links = await fetchFranchiseLinksPage({
|
|
228
|
+
companyId: viewerId,
|
|
229
|
+
side,
|
|
230
|
+
page,
|
|
231
|
+
itemsPerPage: pageSize,
|
|
232
|
+
search,
|
|
233
|
+
});
|
|
234
|
+
const pageLinks = Array.isArray(links) ? links : [];
|
|
235
|
+
|
|
236
|
+
pageLinks.forEach(link => {
|
|
237
|
+
const franchise = extractFranchiseCompanyFromLink(link, viewerId);
|
|
238
|
+
const franchiseId = normalizeShopEntityId(franchise);
|
|
239
|
+
if (!franchiseId || franchiseId === viewerId) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (!byId.has(franchiseId)) {
|
|
243
|
+
byId.set(
|
|
244
|
+
franchiseId,
|
|
245
|
+
typeof franchise === 'object' && franchise
|
|
246
|
+
? franchise
|
|
247
|
+
: {id: franchiseId},
|
|
248
|
+
);
|
|
249
|
+
}
|
|
182
250
|
});
|
|
183
|
-
const items = extractCollectionItems(response);
|
|
184
251
|
|
|
185
|
-
|
|
252
|
+
if (pageLinks.length < pageSize) {
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
page += 1;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return Array.from(byId.values())
|
|
186
260
|
.map(normalizeFranchiseDirectoryItem)
|
|
187
261
|
.sort((left, right) =>
|
|
188
262
|
sortByLabel(
|
|
@@ -192,67 +266,113 @@ export const fetchShopFranchiseCompanies = async ({
|
|
|
192
266
|
);
|
|
193
267
|
};
|
|
194
268
|
|
|
195
|
-
export const
|
|
269
|
+
export const fetchShopFranchiseCompanies = async ({
|
|
196
270
|
companyId,
|
|
197
|
-
publicDirectory = false,
|
|
198
271
|
search = '',
|
|
272
|
+
page = 1,
|
|
199
273
|
itemsPerPage = SHOP_FRANCHISE_PAGE_SIZE,
|
|
274
|
+
publicDirectory = false,
|
|
200
275
|
} = {}) => {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
while (true) {
|
|
206
|
-
const pageItems = await fetchShopFranchiseCompanies({
|
|
207
|
-
companyId,
|
|
208
|
-
publicDirectory,
|
|
209
|
-
search,
|
|
210
|
-
page,
|
|
211
|
-
itemsPerPage: normalizedItemsPerPage,
|
|
212
|
-
});
|
|
213
|
-
const normalizedPageItems = Array.isArray(pageItems) ? pageItems : [];
|
|
276
|
+
const params = {
|
|
277
|
+
page: Math.max(1, Number(page) || 1),
|
|
278
|
+
itemsPerPage: normalizeItemsPerPage(itemsPerPage),
|
|
279
|
+
};
|
|
214
280
|
|
|
215
|
-
|
|
281
|
+
if (String(search || '').trim()) {
|
|
282
|
+
params.search = String(search).trim();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (publicDirectory) {
|
|
286
|
+
const response = await api.fetch('/shop/franchises', {params});
|
|
287
|
+
const items = extractCollectionItems(response);
|
|
288
|
+
return items
|
|
289
|
+
.map(normalizeFranchiseDirectoryItem)
|
|
290
|
+
.sort((left, right) =>
|
|
291
|
+
sortByLabel(
|
|
292
|
+
resolveFranchiseCompanyLabel(left),
|
|
293
|
+
resolveFranchiseCompanyLabel(right),
|
|
294
|
+
),
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Management path: people_links dual-side (company + people).
|
|
299
|
+
// Page param is ignored for dual aggregation; callers that need full list
|
|
300
|
+
// should use fetchAllShopFranchiseDirectory.
|
|
301
|
+
const all = await fetchFranchiseCompaniesFromLinks({
|
|
302
|
+
companyId,
|
|
303
|
+
search,
|
|
304
|
+
itemsPerPage,
|
|
305
|
+
});
|
|
306
|
+
const start = (Math.max(1, Number(page) || 1) - 1) * normalizeItemsPerPage(itemsPerPage);
|
|
307
|
+
return all.slice(start, start + normalizeItemsPerPage(itemsPerPage));
|
|
308
|
+
};
|
|
216
309
|
|
|
217
|
-
|
|
218
|
-
|
|
310
|
+
export const fetchAllShopFranchiseDirectory = async ({
|
|
311
|
+
companyId,
|
|
312
|
+
publicDirectory = false,
|
|
313
|
+
search = '',
|
|
314
|
+
itemsPerPage = SHOP_FRANCHISE_PAGE_SIZE,
|
|
315
|
+
} = {}) => {
|
|
316
|
+
if (publicDirectory) {
|
|
317
|
+
const normalizedItemsPerPage = normalizeItemsPerPage(itemsPerPage);
|
|
318
|
+
const items = [];
|
|
319
|
+
let page = 1;
|
|
320
|
+
|
|
321
|
+
while (true) {
|
|
322
|
+
const pageItems = await fetchShopFranchiseCompanies({
|
|
323
|
+
companyId,
|
|
324
|
+
publicDirectory: true,
|
|
325
|
+
search,
|
|
326
|
+
page,
|
|
327
|
+
itemsPerPage: normalizedItemsPerPage,
|
|
328
|
+
});
|
|
329
|
+
const normalizedPageItems = Array.isArray(pageItems) ? pageItems : [];
|
|
330
|
+
items.push(...normalizedPageItems);
|
|
331
|
+
if (normalizedPageItems.length < normalizedItemsPerPage) {
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
page += 1;
|
|
219
335
|
}
|
|
220
336
|
|
|
221
|
-
|
|
337
|
+
return items
|
|
338
|
+
.map(normalizeFranchiseDirectoryItem)
|
|
339
|
+
.sort((left, right) =>
|
|
340
|
+
sortByLabel(
|
|
341
|
+
resolveFranchiseCompanyLabel(left),
|
|
342
|
+
resolveFranchiseCompanyLabel(right),
|
|
343
|
+
),
|
|
344
|
+
);
|
|
222
345
|
}
|
|
223
346
|
|
|
224
|
-
return
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
resolveFranchiseCompanyLabel(right),
|
|
230
|
-
),
|
|
231
|
-
);
|
|
347
|
+
return fetchFranchiseCompaniesFromLinks({
|
|
348
|
+
companyId,
|
|
349
|
+
search,
|
|
350
|
+
itemsPerPage,
|
|
351
|
+
});
|
|
232
352
|
};
|
|
233
353
|
|
|
234
354
|
export const fetchShopFranchiseAddresses = async ({
|
|
235
355
|
peopleId,
|
|
236
356
|
search = '',
|
|
237
357
|
} = {}) => {
|
|
238
|
-
if (!peopleId) {
|
|
239
|
-
return [];
|
|
240
|
-
}
|
|
241
|
-
|
|
358
|
+
if (!peopleId) {
|
|
359
|
+
return [];
|
|
360
|
+
}
|
|
361
|
+
|
|
242
362
|
const params = {
|
|
243
363
|
people: toEntityIri(peopleId, 'people'),
|
|
244
364
|
itemsPerPage: SHOP_FRANCHISE_PAGE_SIZE,
|
|
245
365
|
page: 1,
|
|
246
366
|
};
|
|
247
|
-
|
|
248
|
-
if (String(search || '').trim()) {
|
|
249
|
-
params.search = String(search).trim();
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const response = await api.fetch('addresses', {params});
|
|
253
|
-
return extractCollectionItems(response);
|
|
254
|
-
};
|
|
255
|
-
|
|
367
|
+
|
|
368
|
+
if (String(search || '').trim()) {
|
|
369
|
+
params.search = String(search).trim();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const response = await api.fetch('addresses', {params});
|
|
373
|
+
return extractCollectionItems(response);
|
|
374
|
+
};
|
|
375
|
+
|
|
256
376
|
export const fetchShopFranchiseDirectory = async ({
|
|
257
377
|
companyId,
|
|
258
378
|
publicDirectory = false,
|
|
@@ -270,4 +390,5 @@ export const fetchShopFranchiseDirectory = async ({
|
|
|
270
390
|
|
|
271
391
|
return companies.map(normalizeFranchiseDirectoryItem);
|
|
272
392
|
};
|
|
393
|
+
|
|
273
394
|
// TODO(store-first): quando este arquivo for mexido, mover a leitura para stores, remover api.fetch e evitar repassar dados em objetos quando o store ja resolver isso.
|
|
@@ -1,67 +1,71 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
item?.title ||
|
|
11
|
-
item?.
|
|
12
|
-
item?.
|
|
13
|
-
item
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
error?.description ||
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
1
|
+
import { mapPasswordErrorMessage } from './passwordPolicy'
|
|
2
|
+
|
|
3
|
+
const normalizeText = value => String(value || '').trim()
|
|
4
|
+
|
|
5
|
+
const resolveMessageList = items =>
|
|
6
|
+
(Array.isArray(items) ? items : [])
|
|
7
|
+
.map(item =>
|
|
8
|
+
normalizeText(
|
|
9
|
+
item?.['hydra:description'] ||
|
|
10
|
+
item?.['hydra:title'] ||
|
|
11
|
+
item?.message ||
|
|
12
|
+
item?.title ||
|
|
13
|
+
item?.detail ||
|
|
14
|
+
item?.description ||
|
|
15
|
+
item,
|
|
16
|
+
),
|
|
17
|
+
)
|
|
18
|
+
.filter(Boolean)
|
|
19
|
+
.join('\n')
|
|
20
|
+
.trim()
|
|
21
|
+
|
|
22
|
+
export const resolveSystemErrorMessage = error => {
|
|
23
|
+
if (error === undefined || error === null) {
|
|
24
|
+
return ''
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (typeof error === 'string') {
|
|
28
|
+
return mapPasswordErrorMessage(error.trim())
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (Array.isArray(error)) {
|
|
32
|
+
return mapPasswordErrorMessage(resolveMessageList(error))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (Array.isArray(error?.message)) {
|
|
36
|
+
return mapPasswordErrorMessage(resolveMessageList(error.message))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (Array.isArray(error?.violations)) {
|
|
40
|
+
return mapPasswordErrorMessage(resolveMessageList(error.violations))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (error?.response?.data) {
|
|
44
|
+
const responseMessage = resolveSystemErrorMessage(error.response.data)
|
|
45
|
+
if (responseMessage) {
|
|
46
|
+
return responseMessage
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (error?.body) {
|
|
51
|
+
const bodyMessage = resolveSystemErrorMessage(error.body)
|
|
52
|
+
if (bodyMessage) {
|
|
53
|
+
return bodyMessage
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return mapPasswordErrorMessage(
|
|
58
|
+
normalizeText(
|
|
59
|
+
error?.['hydra:description'] ||
|
|
60
|
+
error?.['hydra:title'] ||
|
|
61
|
+
error?.detail ||
|
|
62
|
+
error?.description ||
|
|
63
|
+
error?.errmsg ||
|
|
64
|
+
error?.error ||
|
|
65
|
+
error?.message ||
|
|
66
|
+
error?.title,
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default resolveSystemErrorMessage
|
|
@@ -2,6 +2,9 @@ import {api} from '@controleonline/ui-common/src/api';
|
|
|
2
2
|
import * as types from '@controleonline/ui-default/src/store/default/mutation_types';
|
|
3
3
|
import {isWebRuntimeDevice} from '@controleonline/ui-common/src/react/utils/deviceRuntime';
|
|
4
4
|
|
|
5
|
+
/** Max age for company device_configs cache used by print routing (ms). */
|
|
6
|
+
export const COMPANY_DEVICE_CONFIGS_CACHE_TTL_MS = 30 * 1000;
|
|
7
|
+
|
|
5
8
|
const normalizePeopleKey = value =>
|
|
6
9
|
String(value || '')
|
|
7
10
|
.replace(/\D+/g, '')
|
|
@@ -34,16 +37,84 @@ const getStoredDevice = () => {
|
|
|
34
37
|
return {};
|
|
35
38
|
};
|
|
36
39
|
|
|
40
|
+
const resolveConfigId = config => {
|
|
41
|
+
if (!config) {
|
|
42
|
+
return '';
|
|
43
|
+
}
|
|
44
|
+
if (config.id != null && String(config.id).trim() !== '') {
|
|
45
|
+
return String(config.id).trim();
|
|
46
|
+
}
|
|
47
|
+
const iri = String(config['@id'] || '').trim();
|
|
48
|
+
if (!iri) {
|
|
49
|
+
return '';
|
|
50
|
+
}
|
|
51
|
+
const parts = iri.split('/');
|
|
52
|
+
return String(parts[parts.length - 1] || '').trim();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Merge a saved device_config into the company list and clear loadedKey so the
|
|
57
|
+
* next ensureCompanyDeviceConfigsLoaded can refresh (print routing picks up
|
|
58
|
+
* manager/destination changes without app rebuild).
|
|
59
|
+
*/
|
|
60
|
+
const mergeSavedConfigIntoCompanyItems = (commit, getters, saved) => {
|
|
61
|
+
if (!saved) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const currentItems = Array.isArray(getters.items) ? getters.items : [];
|
|
66
|
+
const savedId = resolveConfigId(saved);
|
|
67
|
+
const savedDeviceId = String(
|
|
68
|
+
saved?.device?.device || saved?.device?.id || saved?.device || '',
|
|
69
|
+
).trim();
|
|
70
|
+
const savedType = String(saved?.type || '').trim().toUpperCase();
|
|
71
|
+
|
|
72
|
+
let replaced = false;
|
|
73
|
+
const nextItems = currentItems.map(item => {
|
|
74
|
+
const itemId = resolveConfigId(item);
|
|
75
|
+
if (savedId && itemId && savedId === itemId) {
|
|
76
|
+
replaced = true;
|
|
77
|
+
return {...item, ...saved};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const itemDeviceId = String(
|
|
81
|
+
item?.device?.device || item?.device?.id || item?.device || '',
|
|
82
|
+
).trim();
|
|
83
|
+
const itemType = String(item?.type || '').trim().toUpperCase();
|
|
84
|
+
if (
|
|
85
|
+
savedDeviceId &&
|
|
86
|
+
itemDeviceId &&
|
|
87
|
+
savedDeviceId === itemDeviceId &&
|
|
88
|
+
savedType &&
|
|
89
|
+
itemType === savedType
|
|
90
|
+
) {
|
|
91
|
+
replaced = true;
|
|
92
|
+
return {...item, ...saved};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return item;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (!replaced && savedId) {
|
|
99
|
+
nextItems.push(saved);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
commit(types.SET_ITEMS, nextItems);
|
|
103
|
+
// Force next ensure to re-fetch when TTL allows (other runtimes still refresh via TTL).
|
|
104
|
+
commit(types.SET_LOADED_KEY, '');
|
|
105
|
+
commit(types.SET_LOADED_AT, 0);
|
|
106
|
+
};
|
|
107
|
+
|
|
37
108
|
export const addDeviceConfigs = ({commit, getters}, params) => {
|
|
38
109
|
let configsObj = {};
|
|
39
110
|
if (params.configs) {
|
|
40
111
|
if (typeof params.configs === 'string') {
|
|
41
112
|
try {
|
|
42
113
|
configsObj = JSON.parse(params.configs);
|
|
43
|
-
} catch (e) {
|
|
44
|
-
configsObj = {};
|
|
45
|
-
}
|
|
46
|
-
} else if (typeof params.configs === 'object') {
|
|
114
|
+
} catch (e) {
|
|
115
|
+
configsObj = {};
|
|
116
|
+
}
|
|
117
|
+
} else if (typeof params.configs === 'object') {
|
|
47
118
|
configsObj = params.configs;
|
|
48
119
|
}
|
|
49
120
|
}
|
|
@@ -82,30 +153,31 @@ export const addDeviceConfigs = ({commit, getters}, params) => {
|
|
|
82
153
|
if (isRuntimeWebDevice) {
|
|
83
154
|
commit(types.SET_ITEM, nextItem);
|
|
84
155
|
if (!canPersistRuntimeWebDevice) {
|
|
156
|
+
mergeSavedConfigIntoCompanyItems(commit, getters, nextItem);
|
|
85
157
|
return Promise.resolve(nextItem);
|
|
86
158
|
}
|
|
87
159
|
}
|
|
88
160
|
|
|
89
161
|
let options = {
|
|
90
162
|
method: 'POST',
|
|
91
|
-
body: updatedParams,
|
|
92
|
-
};
|
|
93
|
-
commit(types.SET_ISSAVING, true);
|
|
94
|
-
|
|
95
|
-
return api
|
|
163
|
+
body: updatedParams,
|
|
164
|
+
};
|
|
165
|
+
commit(types.SET_ISSAVING, true);
|
|
166
|
+
|
|
167
|
+
return api
|
|
96
168
|
.fetch(getters.resourceEndpoint + '/add-configs', options)
|
|
97
169
|
.then(data => {
|
|
98
170
|
let parsedConfigs = {};
|
|
99
|
-
if (data?.configs) {
|
|
100
|
-
if (typeof data.configs === 'string') {
|
|
101
|
-
try {
|
|
102
|
-
parsedConfigs = JSON.parse(data.configs);
|
|
103
|
-
} catch (e) {
|
|
104
|
-
parsedConfigs = {};
|
|
105
|
-
}
|
|
106
|
-
} else if (typeof data.configs === 'object') {
|
|
107
|
-
parsedConfigs = data.configs;
|
|
108
|
-
}
|
|
171
|
+
if (data?.configs) {
|
|
172
|
+
if (typeof data.configs === 'string') {
|
|
173
|
+
try {
|
|
174
|
+
parsedConfigs = JSON.parse(data.configs);
|
|
175
|
+
} catch (e) {
|
|
176
|
+
parsedConfigs = {};
|
|
177
|
+
}
|
|
178
|
+
} else if (typeof data.configs === 'object') {
|
|
179
|
+
parsedConfigs = data.configs;
|
|
180
|
+
}
|
|
109
181
|
}
|
|
110
182
|
const hasParsedConfigs =
|
|
111
183
|
parsedConfigs && Object.keys(parsedConfigs).length > 0;
|
|
@@ -118,15 +190,22 @@ export const addDeviceConfigs = ({commit, getters}, params) => {
|
|
|
118
190
|
: nextItem.configs || getters.item?.configs || {},
|
|
119
191
|
};
|
|
120
192
|
commit(types.SET_ITEM, d);
|
|
193
|
+
mergeSavedConfigIntoCompanyItems(commit, getters, d);
|
|
121
194
|
return d;
|
|
122
195
|
})
|
|
123
|
-
.catch(e => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
196
|
+
.catch(e => {
|
|
197
|
+
const status = Number(e?.status || e?.code || e?.response?.status || 0);
|
|
198
|
+
// 403 is expected for non-tenant-admin users during runtime bootstrap.
|
|
199
|
+
if (status === 403) {
|
|
200
|
+
console.debug('addDeviceConfigs skipped (no tenant admin authority):', e?.message || e);
|
|
201
|
+
} else {
|
|
202
|
+
console.error('addDeviceConfigs API error:', e);
|
|
203
|
+
}
|
|
204
|
+
commit(types.SET_ERROR, e.message);
|
|
205
|
+
throw e;
|
|
206
|
+
})
|
|
207
|
+
.finally(() => {
|
|
208
|
+
commit(types.SET_ISSAVING, false);
|
|
130
209
|
});
|
|
131
210
|
};
|
|
132
211
|
|
|
@@ -174,10 +253,16 @@ export const ensureCompanyDeviceConfigsLoaded = (context, params = {}) => {
|
|
|
174
253
|
return Promise.resolve([]);
|
|
175
254
|
}
|
|
176
255
|
|
|
177
|
-
|
|
256
|
+
const force = params?.force === true;
|
|
257
|
+
const loadedAt = Number(context.getters.loadedAt) || 0;
|
|
258
|
+
const cacheAgeMs = loadedAt > 0 ? Date.now() - loadedAt : Number.POSITIVE_INFINITY;
|
|
259
|
+
const cacheFresh =
|
|
260
|
+
!force &&
|
|
178
261
|
context.getters.loadedKey === peopleKey &&
|
|
179
|
-
Array.isArray(context.getters.items)
|
|
180
|
-
|
|
262
|
+
Array.isArray(context.getters.items) &&
|
|
263
|
+
cacheAgeMs < COMPANY_DEVICE_CONFIGS_CACHE_TTL_MS;
|
|
264
|
+
|
|
265
|
+
if (cacheFresh) {
|
|
181
266
|
return Promise.resolve(context.getters.items);
|
|
182
267
|
}
|
|
183
268
|
|
|
@@ -187,4 +272,8 @@ export const ensureCompanyDeviceConfigsLoaded = (context, params = {}) => {
|
|
|
187
272
|
|
|
188
273
|
return getCompanyDeviceConfigs(context, params);
|
|
189
274
|
};
|
|
190
|
-
|
|
275
|
+
|
|
276
|
+
export const invalidateCompanyDeviceConfigsCache = ({commit}) => {
|
|
277
|
+
commit(types.SET_LOADED_KEY, '');
|
|
278
|
+
commit(types.SET_LOADED_AT, 0);
|
|
279
|
+
};
|