adminizer 5.1.0-build.28 → 5.2.0-build.30
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/assets/{abnfDiagram-VCTEODGH-uWOVpoSR.js → abnfDiagram-VCTEODGH-BhZn7BLv.js} +1 -1
- package/assets/{add-A_fIlNKI.js → add-DDfJt70P.js} +1 -1
- package/assets/{add-group-C2x6K3tV.js → add-group-Cs3S9VVn.js} +1 -1
- package/assets/{add-user-u61kYlEm.js → add-user-DxI_sJLw.js} +1 -1
- package/assets/ai-assistant/agent.es.js +14736 -11737
- package/assets/app-DM8KzeFz.css +1 -0
- package/assets/app-DOMFUTFw.js +138 -0
- package/assets/{arc--ZX64bmY.js → arc-BRY7wCfa.js} +1 -1
- package/assets/{architectureDiagram-5GKGNRK7-W9AzLZvl.js → architectureDiagram-5GKGNRK7-DfBT7jAW.js} +1 -1
- package/assets/{blockDiagram-I7D4REHJ-BpK_yEa7.js → blockDiagram-I7D4REHJ-CW2mcR7Z.js} +1 -1
- package/assets/{c4Diagram-7LVT6UL2-DiQzU1s0.js → c4Diagram-7LVT6UL2-BGsyO-4Y.js} +1 -1
- package/assets/{catalog-dSCv3Szm.js → catalog-cyCZOCXs.js} +3 -3
- package/assets/channel-Bd-bsjgh.js +1 -0
- package/assets/{chunk-2Q5K7J3B-GPHETbCq.js → chunk-2Q5K7J3B-4EnjsYhG.js} +1 -1
- package/assets/{chunk-5VM5RSS4-bLovkogd.js → chunk-5VM5RSS4-BVzmz0IS.js} +1 -1
- package/assets/{chunk-F27PBJKO-DbkTY41z.js → chunk-F27PBJKO-Bkf9Z4lt.js} +1 -1
- package/assets/{chunk-IMKFNOWR-CL0VAxX7.js → chunk-IMKFNOWR-Dg6Y3xGC.js} +1 -1
- package/assets/{chunk-JWPE2WC7-d1Grk14b.js → chunk-JWPE2WC7-BdVNpqE2.js} +1 -1
- package/assets/{chunk-POPQ4Y6H-CAUhiiNt.js → chunk-POPQ4Y6H-0R-etdBM.js} +1 -1
- package/assets/{chunk-SVP7TREG-C8ADZUEr.js → chunk-SVP7TREG-CdABJwLU.js} +1 -1
- package/assets/{chunk-TICWLB2K-oRqFZabo.js → chunk-TICWLB2K-Da-oSw37.js} +1 -1
- package/assets/{chunk-XXDRQBXY-C3zo4c2P.js → chunk-XXDRQBXY-Ba8BsH2Z.js} +1 -1
- package/assets/classDiagram-ZZMXUADV-BftNBmvr.js +1 -0
- package/assets/classDiagram-v2-VYDZK3BY-BftNBmvr.js +1 -0
- package/assets/controls/ckeditor.es.js +10659 -10551
- package/assets/controls/handsontable.es.js +9053 -8479
- package/assets/controls/jsoneditor.es.js +641 -614
- package/assets/controls/leaflet.es.js +672 -663
- package/assets/controls/toast-ui.es.js +13696 -13676
- package/assets/{cose-bilkent-JH36ORCC-ZPvVaI6n.js → cose-bilkent-JH36ORCC-BYrWDdVx.js} +1 -1
- package/assets/{cynefin-OW5HDTMX-EEL4eqU7.js → cynefin-OW5HDTMX-CwHzXvDB.js} +1 -1
- package/assets/{cynefinDiagram-5FMLGOSQ-DpDFbbrn.js → cynefinDiagram-5FMLGOSQ-V6tH6arn.js} +1 -1
- package/assets/{dagre-GXQ25YYZ-Dtjy1xN1.js → dagre-GXQ25YYZ-CaBvafdG.js} +1 -1
- package/assets/{dashboard-DXs-vdJG.js → dashboard-C27hB1GI.js} +1 -1
- package/assets/{diagram-S7CK7UJ4-BhWisCOy.js → diagram-S7CK7UJ4-85jkE9hd.js} +1 -1
- package/assets/{diagram-UQ7AKVKN-BBH2Mm33.js → diagram-UQ7AKVKN-DHJRq1TM.js} +1 -1
- package/assets/{diagram-VSXAHHWV-B1Xc2cPz.js → diagram-VSXAHHWV-C74VLfSc.js} +1 -1
- package/assets/{diagram-VX7I27RA-CTTYzy1s.js → diagram-VX7I27RA-CVJcG5hn.js} +1 -1
- package/assets/{diagram-Z3DM3KII-DdH0yWH0.js → diagram-Z3DM3KII-B8ybg5RA.js} +1 -1
- package/assets/{docs-BZYlfOON.js → docs-CK6W0Jko.js} +1 -1
- package/assets/{ebnfDiagram-PWID7BFC-R9cSqf0y.js → ebnfDiagram-PWID7BFC-D_gz_iz6.js} +1 -1
- package/assets/{erDiagram-RLTQ6QDP-Bpu-Pm95.js → erDiagram-RLTQ6QDP-D3W95aGH.js} +1 -1
- package/assets/{flowDiagram-HODETNUW-NdXf-fRN.js → flowDiagram-HODETNUW-D2PTWIEg.js} +1 -1
- package/assets/{ganttDiagram-EL5Y4UJY-itQHv_dH.js → ganttDiagram-EL5Y4UJY-TFBUh0C6.js} +1 -1
- package/assets/{gitGraphDiagram-WWUBYQGX-DorHK4Wx.js → gitGraphDiagram-WWUBYQGX-BfFJzeqx.js} +1 -1
- package/assets/{history-DB48MCa8.js → history-DX6CEQqv.js} +1 -1
- package/assets/{infoDiagram-27XIBGKW-FgFATKk4.js → infoDiagram-27XIBGKW-esGmrWiw.js} +1 -1
- package/assets/{init-user-DAXb55S_.js → init-user-D1G_dbcZ.js} +1 -1
- package/assets/{ishikawaDiagram-5VMMS53U-Bjl1a496.js → ishikawaDiagram-5VMMS53U--aGHPnva.js} +1 -1
- package/assets/{journeyDiagram-3NMN7TZE-DU1TmnbI.js → journeyDiagram-3NMN7TZE-03BXeE8O.js} +1 -1
- package/assets/{kanban-definition-UXKFOSKX-BsrlKdcU.js → kanban-definition-UXKFOSKX-JdDv2WHg.js} +1 -1
- package/assets/{layout-C68duuC7.js → layout-Bz6WKClu.js} +1 -1
- package/assets/{linear-DdadsTLS.js → linear-Chzx_9mb.js} +1 -1
- package/assets/{list-D3R3q3Ti.js → list-BuSdZ2Ic.js} +1 -1
- package/assets/{login-DwxC2WD6.js → login-DFWfnrmE.js} +1 -1
- package/assets/manifest.json +277 -277
- package/assets/media-manager-CT4LZgo_.js +7 -0
- package/assets/mermaid.core-Dp-0i8FJ.js +310 -0
- package/assets/{mindmap-definition-YA3MSWOX-BfdP8hOy.js → mindmap-definition-YA3MSWOX-BJSEYobY.js} +1 -1
- package/assets/{module-DKdDlng0.js → module-Cgn-q5gZ.js} +1 -1
- package/assets/{notification-DDWbaooT.js → notification-BrfoqVVT.js} +1 -1
- package/assets/{pegDiagram-XKGWAZYB-BO9cFGCI.js → pegDiagram-XKGWAZYB-ccVsKwl1.js} +1 -1
- package/assets/{pieDiagram-E7YTZNPT-B4KrTpto.js → pieDiagram-E7YTZNPT-B1XZVgNq.js} +1 -1
- package/assets/{quadrantDiagram-AXDQQJYC-BumScZvv.js → quadrantDiagram-AXDQQJYC-CdABEYmF.js} +1 -1
- package/assets/{railroadDiagram-O6MQD6OU-DUDQQC-p.js → railroadDiagram-O6MQD6OU-OX83MXuO.js} +1 -1
- package/assets/{register-5rs1OSaq.js → register-Cxf3Pow-.js} +1 -1
- package/assets/{requirementDiagram-BXWQKSXE-BtNsxNt1.js → requirementDiagram-BXWQKSXE-CD7swf9u.js} +1 -1
- package/assets/{ru-Cf1WWSt0.js → ru-DxiFV21N.js} +1 -1
- package/assets/{sankeyDiagram-P5KCCOFB-xjPcSTcN.js → sankeyDiagram-P5KCCOFB-Cgwq2DVJ.js} +1 -1
- package/assets/{sequenceDiagram-WJ2MYXX4-BVOgA3-B.js → sequenceDiagram-WJ2MYXX4-BgjP07Zh.js} +1 -1
- package/assets/{sizeCapture-INFHLROL-BDI5FsKO.js → sizeCapture-INFHLROL-B-jQ1aKT.js} +1 -1
- package/assets/{stateDiagram-D77RDMKH-DreIJby8.js → stateDiagram-D77RDMKH-Drot14NK.js} +1 -1
- package/assets/stateDiagram-v2-MP3YSRHH-C5VLF0H1.js +1 -0
- package/assets/{swimlanes-42K2YHIH-J_CNnPZv.js → swimlanes-42K2YHIH-lCqHhK1l.js} +1 -1
- package/assets/swimlanesDiagram-VR7AAH4N-4yyX1ZbA.js +8 -0
- package/assets/{timeline-definition-24CTP7MA-BXGN0YmT.js → timeline-definition-24CTP7MA-B-QLcvwc.js} +1 -1
- package/assets/{use-appearance-B2YFalXG.js → use-appearance-B0ck1CR_.js} +1 -1
- package/assets/{user-filters-list-D8WohjvX.js → user-filters-list-ce2cmS5Z.js} +1 -1
- package/assets/{vennDiagram-4TSXK5OY-Mu0lMKUo.js → vennDiagram-4TSXK5OY-2DN2uspP.js} +1 -1
- package/assets/{wardleyDiagram-VM6X3IG4-CXFMI74r.js → wardleyDiagram-VM6X3IG4-C_GODc0p.js} +1 -1
- package/assets/{welcome-DHQx7R11.js → welcome-DaAj6jOI.js} +1 -1
- package/assets/with-app-layout-CwAECGwY.js +34 -0
- package/assets/{xychartDiagram-S5SC5T6Z-DCr7j1YG.js → xychartDiagram-S5SC5T6Z-Cq8htGoW.js} +1 -1
- package/controllers/catalog/FrontendCatalogAdapter.d.ts +5 -5
- package/controllers/catalog/FrontentCatalogAdapter.d.ts +5 -5
- package/controllers/initUser.js +1 -1
- package/helpers/inertiaAddHelper.js +10 -1
- package/helpers/inertiaMenuHelper.d.ts +3 -2
- package/helpers/inertiaMenuHelper.js +9 -4
- package/helpers/navigationAccessHelper.d.ts +5 -0
- package/helpers/navigationAccessHelper.js +13 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/interfaces/adminpanelConfig.d.ts +7 -0
- package/lib/Adminizer.d.ts +2 -0
- package/lib/Adminizer.js +3 -0
- package/lib/admin-links/AdminLinkHandler.js +5 -1
- package/lib/app-manager/AdminizerApp.d.ts +34 -0
- package/lib/app-manager/AppManager.js +35 -0
- package/lib/app-manager/ContextHandler.d.ts +240 -0
- package/lib/app-manager/ContextHandler.js +446 -0
- package/lib/app-manager/ControllerHandler.d.ts +2 -0
- package/lib/app-manager/ControllerHandler.js +7 -0
- package/lib/media-manager/helpers/MediaManagerHelper.d.ts +1 -1
- package/lib/notifications/AbstractNotificationService.d.ts +3 -3
- package/package.json +3 -2
- package/system/bindDocs.js +1 -1
- package/system/bindInertia.js +34 -9
- package/assets/app-CRa5Kokq.js +0 -138
- package/assets/app-TCCHARSx.css +0 -1
- package/assets/channel-DRO88SVn.js +0 -1
- package/assets/classDiagram-ZZMXUADV-Cb-leHEm.js +0 -1
- package/assets/classDiagram-v2-VYDZK3BY-Cb-leHEm.js +0 -1
- package/assets/media-manager-mt4Ykki7.js +0 -6
- package/assets/mermaid.core-DxLHD4PF.js +0 -310
- package/assets/stateDiagram-v2-MP3YSRHH-BNzSJJYe.js +0 -1
- package/assets/swimlanesDiagram-VR7AAH4N-DN5N1rG7.js +0 -8
- package/assets/with-app-layout-Cvg36gxr.js +0 -31
|
@@ -19,6 +19,12 @@ export class ControllerHandler {
|
|
|
19
19
|
path: fullPath,
|
|
20
20
|
enabled: true,
|
|
21
21
|
};
|
|
22
|
+
if (controller.context) {
|
|
23
|
+
record.unassign = this.adminizer.contextHandler.assign(controller.route, controller.context, {
|
|
24
|
+
owner: appName,
|
|
25
|
+
exact: true,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
22
28
|
const middleware = bindWithMiddlewares(this.adminizer, this.adminizer.config.middlewares ?? [], (req, res, next) => {
|
|
23
29
|
if (!record.enabled) {
|
|
24
30
|
return next();
|
|
@@ -69,6 +75,7 @@ export class ControllerHandler {
|
|
|
69
75
|
return;
|
|
70
76
|
}
|
|
71
77
|
this.removeRoute(record.path, record.method);
|
|
78
|
+
record.unassign?.();
|
|
72
79
|
this.controllers.delete(id);
|
|
73
80
|
this.adminizer.emitter.emit("app:controller:unregistered", {
|
|
74
81
|
appName: record.appName,
|
|
@@ -32,7 +32,7 @@ export declare function updateCurrentHistoryMediaManagerData(adminizer: Adminize
|
|
|
32
32
|
* @param model
|
|
33
33
|
* @param widgetName
|
|
34
34
|
*/
|
|
35
|
-
export declare function getRelationsMediaManager(adminizer: Adminizer, data: MediaManagerWidgetJSON): Promise<import("../AbstractMediaManager").MediaManagerWidgetClientItem[]>;
|
|
35
|
+
export declare function getRelationsMediaManager(adminizer: Adminizer, data: MediaManagerWidgetJSON): Promise<import("../AbstractMediaManager.js").MediaManagerWidgetClientItem[]>;
|
|
36
36
|
/**
|
|
37
37
|
* Delate Ralations
|
|
38
38
|
* @param adminizer
|
|
@@ -56,9 +56,9 @@ export declare abstract class AbstractNotificationService extends EventEmitter {
|
|
|
56
56
|
abstract readonly icon: string;
|
|
57
57
|
abstract readonly iconColor: string;
|
|
58
58
|
constructor(adminizer: Adminizer);
|
|
59
|
-
protected notificationModel(): import("
|
|
60
|
-
protected userNotificationModel(): import("
|
|
61
|
-
protected userModel(): import("
|
|
59
|
+
protected notificationModel(): import("../../index.js").InternalModelRepository<any>;
|
|
60
|
+
protected userNotificationModel(): import("../../index.js").InternalModelRepository<any>;
|
|
61
|
+
protected userModel(): import("../../index.js").InternalModelRepository<User>;
|
|
62
62
|
private _bindAccessRight;
|
|
63
63
|
/**
|
|
64
64
|
* Registers a client for a user and associates it with a send function.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adminizer",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.2.0-build.30",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./index.js",
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"build": "npm run copy:backend && npm run compile:backend && npm run compile:ui && npm run build:assets",
|
|
31
31
|
"build:catalog-modules": "cross-env CATALOG_COMPONENT=catalogAction vite build --config fixture/virtual-catalog/vite.config.module.ts && cross-env CATALOG_COMPONENT=Group vite build --config fixture/virtual-catalog/vite.config.module.ts",
|
|
32
32
|
"start:typeorm:seed": "cross-env ORM=typeorm tsx --tsconfig ./fixture/tsconfig.json ./fixture/index.ts",
|
|
33
|
-
"build:apps": "
|
|
33
|
+
"build:context-apps": "cross-env CONTEXT_ENTRY=ContextDemoPage vite build --config fixture/apps/contexts/vite.config.module.ts && cross-env CONTEXT_ENTRY=SupportLayout vite build --config fixture/apps/contexts/vite.config.module.ts && cross-env CONTEXT_ENTRY=StudioLayout vite build --config fixture/apps/contexts/vite.config.module.ts && cross-env CONTEXT_ENTRY=DefaultShellOverrides vite build --config fixture/apps/contexts/vite.config.module.ts && cross-env CONTEXT_ENTRY=SystemShellOverrides vite build --config fixture/apps/contexts/vite.config.module.ts && cross-env CONTEXT_ENTRY=ReportsShellOverrides vite build --config fixture/apps/contexts/vite.config.module.ts",
|
|
34
|
+
"build:apps": "npm run build:context-apps && vite build --config fixture/apps/notification-sender/vite.config.module.ts && vite build --config fixture/apps/module-manager/vite.config.module.ts && vite build --config fixture/apps/navigation/vite.config.module.ts && vite build --config fixture/apps/quill-editor/vite.config.module.ts && vite build --config fixture/apps/widgets/vite.config.module.ts && vite build --config fixture/apps/handsontable-test/vite.config.module.ts && vite build --config fixture/apps/record-scope-test/vite.config.module.ts && npm run build:catalog-modules",
|
|
34
35
|
"build:ai-assistant": "vite build --config vite.config.ai-assistant.ts",
|
|
35
36
|
"lint": "eslint src",
|
|
36
37
|
"lint:async": "eslint src -c eslint.config.async.mjs"
|
package/system/bindDocs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DOCUMENTATION_DEPARTMENT } from '../lib/docs/DocumentationHandler.js';
|
|
2
2
|
import { buildDocumentationAgentSkills } from '../lib/docs/docsAgentSkills.js';
|
|
3
|
-
import { docsContext, docsDoc, docsKeywords, docsList, docsPage, docsResolve, docsSchema, docsSearch } from '../controllers/docs';
|
|
3
|
+
import { docsContext, docsDoc, docsKeywords, docsList, docsPage, docsResolve, docsSchema, docsSearch } from '../controllers/docs/index.js';
|
|
4
4
|
import { requireAuthAPI, requireAuthUI, requirePermission } from '../policies/authPolicies.js';
|
|
5
5
|
import { documentationToken } from '../policies/permissionResolvers.js';
|
|
6
6
|
import { bindWithMiddlewares } from './routePolicies.js';
|
package/system/bindInertia.js
CHANGED
|
@@ -132,7 +132,7 @@ export function bindInertia(adminizer) {
|
|
|
132
132
|
headerName: 'x-xsrf-token'
|
|
133
133
|
}
|
|
134
134
|
}));
|
|
135
|
-
adminizer.app.use(async (req,
|
|
135
|
+
adminizer.app.use(async (req, res, next) => {
|
|
136
136
|
checkAuth(req, adminizer);
|
|
137
137
|
const defaultLocale = typeof req.adminizer.config.translation !== "boolean"
|
|
138
138
|
? req.adminizer.config.translation.defaultLocale
|
|
@@ -143,13 +143,30 @@ export function bindInertia(adminizer) {
|
|
|
143
143
|
lang: locale,
|
|
144
144
|
});
|
|
145
145
|
const menuHelper = new InertiaMenuHelper(adminizer);
|
|
146
|
-
//
|
|
147
|
-
// the
|
|
148
|
-
//
|
|
149
|
-
|
|
146
|
+
// The layout context of the request decides which menu and which
|
|
147
|
+
// layout the page gets; the default context reproduces the plain panel.
|
|
148
|
+
// Context, menu and switcher are computed once, and only when a page
|
|
149
|
+
// is rendered: API calls and partial reloads that skip them pay nothing.
|
|
150
|
+
let resolvedContext;
|
|
151
|
+
const resolveContext = () => resolvedContext ??= (async () => {
|
|
152
|
+
const context = await adminizer.contextHandler.resolve(req);
|
|
153
|
+
const contexts = req.user ? await adminizer.contextHandler.listAccessible(req.user) : [];
|
|
154
|
+
// Only page visits move the user between contexts: a rendered page
|
|
155
|
+
// asked for by the browser, never an asset or an API call.
|
|
156
|
+
const isPageVisit = req.headers["x-inertia"] !== undefined || (req.headers.accept ?? "").includes("text/html");
|
|
157
|
+
if (req.user && contexts.length > 1 && isPageVisit)
|
|
158
|
+
adminizer.contextHandler.remember(req, res, context);
|
|
159
|
+
return { context, contexts };
|
|
160
|
+
})();
|
|
161
|
+
let resolvedMenu;
|
|
162
|
+
const resolveMenu = () => resolvedMenu ??= (async () => req.user ? menuHelper.getMenuItems(req, (await resolveContext()).context) : null)();
|
|
163
|
+
const contextProps = (context) => adminizer.contextHandler.toProps({ ...context, title: req.i18n.__(context.title) });
|
|
150
164
|
const historyAccess = req.user && req.adminizer.config.history.enabled
|
|
151
165
|
? await req.adminizer.accessRightsHelper.checkPermission(`history-${req.adminizer.config.history?.adapter ?? 'default'}`, req.user)
|
|
152
166
|
: false;
|
|
167
|
+
// Contextual documentation of this page: metadata only, and null when
|
|
168
|
+
// the subsystem is off or the user may not read documentation.
|
|
169
|
+
// A broken implementation must never take the panel down with it.
|
|
153
170
|
let docs = null;
|
|
154
171
|
try {
|
|
155
172
|
docs = req.user ? await adminizer.documentationHandler.forContextMeta(req.user, req.path) : null;
|
|
@@ -166,16 +183,21 @@ export function bindInertia(adminizer) {
|
|
|
166
183
|
locale,
|
|
167
184
|
common: commonMessages,
|
|
168
185
|
},
|
|
169
|
-
menu,
|
|
170
|
-
menuSections: req.user ? menuHelper.getSections(req) : null,
|
|
186
|
+
menu: resolveMenu,
|
|
187
|
+
menuSections: async () => req.user ? menuHelper.getSections(req, (await resolveContext()).context) : null,
|
|
188
|
+
adminContext: async () => contextProps((await resolveContext()).context),
|
|
189
|
+
adminContexts: async () => (await resolveContext()).contexts
|
|
190
|
+
.filter((item) => item.switcher !== false)
|
|
191
|
+
.map(contextProps),
|
|
171
192
|
// Auto breadcrumbs. A function prop is evaluated by the Inertia
|
|
172
193
|
// adapter only when the key is rendered and only when the page did
|
|
173
194
|
// not send `breadcrumbs` of its own — page props override shared
|
|
174
195
|
// props before evaluation.
|
|
175
|
-
...(
|
|
196
|
+
...(req.user && adminizer.config.navbar?.breadcrumbs === 'auto' ? {
|
|
176
197
|
breadcrumbs: async () => {
|
|
177
198
|
try {
|
|
178
|
-
const
|
|
199
|
+
const menu = await resolveMenu();
|
|
200
|
+
const crumbs = await adminizer.adminLinkHandler.locate(req.user, req.originalUrl || req.url, menu ?? undefined);
|
|
179
201
|
return crumbs.map((crumb) => ({ ...crumb, title: req.i18n.__(crumb.title) }));
|
|
180
202
|
}
|
|
181
203
|
catch (error) {
|
|
@@ -224,6 +246,9 @@ export function bindInertia(adminizer) {
|
|
|
224
246
|
});
|
|
225
247
|
next();
|
|
226
248
|
});
|
|
249
|
+
// Pages declared to belong to a layout context the user may not enter are
|
|
250
|
+
// blocked here, before any route; `req.user` is known by now.
|
|
251
|
+
adminizer.app.use(adminizer.contextHandler.guard());
|
|
227
252
|
}
|
|
228
253
|
function checkAuth(req, adminizer) {
|
|
229
254
|
let locale = "";
|