@acorex-platform/module-platform-management-client 0.0.21

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.
Files changed (45) hide show
  1. package/fesm2022/acorex-platform-module-platform-management-client-acorex-platform-module-platform-management-client-V_ao4WVM.mjs +4599 -0
  2. package/fesm2022/acorex-platform-module-platform-management-client-acorex-platform-module-platform-management-client-V_ao4WVM.mjs.map +1 -0
  3. package/fesm2022/acorex-platform-module-platform-management-client-contracts.mjs +179 -0
  4. package/fesm2022/acorex-platform-module-platform-management-client-contracts.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-module-platform-management-client-create-user-query.command-2Qu7aoCp.mjs +1542 -0
  6. package/fesm2022/acorex-platform-module-platform-management-client-create-user-query.command-2Qu7aoCp.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-module-platform-management-client-create-user-query.types-DdygAori.mjs +71 -0
  8. package/fesm2022/acorex-platform-module-platform-management-client-create-user-query.types-DdygAori.mjs.map +1 -0
  9. package/fesm2022/acorex-platform-module-platform-management-client-customize-menu.command-2319iHsD.mjs +231 -0
  10. package/fesm2022/acorex-platform-module-platform-management-client-customize-menu.command-2319iHsD.mjs.map +1 -0
  11. package/fesm2022/acorex-platform-module-platform-management-client-data-list-pick-entity-item.command-CJVpqPmY.mjs +52 -0
  12. package/fesm2022/acorex-platform-module-platform-management-client-data-list-pick-entity-item.command-CJVpqPmY.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-module-platform-management-client-entity-master-actions-for-ai.util-DmGyoy9o.mjs +55 -0
  14. package/fesm2022/acorex-platform-module-platform-management-client-entity-master-actions-for-ai.util-DmGyoy9o.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-module-platform-management-client-execute-token.command-DNuHR-0y.mjs +152 -0
  16. package/fesm2022/acorex-platform-module-platform-management-client-execute-token.command-DNuHR-0y.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-module-platform-management-client-list-catalog-scopes.query-ByhDTWE6.mjs +71 -0
  18. package/fesm2022/acorex-platform-module-platform-management-client-list-catalog-scopes.query-ByhDTWE6.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-module-platform-management-client-list-editor-widgets.query-DITPMmbw.mjs +68 -0
  20. package/fesm2022/acorex-platform-module-platform-management-client-list-editor-widgets.query-DITPMmbw.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-module-platform-management-client-list-platform-navigation-for-ai.query-2oHMITw-.mjs +194 -0
  22. package/fesm2022/acorex-platform-module-platform-management-client-list-platform-navigation-for-ai.query-2oHMITw-.mjs.map +1 -0
  23. package/fesm2022/acorex-platform-module-platform-management-client-list-registered-entities.query-DjStd3a8.mjs +136 -0
  24. package/fesm2022/acorex-platform-module-platform-management-client-list-registered-entities.query-DjStd3a8.mjs.map +1 -0
  25. package/fesm2022/acorex-platform-module-platform-management-client-list-registered-queries.query-C-IfkTel.mjs +122 -0
  26. package/fesm2022/acorex-platform-module-platform-management-client-list-registered-queries.query-C-IfkTel.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-module-platform-management-client-menu-list.component-C5HH5-Dw.mjs +524 -0
  28. package/fesm2022/acorex-platform-module-platform-management-client-menu-list.component-C5HH5-Dw.mjs.map +1 -0
  29. package/fesm2022/acorex-platform-module-platform-management-client-platform-console-charts.queries-CQ34R13-.mjs +216 -0
  30. package/fesm2022/acorex-platform-module-platform-management-client-platform-console-charts.queries-CQ34R13-.mjs.map +1 -0
  31. package/fesm2022/acorex-platform-module-platform-management-client-platform-console-data.util-BIkULtKk.mjs +81 -0
  32. package/fesm2022/acorex-platform-module-platform-management-client-platform-console-data.util-BIkULtKk.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-module-platform-management-client-platform-console-kpi.queries-BkPdhVq_.mjs +137 -0
  34. package/fesm2022/acorex-platform-module-platform-management-client-platform-console-kpi.queries-BkPdhVq_.mjs.map +1 -0
  35. package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-column.component-CYb2zOSj.mjs +45 -0
  36. package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-column.component-CYb2zOSj.mjs.map +1 -0
  37. package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-edit.component-BBwrzcXa.mjs +178 -0
  38. package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-edit.component-BBwrzcXa.mjs.map +1 -0
  39. package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-view.component-DBlZVz6u.mjs +45 -0
  40. package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-view.component-DBlZVz6u.mjs.map +1 -0
  41. package/fesm2022/acorex-platform-module-platform-management-client.mjs +2 -0
  42. package/fesm2022/acorex-platform-module-platform-management-client.mjs.map +1 -0
  43. package/package.json +28 -0
  44. package/types/acorex-platform-module-platform-management-client-contracts.d.ts +258 -0
  45. package/types/acorex-platform-module-platform-management-client.d.ts +548 -0
@@ -0,0 +1,1542 @@
1
+ import { resolveLayoutBuilderDialogResult, AXPLayoutBuilderService } from '@acorex-platform/framework-client/layout/builder';
2
+ import { AXPEntityResolver } from '@acorex-platform/framework-client/layout/entity';
3
+ import { resolveEntityActionCommandInput, entityActionDataRecord } from '@acorex-platform/framework-shared/runtime';
4
+ import { AXToastService } from '@acorex/components/toast';
5
+ import { AXTranslationService } from '@acorex/core/translation';
6
+ import * as i0 from '@angular/core';
7
+ import { inject, Injectable } from '@angular/core';
8
+ import { AXPWidgetsList } from '@acorex-platform/framework-shared/core';
9
+ import { parsePropertyExposeEntries } from '@acorex-platform/framework-shared/entity';
10
+ import { C as CREATE_USER_QUERY_I18N, a as CREATE_USER_QUERY_WIZARD_NAME, b as CREATE_USER_QUERY_ENTITY_CATALOG } from './acorex-platform-module-platform-management-client-create-user-query.types-DdygAori.mjs';
11
+ import { n as normalizeRestApiOutput, c as createEmptyRestApiOutput, R as REST_API_GENERATOR_WIDGET_NAME } from './acorex-platform-module-platform-management-client-acorex-platform-module-platform-management-client-V_ao4WVM.mjs';
12
+
13
+ //#region ----- Imports -----
14
+ //#endregion
15
+ //#region ----- Constants -----
16
+ const LOOKUP_WIDGET_TYPES = new Set(['lookup-editor', 'select-editor']);
17
+ const USER_QUERY_RELATED_DEPTH = 3;
18
+ //#endregion
19
+ //#region ----- Public API -----
20
+ function parseEntityFullName(fullName) {
21
+ const trimmed = fullName.trim();
22
+ const separator = trimmed.indexOf('.');
23
+ if (separator <= 0 || separator === trimmed.length - 1) {
24
+ return null;
25
+ }
26
+ return {
27
+ module: trimmed.slice(0, separator),
28
+ name: trimmed.slice(separator + 1),
29
+ };
30
+ }
31
+ function resolvePickedEntityFullName(value) {
32
+ if (typeof value === 'string') {
33
+ return value.trim();
34
+ }
35
+ if (value == null || typeof value !== 'object') {
36
+ return '';
37
+ }
38
+ const record = value;
39
+ const id = typeof record['id'] === 'string' ? record['id'].trim() : '';
40
+ if (id.includes('.')) {
41
+ return id;
42
+ }
43
+ const moduleName = typeof record['module_id'] === 'string' ? record['module_id'].trim() : '';
44
+ const name = typeof record['name'] === 'string' ? record['name'].trim() : '';
45
+ if (moduleName && name) {
46
+ return `${moduleName}.${name}`;
47
+ }
48
+ return id;
49
+ }
50
+ function collectUserQueryEntityFieldGroups(entity, relatedByName) {
51
+ return collectGroups(entity, relatedByName, '', new Set());
52
+ }
53
+ function flattenEntityFieldGroups(groups) {
54
+ const result = [];
55
+ const walk = (group) => {
56
+ result.push(group);
57
+ for (const child of group.children ?? []) {
58
+ walk(child);
59
+ }
60
+ };
61
+ for (const group of groups) {
62
+ walk(group);
63
+ }
64
+ return result;
65
+ }
66
+ function defaultSelectedFieldGroups(groups) {
67
+ const selected = {};
68
+ for (const group of flattenEntityFieldGroups(groups)) {
69
+ selected[group.id] = [];
70
+ }
71
+ for (const group of groups) {
72
+ if (group.kind !== 'relation') {
73
+ selected[group.id] = group.items.map((item) => item.path);
74
+ }
75
+ }
76
+ return selected;
77
+ }
78
+ function collectSelectedFieldGroupPaths(fieldGroups, groups) {
79
+ const paths = [];
80
+ const walk = (group) => {
81
+ paths.push(...normalizeSelectedFieldPaths(fieldGroups?.[group.id]));
82
+ for (const child of group.children ?? []) {
83
+ walk(child);
84
+ }
85
+ };
86
+ for (const group of groups) {
87
+ walk(group);
88
+ }
89
+ return paths;
90
+ }
91
+ function withRequiredIdField(paths) {
92
+ const unique = [];
93
+ const seen = new Set();
94
+ for (const path of paths) {
95
+ const trimmed = path.trim();
96
+ if (!trimmed || seen.has(trimmed)) {
97
+ continue;
98
+ }
99
+ seen.add(trimmed);
100
+ unique.push(trimmed);
101
+ }
102
+ if (!seen.has('id')) {
103
+ unique.unshift('id');
104
+ }
105
+ return unique;
106
+ }
107
+ function normalizeSelectedFieldPaths(value) {
108
+ if (!Array.isArray(value)) {
109
+ return [];
110
+ }
111
+ const paths = [];
112
+ for (const item of value) {
113
+ if (typeof item === 'string') {
114
+ paths.push(item);
115
+ continue;
116
+ }
117
+ if (item != null && typeof item === 'object') {
118
+ const path = item.path;
119
+ if (typeof path === 'string') {
120
+ paths.push(path);
121
+ }
122
+ }
123
+ }
124
+ return paths;
125
+ }
126
+ //#endregion
127
+ //#region ----- Group collection -----
128
+ function collectGroups(entity, relatedByName, pathPrefix, ancestry) {
129
+ const groups = [];
130
+ const { host, lookups } = collectHostAndLookups(entity, pathPrefix);
131
+ if (host.items.length) {
132
+ groups.push(host);
133
+ }
134
+ groups.push(...lookups);
135
+ const hostFullName = `${entity.module}.${entity.name}`;
136
+ const nextAncestry = new Set(ancestry);
137
+ nextAncestry.add(hostFullName);
138
+ for (const related of entity.relatedEntities ?? []) {
139
+ const group = collectRelatedGroup(related, relatedByName, pathPrefix, nextAncestry);
140
+ if (group) {
141
+ groups.push(group);
142
+ }
143
+ }
144
+ return groups;
145
+ }
146
+ function collectHostAndLookups(entity, pathPrefix, allowedNames) {
147
+ const hostItems = [];
148
+ const lookups = [];
149
+ for (const property of entity?.properties ?? []) {
150
+ const name = property.name?.trim();
151
+ if (!name || name.toLowerCase() === 'id') {
152
+ continue;
153
+ }
154
+ if (allowedNames && !allowedNames.has(name)) {
155
+ continue;
156
+ }
157
+ if (isLookupProperty(property)) {
158
+ const lookupGroup = collectLookupGroup(property, pathPrefix);
159
+ if (lookupGroup) {
160
+ lookups.push(lookupGroup);
161
+ }
162
+ continue;
163
+ }
164
+ hostItems.push({
165
+ path: joinPath(pathPrefix, name),
166
+ title: property.title,
167
+ fallback: name,
168
+ });
169
+ }
170
+ return {
171
+ host: {
172
+ id: pathPrefix ? `rel_${sanitizeGroupId(pathPrefix)}_fields` : 'host',
173
+ kind: pathPrefix ? 'relation' : 'host',
174
+ title: '',
175
+ fallback: 'fields',
176
+ items: hostItems,
177
+ },
178
+ lookups,
179
+ };
180
+ }
181
+ function collectLookupGroup(property, pathPrefix) {
182
+ const name = property.name?.trim();
183
+ if (!name) {
184
+ return null;
185
+ }
186
+ const items = [
187
+ {
188
+ path: joinPath(pathPrefix, name),
189
+ title: property.title,
190
+ fallback: name,
191
+ },
192
+ ];
193
+ const opts = property.schema?.interface?.options;
194
+ for (const entry of parsePropertyExposeEntries(opts)) {
195
+ const target = entry.target.trim();
196
+ if (!target || target === name) {
197
+ continue;
198
+ }
199
+ items.push({
200
+ path: joinPath(pathPrefix, target),
201
+ title: entry.source,
202
+ fallback: entry.source.trim() || target,
203
+ });
204
+ }
205
+ return {
206
+ id: `lookup_${sanitizeGroupId(joinPath(pathPrefix, name))}`,
207
+ kind: 'lookup',
208
+ title: property.title,
209
+ fallback: name,
210
+ items,
211
+ };
212
+ }
213
+ function collectRelatedGroup(related, relatedByName, pathPrefix, ancestry) {
214
+ if (related.hidden === true) {
215
+ return null;
216
+ }
217
+ const entityName = related.entity?.trim();
218
+ if (!entityName || ancestry.has(entityName)) {
219
+ return null;
220
+ }
221
+ const relatedEntity = relatedByName.get(entityName);
222
+ const dataPath = related.persistence?.dataPath?.trim() || entityName.split('.').pop() || entityName;
223
+ const pathRoot = joinPath(pathPrefix, dataPath);
224
+ const allowed = resolveAllowedPropertyNames(related, relatedEntity);
225
+ const { host, lookups } = collectHostAndLookups(relatedEntity, pathRoot, allowed);
226
+ const childAncestry = new Set(ancestry);
227
+ childAncestry.add(entityName);
228
+ const children = [...lookups];
229
+ for (const nested of relatedEntity?.relatedEntities ?? []) {
230
+ const child = collectRelatedGroup(nested, relatedByName, pathRoot, childAncestry);
231
+ if (child) {
232
+ children.push(child);
233
+ }
234
+ }
235
+ if (!host.items.length && !children.length) {
236
+ return null;
237
+ }
238
+ return {
239
+ id: `rel_${sanitizeGroupId(pathRoot)}`,
240
+ kind: 'relation',
241
+ title: related.title,
242
+ fallback: entityName,
243
+ items: host.items,
244
+ children: children.length ? children : undefined,
245
+ };
246
+ }
247
+ function resolveAllowedPropertyNames(related, relatedEntity) {
248
+ const excluded = new Set((related.excludeProperties ?? [])
249
+ .filter((name) => typeof name === 'string')
250
+ .map((name) => name.trim())
251
+ .filter(Boolean));
252
+ const fromConfig = Array.isArray(related.properties)
253
+ ? related.properties.filter((name) => typeof name === 'string' && name.trim().length > 0)
254
+ : [];
255
+ if (fromConfig.length) {
256
+ return new Set(fromConfig.map((name) => name.trim()).filter((name) => name.toLowerCase() !== 'id' && !excluded.has(name)));
257
+ }
258
+ if (!excluded.size) {
259
+ return undefined;
260
+ }
261
+ const names = (relatedEntity?.properties ?? [])
262
+ .map((property) => property.name?.trim() ?? '')
263
+ .filter((name) => name && name.toLowerCase() !== 'id' && !excluded.has(name));
264
+ return new Set(names);
265
+ }
266
+ function isLookupProperty(property) {
267
+ const iface = property.schema?.interface;
268
+ const opts = iface?.options;
269
+ const lookupEntity = typeof opts?.['entity'] === 'string' ? String(opts['entity']).trim() : '';
270
+ if (!lookupEntity) {
271
+ return false;
272
+ }
273
+ const widgetType = typeof iface?.type === 'string' ? iface.type : '';
274
+ if (LOOKUP_WIDGET_TYPES.has(widgetType)) {
275
+ return true;
276
+ }
277
+ return /(Ids|IDs|Id|ID)$/i.test(property.name);
278
+ }
279
+ function joinPath(prefix, segment) {
280
+ return prefix ? `${prefix}.${segment}` : segment;
281
+ }
282
+ function sanitizeGroupId(value) {
283
+ return value.replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_|_$/g, '');
284
+ }
285
+ //#endregion
286
+
287
+ //#region ----- Imports -----
288
+ const ENTITY_WIZARD_BACK = 'back';
289
+ const API_WIZARD_BACK = 'back';
290
+ //#endregion
291
+ //#region ----- Public API -----
292
+ async function openCreateUserQueryWizard(args) {
293
+ if (args.sourceKind === 'advanced') {
294
+ return openAdvancedPlaceholder(args);
295
+ }
296
+ if (args.sourceKind === 'entity') {
297
+ return openEntitySourceWizard(args);
298
+ }
299
+ if (args.sourceKind === 'api') {
300
+ return openApiSourceWizard(args);
301
+ }
302
+ const result = await args.layoutBuilder
303
+ .create()
304
+ .dialog((dialog) => {
305
+ configureSimpleSourceWizard(dialog, args);
306
+ })
307
+ .show();
308
+ return resolveLayoutBuilderDialogResult(result);
309
+ }
310
+ //#endregion
311
+ //#region ----- Entity sub-steps -----
312
+ async function openEntitySourceWizard(args) {
313
+ let entityName = '';
314
+ let previousFields;
315
+ while (true) {
316
+ const picked = await openEntityPickDialog(args, entityName);
317
+ if (!picked) {
318
+ return null;
319
+ }
320
+ const graph = await loadEntityFieldGraph(args, picked);
321
+ if (!graph) {
322
+ entityName = picked;
323
+ continue;
324
+ }
325
+ const selectedGroups = picked === entityName && previousFields != null ? previousFields : graph.defaultGroups;
326
+ const outcome = await openEntityFieldsDialog(args, picked, graph.groups, selectedGroups);
327
+ if (outcome === ENTITY_WIZARD_BACK) {
328
+ entityName = picked;
329
+ previousFields = selectedGroups;
330
+ continue;
331
+ }
332
+ return outcome;
333
+ }
334
+ }
335
+ async function openEntityPickDialog(args, entityName) {
336
+ const result = await args.layoutBuilder
337
+ .create()
338
+ .dialog((dialog) => {
339
+ dialog
340
+ .setTitle(CREATE_USER_QUERY_I18N.titles.entity)
341
+ .setSize('lg')
342
+ .setCloseButton(true)
343
+ .setContext(createInitialContext('entity', entityName))
344
+ .content((flex) => {
345
+ flex.setDirection('column').setGap('16px').stepWizard((wizard) => {
346
+ wizard
347
+ .name(CREATE_USER_QUERY_WIZARD_NAME)
348
+ .setLook('circular-icon')
349
+ .setLinear(true)
350
+ .setDirection('horizontal')
351
+ .setShowActions(false)
352
+ .step('entity', CREATE_USER_QUERY_I18N.steps.entity, (step) => {
353
+ step.setIcon('fa-light fa-database').content((layout) => {
354
+ fillEntityPickStep(layout);
355
+ });
356
+ })
357
+ .step('fields', CREATE_USER_QUERY_I18N.steps.fields, (step) => {
358
+ step.setIcon('fa-light fa-diagram-project').content((layout) => {
359
+ fillHintStep(layout, CREATE_USER_QUERY_I18N.steps.fields, CREATE_USER_QUERY_I18N.placeholders.fieldsNext);
360
+ });
361
+ })
362
+ .step('contract', CREATE_USER_QUERY_I18N.steps.contract, (step) => {
363
+ step.setIcon('fa-light fa-file-contract').content((layout) => {
364
+ fillContractPlaceholderStep(layout);
365
+ });
366
+ });
367
+ });
368
+ })
369
+ .setActions((actions) => {
370
+ actions
371
+ .cancel('@general:actions.cancel.title')
372
+ .custom({
373
+ title: '@general:actions.next.title',
374
+ command: 'continue-to-fields',
375
+ icon: 'fa-regular fa-arrow-right',
376
+ color: 'primary',
377
+ hidden: '{{!api.getStatus().isFirst}}',
378
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
379
+ })
380
+ .custom({
381
+ title: '@general:actions.next.title',
382
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.next`,
383
+ icon: 'fa-regular fa-arrow-right',
384
+ color: 'primary',
385
+ hidden: '{{api.getStatus().isFirst || api.getStatus().isLast}}',
386
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
387
+ })
388
+ .custom({
389
+ title: '@general:actions.submit.title',
390
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.submit`,
391
+ icon: 'fa-regular fa-check',
392
+ color: 'success',
393
+ hidden: '{{!api.getStatus().isLast}}',
394
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
395
+ });
396
+ })
397
+ .onAction(async (dialogRef) => {
398
+ if (dialogRef.action() === 'cancel') {
399
+ return null;
400
+ }
401
+ const context = dialogRef.context() ?? null;
402
+ const name = resolvePickedEntityFullName(context?.source?.entity?.entityName);
403
+ if (!name) {
404
+ throw new Error(CREATE_USER_QUERY_I18N.errors.entityRequired);
405
+ }
406
+ return name;
407
+ });
408
+ })
409
+ .show();
410
+ return resolveLayoutBuilderDialogResult(result);
411
+ }
412
+ async function openEntityFieldsDialog(args, entityName, groups, selectedGroups) {
413
+ const result = await args.layoutBuilder
414
+ .create()
415
+ .dialog((dialog) => {
416
+ dialog
417
+ .setTitle(CREATE_USER_QUERY_I18N.titles.entity)
418
+ .setSize('xl')
419
+ .setCloseButton(true)
420
+ .setContext(createInitialContext('entity', entityName, [], selectedGroups))
421
+ .content((flex) => {
422
+ flex.setDirection('column').setGap('16px').stepWizard((wizard) => {
423
+ wizard
424
+ .name(CREATE_USER_QUERY_WIZARD_NAME)
425
+ .setLook('circular-icon')
426
+ .setLinear(true)
427
+ .setDirection('horizontal')
428
+ .setShowActions(false)
429
+ .setGuards({
430
+ next: (payload) => canLeaveEntityFieldsStep(payload, args, groups),
431
+ })
432
+ .step('fields', CREATE_USER_QUERY_I18N.steps.fields, (step) => {
433
+ step.setIcon('fa-light fa-diagram-project').content((layout) => {
434
+ fillEntityFieldsStep(layout, entityName, groups);
435
+ });
436
+ })
437
+ .step('contract', CREATE_USER_QUERY_I18N.steps.contract, (step) => {
438
+ step.setIcon('fa-light fa-file-contract').content((layout) => {
439
+ fillContractPlaceholderStep(layout);
440
+ });
441
+ });
442
+ });
443
+ })
444
+ .setActions((actions) => {
445
+ actions
446
+ .cancel('@general:actions.cancel.title')
447
+ .custom({
448
+ title: '@general:actions.previous.title',
449
+ command: 'back-to-entity',
450
+ icon: 'fa-regular fa-arrow-left',
451
+ color: 'primary',
452
+ position: 'prefix',
453
+ hidden: '{{!api.getStatus().isFirst}}',
454
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
455
+ })
456
+ .custom({
457
+ title: '@general:actions.previous.title',
458
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.previous`,
459
+ icon: 'fa-regular fa-arrow-left',
460
+ color: 'primary',
461
+ position: 'prefix',
462
+ hidden: '{{api.getStatus().isFirst}}',
463
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
464
+ })
465
+ .custom({
466
+ title: '@general:actions.next.title',
467
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.next`,
468
+ icon: 'fa-regular fa-arrow-right',
469
+ color: 'primary',
470
+ hidden: '{{api.getStatus().isLast}}',
471
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
472
+ })
473
+ .custom({
474
+ title: '@general:actions.submit.title',
475
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.submit`,
476
+ icon: 'fa-regular fa-check',
477
+ color: 'success',
478
+ hidden: '{{!api.getStatus().isLast}}',
479
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
480
+ });
481
+ })
482
+ .onAction(async (dialogRef) => {
483
+ if (dialogRef.action() === 'cancel') {
484
+ return null;
485
+ }
486
+ if (dialogRef.action() === 'back-to-entity') {
487
+ return ENTITY_WIZARD_BACK;
488
+ }
489
+ const context = dialogRef.context() ?? null;
490
+ if (!context) {
491
+ return null;
492
+ }
493
+ const fields = withRequiredIdField(collectSelectedFieldGroupPaths(context.source?.entity?.fieldGroups, groups));
494
+ if (countFieldGroupItems(groups) > 0 && fields.filter((path) => path !== 'id').length === 0) {
495
+ throw new Error(CREATE_USER_QUERY_I18N.errors.fieldsRequired);
496
+ }
497
+ return {
498
+ ...context,
499
+ source: {
500
+ ...context.source,
501
+ kind: 'entity',
502
+ entity: {
503
+ entityName,
504
+ fields,
505
+ fieldGroups: context.source?.entity?.fieldGroups,
506
+ },
507
+ },
508
+ };
509
+ });
510
+ })
511
+ .show();
512
+ return resolveLayoutBuilderDialogResult(result);
513
+ }
514
+ async function loadEntityFieldGraph(args, entityName) {
515
+ const parsed = parseEntityFullName(entityName);
516
+ if (!parsed) {
517
+ await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.entityLoadFailed);
518
+ return null;
519
+ }
520
+ const entity = await args.entityResolver.get(parsed.module, parsed.name);
521
+ if (!entity) {
522
+ await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.entityLoadFailed);
523
+ return null;
524
+ }
525
+ const relatedByName = await loadRelatedEntityClosure(args.entityResolver, entity.relatedEntities ?? []);
526
+ const groups = collectUserQueryEntityFieldGroups(entity, relatedByName);
527
+ return {
528
+ groups: await toFieldGroupViews(groups, args.translationService),
529
+ defaultGroups: defaultSelectedFieldGroups(groups),
530
+ };
531
+ }
532
+ async function loadRelatedEntityClosure(entityResolver, roots) {
533
+ const relatedByName = new Map();
534
+ let pending = [...new Set(roots.map((item) => item.entity?.trim() ?? '').filter(Boolean))];
535
+ for (let depth = 1; depth <= USER_QUERY_RELATED_DEPTH && pending.length > 0; depth += 1) {
536
+ const batch = pending.filter((name) => !relatedByName.has(name));
537
+ pending = [];
538
+ await Promise.all(batch.map(async (fullName) => {
539
+ const parsed = parseEntityFullName(fullName);
540
+ if (!parsed) {
541
+ return;
542
+ }
543
+ const definition = await entityResolver.get(parsed.module, parsed.name);
544
+ if (!definition) {
545
+ return;
546
+ }
547
+ relatedByName.set(fullName, definition);
548
+ for (const nested of definition.relatedEntities ?? []) {
549
+ const nestedName = nested.entity?.trim();
550
+ if (nestedName && !relatedByName.has(nestedName)) {
551
+ pending.push(nestedName);
552
+ }
553
+ }
554
+ }));
555
+ }
556
+ return relatedByName;
557
+ }
558
+ //#endregion
559
+ //#region ----- API sub-steps -----
560
+ async function openApiSourceWizard(args) {
561
+ let previous = createApiWizardContext();
562
+ while (true) {
563
+ const tested = await openApiSourceDialog(args, previous);
564
+ if (!tested) {
565
+ return null;
566
+ }
567
+ const groups = inferJsonFieldGroups(tested.source?.api?.lastTest?.body);
568
+ const views = await toFieldGroupViews(groups, args.translationService);
569
+ const selected = isSameApiRequest(previous, tested) && previous.source?.api?.fieldGroups
570
+ ? previous.source.api.fieldGroups
571
+ : defaultHostFieldGroups(groups);
572
+ const outcome = await openApiFieldsDialog(args, tested, views, selected);
573
+ if (outcome === API_WIZARD_BACK) {
574
+ previous = tested;
575
+ continue;
576
+ }
577
+ return outcome;
578
+ }
579
+ }
580
+ async function openApiSourceDialog(args, previous) {
581
+ const result = await args.layoutBuilder
582
+ .create()
583
+ .dialog((dialog) => {
584
+ dialog
585
+ .setTitle(CREATE_USER_QUERY_I18N.titles.api)
586
+ .setSize('xl')
587
+ .setCloseButton(true)
588
+ .setContext(previous)
589
+ .content((flex) => {
590
+ flex.setDirection('column').setGap('16px').stepWizard((wizard) => {
591
+ wizard
592
+ .name(CREATE_USER_QUERY_WIZARD_NAME)
593
+ .setLook('circular-icon')
594
+ .setLinear(true)
595
+ .setDirection('horizontal')
596
+ .setShowActions(false)
597
+ .step('source', CREATE_USER_QUERY_I18N.steps.source, (step) => {
598
+ step.setIcon('fa-light fa-cloud-arrow-up').content((layout) => {
599
+ fillApiSourceStep(layout);
600
+ });
601
+ })
602
+ .step('fields', CREATE_USER_QUERY_I18N.steps.fields, (step) => {
603
+ step.setIcon('fa-light fa-diagram-project').content((layout) => {
604
+ fillHintStep(layout, CREATE_USER_QUERY_I18N.steps.fields, CREATE_USER_QUERY_I18N.placeholders.apiFieldsNext);
605
+ });
606
+ })
607
+ .step('contract', CREATE_USER_QUERY_I18N.steps.contract, (step) => {
608
+ step.setIcon('fa-light fa-file-contract').content((layout) => {
609
+ fillContractPlaceholderStep(layout);
610
+ });
611
+ });
612
+ });
613
+ })
614
+ .setActions((actions) => {
615
+ actions
616
+ .cancel('@general:actions.cancel.title')
617
+ .custom({
618
+ title: '@general:actions.next.title',
619
+ command: 'continue-to-fields',
620
+ icon: 'fa-regular fa-arrow-right',
621
+ color: 'primary',
622
+ hidden: '{{!api.getStatus().isFirst}}',
623
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
624
+ })
625
+ .custom({
626
+ title: '@general:actions.next.title',
627
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.next`,
628
+ icon: 'fa-regular fa-arrow-right',
629
+ color: 'primary',
630
+ hidden: '{{api.getStatus().isFirst || api.getStatus().isLast}}',
631
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
632
+ })
633
+ .custom({
634
+ title: '@general:actions.submit.title',
635
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.submit`,
636
+ icon: 'fa-regular fa-check',
637
+ color: 'success',
638
+ hidden: '{{!api.getStatus().isLast}}',
639
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
640
+ });
641
+ })
642
+ .onAction(async (dialogRef) => {
643
+ if (dialogRef.action() === 'cancel') {
644
+ return null;
645
+ }
646
+ const context = dialogRef.context() ?? null;
647
+ const request = normalizeRestApiOutput(context?.source?.api?.request);
648
+ if (!request.url) {
649
+ throw new Error(CREATE_USER_QUERY_I18N.errors.apiUrlRequired);
650
+ }
651
+ const lastTest = await runRestApiRequest(request);
652
+ if (!lastTest.ok) {
653
+ await showApiTestFailure(args, lastTest);
654
+ throw new Error(lastTest.status === 0 ? CREATE_USER_QUERY_I18N.errors.apiTestNetwork : CREATE_USER_QUERY_I18N.errors.apiTestFailed);
655
+ }
656
+ await showWizardWarning(args, CREATE_USER_QUERY_I18N.placeholders.apiTestOk, { status: String(lastTest.status) }, 'success');
657
+ return createApiWizardContext({
658
+ connectionId: context?.source?.api?.connectionId,
659
+ request,
660
+ lastTest,
661
+ fieldGroups: context?.source?.api?.fieldGroups,
662
+ });
663
+ });
664
+ })
665
+ .show();
666
+ return resolveLayoutBuilderDialogResult(result);
667
+ }
668
+ async function openApiFieldsDialog(args, tested, groups, selectedGroups) {
669
+ const result = await args.layoutBuilder
670
+ .create()
671
+ .dialog((dialog) => {
672
+ dialog
673
+ .setTitle(CREATE_USER_QUERY_I18N.titles.api)
674
+ .setSize('xl')
675
+ .setCloseButton(true)
676
+ .setContext(createApiWizardContext(tested.source?.api, selectedGroups))
677
+ .content((flex) => {
678
+ flex.setDirection('column').setGap('16px').stepWizard((wizard) => {
679
+ wizard
680
+ .name(CREATE_USER_QUERY_WIZARD_NAME)
681
+ .setLook('circular-icon')
682
+ .setLinear(true)
683
+ .setDirection('horizontal')
684
+ .setShowActions(false)
685
+ .setGuards({
686
+ next: (payload) => canLeaveApiFieldsStep(payload, args, groups),
687
+ })
688
+ .step('fields', CREATE_USER_QUERY_I18N.steps.fields, (step) => {
689
+ step.setIcon('fa-light fa-diagram-project').content((layout) => {
690
+ fillApiFieldsStep(layout, groups);
691
+ });
692
+ })
693
+ .step('contract', CREATE_USER_QUERY_I18N.steps.contract, (step) => {
694
+ step.setIcon('fa-light fa-file-contract').content((layout) => {
695
+ fillContractPlaceholderStep(layout);
696
+ });
697
+ });
698
+ });
699
+ })
700
+ .setActions((actions) => {
701
+ actions
702
+ .cancel('@general:actions.cancel.title')
703
+ .custom({
704
+ title: '@general:actions.previous.title',
705
+ command: 'back-to-source',
706
+ icon: 'fa-regular fa-arrow-left',
707
+ color: 'primary',
708
+ position: 'prefix',
709
+ hidden: '{{!api.getStatus().isFirst}}',
710
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
711
+ })
712
+ .custom({
713
+ title: '@general:actions.previous.title',
714
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.previous`,
715
+ icon: 'fa-regular fa-arrow-left',
716
+ color: 'primary',
717
+ position: 'prefix',
718
+ hidden: '{{api.getStatus().isFirst}}',
719
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
720
+ })
721
+ .custom({
722
+ title: '@general:actions.next.title',
723
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.next`,
724
+ icon: 'fa-regular fa-arrow-right',
725
+ color: 'primary',
726
+ hidden: '{{api.getStatus().isLast}}',
727
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
728
+ })
729
+ .custom({
730
+ title: '@general:actions.submit.title',
731
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.submit`,
732
+ icon: 'fa-regular fa-check',
733
+ color: 'success',
734
+ hidden: '{{!api.getStatus().isLast}}',
735
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
736
+ });
737
+ })
738
+ .onAction(async (dialogRef) => {
739
+ if (dialogRef.action() === 'cancel') {
740
+ return null;
741
+ }
742
+ if (dialogRef.action() === 'back-to-source') {
743
+ return API_WIZARD_BACK;
744
+ }
745
+ const context = dialogRef.context() ?? null;
746
+ if (!context) {
747
+ return null;
748
+ }
749
+ const fields = collectSelectedFieldGroupPaths(context.source?.api?.fieldGroups, groups);
750
+ if (countFieldGroupItems(groups) > 0 && fields.length === 0) {
751
+ throw new Error(CREATE_USER_QUERY_I18N.errors.fieldsRequired);
752
+ }
753
+ return createApiWizardContext({
754
+ ...tested.source?.api,
755
+ fields,
756
+ fieldGroups: context.source?.api?.fieldGroups,
757
+ });
758
+ });
759
+ })
760
+ .show();
761
+ return resolveLayoutBuilderDialogResult(result);
762
+ }
763
+ function fillApiFieldsStep(layout, groups) {
764
+ layout.flex((flex) => {
765
+ flex.setDirection('column').setGap('16px').setAlignItems('stretch');
766
+ flex.fieldset((fieldset) => {
767
+ fieldset
768
+ .setTitle(CREATE_USER_QUERY_I18N.fields.sample)
769
+ .setDescription(CREATE_USER_QUERY_I18N.placeholders.selectedApiHint)
770
+ .setIcon('fa-light fa-code')
771
+ .setCols(1)
772
+ .formField(CREATE_USER_QUERY_I18N.fields.status, (field) => {
773
+ field.path('source.api.lastTest.status').textBox().readonly(true);
774
+ })
775
+ .formField(CREATE_USER_QUERY_I18N.fields.sample, (field) => {
776
+ field.path('source.api.lastTest.body').customWidget('json-viewer', {}).layout(1);
777
+ });
778
+ });
779
+ for (const group of groups) {
780
+ renderFieldGroup(flex, group, 'source.api.fieldGroups');
781
+ }
782
+ });
783
+ }
784
+ async function canLeaveApiFieldsStep(payload, args, groups) {
785
+ if (payload.step?.id !== 'fields') {
786
+ return true;
787
+ }
788
+ if (countFieldGroupItems(groups) === 0) {
789
+ return true;
790
+ }
791
+ const context = (payload.context ?? {});
792
+ const selected = collectSelectedFieldGroupPaths(context.source?.api?.fieldGroups, groups);
793
+ if (selected.length > 0) {
794
+ return true;
795
+ }
796
+ await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.fieldsRequired);
797
+ return false;
798
+ }
799
+ function createApiWizardContext(api, fieldGroups = {}) {
800
+ const base = createInitialContext('api');
801
+ return {
802
+ ...base,
803
+ source: {
804
+ ...base.source,
805
+ api: {
806
+ request: api?.request ?? createEmptyRestApiOutput(),
807
+ connectionId: api?.connectionId,
808
+ lastTest: api?.lastTest
809
+ ? { ...api.lastTest, status: String(api.lastTest.status) }
810
+ : undefined,
811
+ fields: api?.fields ?? [],
812
+ fieldGroups: Object.keys(fieldGroups).length ? fieldGroups : (api?.fieldGroups ?? {}),
813
+ },
814
+ },
815
+ };
816
+ }
817
+ function isSameApiRequest(left, right) {
818
+ return JSON.stringify(left.source?.api?.request) === JSON.stringify(right.source?.api?.request);
819
+ }
820
+ async function showApiTestFailure(args, lastTest) {
821
+ if (Number(lastTest.status) === 0) {
822
+ await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.apiTestNetwork);
823
+ return;
824
+ }
825
+ await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.apiTestFailed, { status: String(lastTest.status) });
826
+ }
827
+ async function runRestApiRequest(value) {
828
+ const request = normalizeRestApiOutput(value);
829
+ const url = buildApiRequestUrl(request);
830
+ if (!url) {
831
+ return { ok: false, status: 0, body: null };
832
+ }
833
+ const headers = {};
834
+ if (request.headers['ContentType'].trim()) {
835
+ headers['Content-Type'] = request.headers['ContentType'].trim();
836
+ }
837
+ if (request.headers['Authorization'].trim()) {
838
+ headers['Authorization'] = request.headers['Authorization'].trim();
839
+ }
840
+ for (const [key, headerValue] of Object.entries(request.headers)) {
841
+ if (key === 'Authorization' || key === 'ContentType' || key === 'tempId') {
842
+ continue;
843
+ }
844
+ if (typeof headerValue === 'string' && headerValue.trim()) {
845
+ headers[key] = headerValue.trim();
846
+ }
847
+ }
848
+ try {
849
+ const response = await fetch(url, {
850
+ method: request.method,
851
+ headers,
852
+ body: request.method === 'GET' || !request.body.trim() ? undefined : request.body,
853
+ credentials: 'omit',
854
+ signal: AbortSignal.timeout(15000),
855
+ });
856
+ const text = (await response.text()).trim();
857
+ let body = text || null;
858
+ if (text) {
859
+ try {
860
+ body = JSON.parse(text);
861
+ }
862
+ catch {
863
+ body = text;
864
+ }
865
+ }
866
+ return {
867
+ ok: response.status >= 200 && response.status < 300,
868
+ status: response.status,
869
+ body,
870
+ };
871
+ }
872
+ catch {
873
+ return { ok: false, status: 0, body: null };
874
+ }
875
+ }
876
+ function buildApiRequestUrl(request) {
877
+ const base = request.url.trim();
878
+ if (!base) {
879
+ return '';
880
+ }
881
+ const params = new URLSearchParams();
882
+ for (const query of request.queries ?? []) {
883
+ for (const [key, value] of Object.entries(query)) {
884
+ if (key === 'tempId' || !String(key).trim() || value == null || !String(value).trim()) {
885
+ continue;
886
+ }
887
+ params.append(key, String(value).trim());
888
+ }
889
+ }
890
+ const search = params.toString();
891
+ if (!search) {
892
+ return base;
893
+ }
894
+ return base.includes('?') ? `${base}&${search}` : `${base}?${search}`;
895
+ }
896
+ function inferJsonFieldGroups(sample) {
897
+ const root = firstPlainObject(sample);
898
+ if (!root) {
899
+ return [];
900
+ }
901
+ const envelope = splitJsonEnvelope(root);
902
+ if (!envelope) {
903
+ return jsonGroupsFromObject(root, '', 0, true);
904
+ }
905
+ const groups = jsonGroupsFromObject(envelope.record, envelope.path, 0, true);
906
+ if (envelope.meta.length > 0) {
907
+ groups.push({
908
+ id: 'response',
909
+ kind: 'lookup',
910
+ title: CREATE_USER_QUERY_I18N.fields.response,
911
+ fallback: 'response',
912
+ items: envelope.meta,
913
+ });
914
+ }
915
+ return groups;
916
+ }
917
+ function splitJsonEnvelope(root) {
918
+ const payloadKeys = ['data', 'result', 'results', 'items', 'records', 'value', 'payload', 'content'];
919
+ const flagKeys = new Set(['ok', 'success', 'status', 'kind', 'error', 'code', 'message']);
920
+ const metaObjectKeys = new Set(['info', 'meta', 'pagination', 'support', 'links', 'page']);
921
+ const keys = Object.keys(root);
922
+ const namedPayload = payloadKeys.find((key) => root[key] != null);
923
+ const hasFlag = keys.some((key) => flagKeys.has(key) && isJsonScalar(root[key]));
924
+ let payloadKey = namedPayload;
925
+ if (payloadKey) {
926
+ const rest = keys.filter((key) => key !== payloadKey);
927
+ const restAreMeta = rest.every((key) => isJsonScalar(root[key]) || flagKeys.has(key) || metaObjectKeys.has(key));
928
+ if (!hasFlag && !restAreMeta) {
929
+ payloadKey = undefined;
930
+ }
931
+ }
932
+ else {
933
+ const arrayKeys = keys.filter((key) => {
934
+ const value = root[key];
935
+ return Array.isArray(value) && value.some((item) => isPlainObject(item));
936
+ });
937
+ const otherKeys = keys.filter((key) => !arrayKeys.includes(key));
938
+ if (arrayKeys.length === 1 &&
939
+ otherKeys.length > 0 &&
940
+ otherKeys.every((key) => isJsonScalar(root[key]))) {
941
+ payloadKey = arrayKeys[0];
942
+ }
943
+ }
944
+ if (!payloadKey) {
945
+ return null;
946
+ }
947
+ const payload = root[payloadKey];
948
+ const meta = [];
949
+ for (const [key, value] of Object.entries(root)) {
950
+ if (key === payloadKey || !isJsonScalar(value)) {
951
+ continue;
952
+ }
953
+ meta.push({ path: key, fallback: key });
954
+ }
955
+ if (Array.isArray(payload)) {
956
+ const record = firstPlainObject(payload);
957
+ if (!record) {
958
+ return null;
959
+ }
960
+ return { record, path: payloadKey, meta };
961
+ }
962
+ if (isPlainObject(payload)) {
963
+ const items = payload['items'];
964
+ if (Array.isArray(items)) {
965
+ const record = firstPlainObject(items);
966
+ if (record) {
967
+ for (const [key, value] of Object.entries(payload)) {
968
+ if (key === 'items' || !isJsonScalar(value)) {
969
+ continue;
970
+ }
971
+ meta.push({ path: `${payloadKey}.${key}`, fallback: key });
972
+ }
973
+ return { record, path: `${payloadKey}.items`, meta };
974
+ }
975
+ }
976
+ return { record: payload, path: payloadKey, meta };
977
+ }
978
+ meta.push({ path: payloadKey, fallback: payloadKey });
979
+ return { record: { value: payload }, path: payloadKey, meta };
980
+ }
981
+ function jsonGroupsFromObject(value, prefix, depth, asHost = false) {
982
+ const scalars = [];
983
+ const nested = [];
984
+ for (const [key, raw] of Object.entries(value)) {
985
+ const path = prefix ? `${prefix}.${key}` : key;
986
+ const unwrapped = Array.isArray(raw) ? firstPlainObject(raw) : raw;
987
+ if (isPlainObject(unwrapped) && depth < 2) {
988
+ nested.push({ key, path, value: unwrapped });
989
+ continue;
990
+ }
991
+ scalars.push({ path, fallback: key });
992
+ }
993
+ const groups = [];
994
+ if (scalars.length > 0) {
995
+ const leaf = prefix.includes('.') ? (prefix.split('.').pop() ?? prefix) : prefix;
996
+ const isHost = (asHost && depth === 0) || !prefix;
997
+ groups.push({
998
+ id: isHost ? 'host' : `rel_${prefix.replace(/[^a-zA-Z0-9]+/g, '_')}_fields`,
999
+ kind: isHost ? 'host' : 'relation',
1000
+ title: leaf,
1001
+ fallback: leaf || 'fields',
1002
+ items: scalars,
1003
+ });
1004
+ }
1005
+ if (depth >= 2) {
1006
+ return groups;
1007
+ }
1008
+ for (const item of nested) {
1009
+ const children = jsonGroupsFromObject(item.value, item.path, depth + 1, false);
1010
+ groups.push({
1011
+ id: `rel_${item.path.replace(/[^a-zA-Z0-9]+/g, '_')}`,
1012
+ kind: 'relation',
1013
+ title: item.key,
1014
+ fallback: item.key,
1015
+ items: [],
1016
+ children: children.length > 0 ? children : undefined,
1017
+ });
1018
+ }
1019
+ return groups;
1020
+ }
1021
+ function defaultHostFieldGroups(groups) {
1022
+ const selected = {};
1023
+ const walk = (group) => {
1024
+ selected[group.id] = group.kind === 'host' ? group.items.map((item) => item.path) : [];
1025
+ for (const child of group.children ?? []) {
1026
+ walk(child);
1027
+ }
1028
+ };
1029
+ for (const group of groups) {
1030
+ walk(group);
1031
+ }
1032
+ return selected;
1033
+ }
1034
+ function firstPlainObject(value) {
1035
+ if (Array.isArray(value)) {
1036
+ for (const item of value) {
1037
+ const found = firstPlainObject(item);
1038
+ if (found) {
1039
+ return found;
1040
+ }
1041
+ }
1042
+ return null;
1043
+ }
1044
+ return isPlainObject(value) ? value : null;
1045
+ }
1046
+ function isPlainObject(value) {
1047
+ return value != null && typeof value === 'object' && !Array.isArray(value);
1048
+ }
1049
+ function isJsonScalar(value) {
1050
+ if (value == null) {
1051
+ return true;
1052
+ }
1053
+ const type = typeof value;
1054
+ return type === 'string' || type === 'number' || type === 'boolean';
1055
+ }
1056
+ //#endregion
1057
+ //#region ----- Advanced placeholder -----
1058
+ async function openAdvancedPlaceholder(args) {
1059
+ await args.layoutBuilder
1060
+ .create()
1061
+ .dialog((dialog) => {
1062
+ dialog
1063
+ .setTitle(CREATE_USER_QUERY_I18N.titles.advanced)
1064
+ .setMessage(CREATE_USER_QUERY_I18N.placeholders.advanced)
1065
+ .setSize('md')
1066
+ .setCloseButton(true)
1067
+ .setActions((actions) => {
1068
+ actions.cancel('@general:actions.close.title');
1069
+ })
1070
+ .onAction(async () => null);
1071
+ })
1072
+ .show();
1073
+ return null;
1074
+ }
1075
+ //#endregion
1076
+ //#region ----- Simple source wizard -----
1077
+ function configureSimpleSourceWizard(dialog, args) {
1078
+ dialog
1079
+ .setTitle(dialogTitleKey(args.sourceKind))
1080
+ .setSize(args.sourceKind === 'api' ? 'xl' : 'lg')
1081
+ .setCloseButton(true)
1082
+ .setContext(createInitialContext(args.sourceKind))
1083
+ .content((flex) => {
1084
+ flex.setDirection('column').setGap('16px').stepWizard((wizard) => {
1085
+ wizard
1086
+ .name(CREATE_USER_QUERY_WIZARD_NAME)
1087
+ .setLook('circular-icon')
1088
+ .setLinear(true)
1089
+ .setDirection('horizontal')
1090
+ .setShowActions(false)
1091
+ .setGuards({
1092
+ next: (payload) => canLeaveSourceStep(payload, args),
1093
+ })
1094
+ .step('source', CREATE_USER_QUERY_I18N.steps.source, (step) => {
1095
+ step.setIcon(sourceStepIcon(args.sourceKind)).content((layout) => {
1096
+ fillSourceStep(layout, args);
1097
+ });
1098
+ })
1099
+ .step('contract', CREATE_USER_QUERY_I18N.steps.contract, (step) => {
1100
+ step.setIcon('fa-light fa-file-contract').content((layout) => {
1101
+ fillContractPlaceholderStep(layout);
1102
+ });
1103
+ });
1104
+ });
1105
+ })
1106
+ .setActions((actions) => {
1107
+ actions
1108
+ .cancel('@general:actions.cancel.title')
1109
+ .custom({
1110
+ title: '@general:actions.previous.title',
1111
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.previous`,
1112
+ icon: 'fa-regular fa-arrow-left',
1113
+ color: 'primary',
1114
+ position: 'prefix',
1115
+ disabled: '{{api.getStatus().isFirst}}',
1116
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
1117
+ })
1118
+ .custom({
1119
+ title: '@general:actions.next.title',
1120
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.next`,
1121
+ icon: 'fa-regular fa-arrow-right',
1122
+ color: 'primary',
1123
+ hidden: '{{api.getStatus().isLast}}',
1124
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
1125
+ })
1126
+ .custom({
1127
+ title: '@general:actions.submit.title',
1128
+ command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.submit`,
1129
+ icon: 'fa-regular fa-check',
1130
+ color: 'success',
1131
+ hidden: '{{!api.getStatus().isLast}}',
1132
+ predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
1133
+ });
1134
+ })
1135
+ .onAction(async (dialogRef) => {
1136
+ if (dialogRef.action() === 'cancel') {
1137
+ return null;
1138
+ }
1139
+ return dialogRef.context() ?? null;
1140
+ });
1141
+ }
1142
+ //#endregion
1143
+ //#region ----- Step bodies -----
1144
+ function fillSourceStep(layout, args) {
1145
+ switch (args.sourceKind) {
1146
+ case 'static':
1147
+ fillStaticSourceStep(layout);
1148
+ return;
1149
+ case 'api':
1150
+ fillApiSourceStep(layout);
1151
+ return;
1152
+ default:
1153
+ return;
1154
+ }
1155
+ }
1156
+ function fillEntityPickStep(layout, readonly = false) {
1157
+ layout.fieldset((fieldset) => {
1158
+ fieldset.setTitle(CREATE_USER_QUERY_I18N.steps.entity).setIcon('fa-light fa-database').setCols(1);
1159
+ if (!readonly) {
1160
+ fieldset.setDescription(CREATE_USER_QUERY_I18N.placeholders.fieldsNext);
1161
+ }
1162
+ fieldset.formField(CREATE_USER_QUERY_I18N.fields.entity, (field) => {
1163
+ field.path('source.entity.entityName').lookupBox({
1164
+ entity: CREATE_USER_QUERY_ENTITY_CATALOG,
1165
+ valueField: 'id',
1166
+ textField: 'id',
1167
+ columns: ['name', 'module_id'],
1168
+ look: 'select',
1169
+ searchable: true,
1170
+ allowClear: !readonly,
1171
+ validations: readonly ? undefined : [{ rule: 'required' }],
1172
+ });
1173
+ if (readonly) {
1174
+ field.readonly(true);
1175
+ }
1176
+ });
1177
+ });
1178
+ }
1179
+ function fillEntityFieldsStep(layout, entityName, groups) {
1180
+ layout.flex((flex) => {
1181
+ flex.setDirection('column').setGap('16px').setAlignItems('stretch');
1182
+ flex.fieldset((fieldset) => {
1183
+ fieldset
1184
+ .setTitle(entityName)
1185
+ .setDescription(CREATE_USER_QUERY_I18N.placeholders.selectedEntityHint)
1186
+ .setIcon('fa-light fa-database')
1187
+ .setLook('group');
1188
+ });
1189
+ for (const group of groups) {
1190
+ renderFieldGroup(flex, group);
1191
+ }
1192
+ });
1193
+ }
1194
+ function renderFieldGroup(layout, group, fieldGroupsPath = 'source.entity.fieldGroups') {
1195
+ if (group.kind === 'relation' && (group.children?.length ?? 0) > 0) {
1196
+ layout.panel((panel) => {
1197
+ panel.setCaption(group.title).setIcon('fa-light fa-link').setLook('outline');
1198
+ if (group.items.length) {
1199
+ panel.fieldset((fieldset) => {
1200
+ appendFieldGroupList(fieldset, group, CREATE_USER_QUERY_I18N.fields.hostFields, fieldGroupsPath);
1201
+ });
1202
+ }
1203
+ for (const child of group.children ?? []) {
1204
+ renderFieldGroupOnPanel(panel, child, fieldGroupsPath);
1205
+ }
1206
+ });
1207
+ return;
1208
+ }
1209
+ layout.fieldset((fieldset) => {
1210
+ appendFieldGroupList(fieldset, group, group.title, fieldGroupsPath);
1211
+ });
1212
+ }
1213
+ function renderFieldGroupOnPanel(panel, group, fieldGroupsPath = 'source.entity.fieldGroups') {
1214
+ if ((group.children?.length ?? 0) > 0) {
1215
+ panel.panel((inner) => {
1216
+ inner.setCaption(group.title).setIcon('fa-light fa-link').setLook('outline');
1217
+ if (group.items.length) {
1218
+ inner.fieldset((fieldset) => {
1219
+ appendFieldGroupList(fieldset, group, CREATE_USER_QUERY_I18N.fields.hostFields, fieldGroupsPath);
1220
+ });
1221
+ }
1222
+ for (const child of group.children ?? []) {
1223
+ renderFieldGroupOnPanel(inner, child, fieldGroupsPath);
1224
+ }
1225
+ });
1226
+ return;
1227
+ }
1228
+ panel.fieldset((fieldset) => {
1229
+ appendFieldGroupList(fieldset, group, group.title, fieldGroupsPath);
1230
+ });
1231
+ }
1232
+ function appendFieldGroupList(fieldset, group, title, fieldGroupsPath = 'source.entity.fieldGroups') {
1233
+ fieldset.setTitle(title).setIcon(fieldGroupIcon(group.kind)).setCols(1);
1234
+ if (group.kind === 'host') {
1235
+ fieldset.setDescription(fieldGroupsPath.startsWith('source.api') ? CREATE_USER_QUERY_I18N.placeholders.apiFields : CREATE_USER_QUERY_I18N.placeholders.entityFields);
1236
+ }
1237
+ if (!group.items.length) {
1238
+ return;
1239
+ }
1240
+ fieldset.formField(' ', (field) => {
1241
+ field.path(`${fieldGroupsPath}.${group.id}`).selectionList({
1242
+ dataSource: group.items,
1243
+ valueField: 'path',
1244
+ textField: 'text',
1245
+ multiple: true,
1246
+ searchable: group.items.length > 8,
1247
+ direction: { id: 'vertical' },
1248
+ });
1249
+ });
1250
+ }
1251
+ function fieldGroupIcon(kind) {
1252
+ if (kind === 'relation') {
1253
+ return 'fa-light fa-link';
1254
+ }
1255
+ if (kind === 'lookup') {
1256
+ return 'fa-light fa-diagram-nested';
1257
+ }
1258
+ return 'fa-light fa-list';
1259
+ }
1260
+ function fillHintStep(layout, titleKey, descriptionKey) {
1261
+ layout.fieldset((fieldset) => {
1262
+ fieldset.setTitle(titleKey).setDescription(descriptionKey).setIcon('fa-light fa-diagram-project').setLook('group');
1263
+ });
1264
+ }
1265
+ function fillStaticSourceStep(layout) {
1266
+ layout.fieldset((fieldset) => {
1267
+ fieldset
1268
+ .setTitle(CREATE_USER_QUERY_I18N.steps.source)
1269
+ .setDescription(CREATE_USER_QUERY_I18N.placeholders.itemsHint)
1270
+ .setIcon('fa-light fa-list')
1271
+ .setCols(1)
1272
+ .formField(' ', (field) => {
1273
+ field
1274
+ .path('source.static.items')
1275
+ .setShowLabel(false)
1276
+ .customWidget(AXPWidgetsList.Layouts.RepeaterLayout, {
1277
+ hasControls: true,
1278
+ allowReorder: true,
1279
+ children: staticItemRepeaterChildren(),
1280
+ })
1281
+ .layout(1);
1282
+ });
1283
+ });
1284
+ }
1285
+ function staticItemRepeaterChildren() {
1286
+ return [
1287
+ {
1288
+ type: AXPWidgetsList.Layouts.FlexLayout,
1289
+ mode: 'edit',
1290
+ options: {
1291
+ flexDirection: 'row',
1292
+ gap: '12px',
1293
+ alignItems: 'stretch',
1294
+ },
1295
+ children: [
1296
+ staticItemRepeaterField(CREATE_USER_QUERY_I18N.fields.value, 'value'),
1297
+ staticItemRepeaterField(CREATE_USER_QUERY_I18N.fields.title, 'title'),
1298
+ ],
1299
+ },
1300
+ ];
1301
+ }
1302
+ function staticItemRepeaterField(label, path) {
1303
+ return {
1304
+ type: AXPWidgetsList.Layouts.FlexItemLayout,
1305
+ options: { grow: 1 },
1306
+ children: [
1307
+ {
1308
+ type: AXPWidgetsList.Layouts.FormField,
1309
+ options: { label, showLabel: true },
1310
+ children: [
1311
+ {
1312
+ type: AXPWidgetsList.Editors.TextBox,
1313
+ path,
1314
+ name: path,
1315
+ mode: 'edit',
1316
+ },
1317
+ ],
1318
+ },
1319
+ ],
1320
+ };
1321
+ }
1322
+ function fillApiSourceStep(layout) {
1323
+ layout.fieldset((fieldset) => {
1324
+ fieldset
1325
+ .setTitle(CREATE_USER_QUERY_I18N.steps.source)
1326
+ .setDescription(CREATE_USER_QUERY_I18N.placeholders.apiFieldsNext)
1327
+ .setIcon('fa-light fa-cloud-arrow-up')
1328
+ .setCols(1)
1329
+ .formField(CREATE_USER_QUERY_I18N.fields.connection, (field) => {
1330
+ field
1331
+ .path('source.api.connectionId')
1332
+ .lookupBox({
1333
+ entity: 'IntegrationManagement.IntegrationConnection',
1334
+ searchable: true,
1335
+ allowClear: true,
1336
+ })
1337
+ .layout(1);
1338
+ })
1339
+ .formField(CREATE_USER_QUERY_I18N.fields.request, (field) => {
1340
+ field.path('source.api.request').customWidget(REST_API_GENERATOR_WIDGET_NAME, {}).layout(1);
1341
+ });
1342
+ });
1343
+ }
1344
+ function fillContractPlaceholderStep(layout) {
1345
+ layout.fieldset((fieldset) => {
1346
+ fieldset
1347
+ .setTitle(CREATE_USER_QUERY_I18N.steps.contract)
1348
+ .setDescription(CREATE_USER_QUERY_I18N.placeholders.contract)
1349
+ .setIcon('fa-light fa-file-contract')
1350
+ .setLook('group');
1351
+ });
1352
+ }
1353
+ //#endregion
1354
+ //#region ----- Guards & helpers -----
1355
+ async function canLeaveSourceStep(payload, args) {
1356
+ if (payload.step?.id !== 'source') {
1357
+ return true;
1358
+ }
1359
+ const context = (payload.context ?? {});
1360
+ const errorKey = sourceStepErrorKey(args.sourceKind, context);
1361
+ if (!errorKey) {
1362
+ return true;
1363
+ }
1364
+ await showWizardWarning(args, errorKey);
1365
+ return false;
1366
+ }
1367
+ async function canLeaveEntityFieldsStep(payload, args, groups) {
1368
+ if (payload.step?.id !== 'fields') {
1369
+ return true;
1370
+ }
1371
+ if (countFieldGroupItems(groups) === 0) {
1372
+ return true;
1373
+ }
1374
+ const context = (payload.context ?? {});
1375
+ const selected = collectSelectedFieldGroupPaths(context.source?.entity?.fieldGroups, groups).filter((path) => path !== 'id');
1376
+ if (selected.length > 0) {
1377
+ return true;
1378
+ }
1379
+ await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.fieldsRequired);
1380
+ return false;
1381
+ }
1382
+ function sourceStepErrorKey(sourceKind, context) {
1383
+ if (sourceKind === 'static') {
1384
+ const hasValue = (context.source?.static?.items ?? []).some((row) => String(row?.value ?? '').trim());
1385
+ return hasValue ? null : CREATE_USER_QUERY_I18N.errors.staticRequired;
1386
+ }
1387
+ if (sourceKind === 'api') {
1388
+ const url = context.source?.api?.request?.url?.trim() ?? '';
1389
+ return url ? null : CREATE_USER_QUERY_I18N.errors.apiUrlRequired;
1390
+ }
1391
+ return null;
1392
+ }
1393
+ function createInitialContext(sourceKind, entityName = '', fields = [], fieldGroups = {}) {
1394
+ const items = [{ value: '', title: '' }];
1395
+ return {
1396
+ sourceKind,
1397
+ source: {
1398
+ kind: sourceKind,
1399
+ entity: { entityName, fields, fieldGroups },
1400
+ static: { items },
1401
+ api: { request: createEmptyRestApiOutput() },
1402
+ },
1403
+ };
1404
+ }
1405
+ function countFieldGroupItems(groups) {
1406
+ let count = 0;
1407
+ const walk = (group) => {
1408
+ count += group.items.length;
1409
+ for (const child of group.children ?? []) {
1410
+ walk(child);
1411
+ }
1412
+ };
1413
+ for (const group of groups) {
1414
+ walk(group);
1415
+ }
1416
+ return count;
1417
+ }
1418
+ async function toFieldGroupViews(groups, translationService) {
1419
+ const hostTitle = resolveLabel(translationService, CREATE_USER_QUERY_I18N.fields.hostFields, 'Fields');
1420
+ const mapGroup = async (group) => {
1421
+ const resolvedName = resolveLabel(translationService, group.title, group.fallback);
1422
+ let title = resolvedName;
1423
+ if (group.kind === 'host') {
1424
+ title = hostTitle;
1425
+ }
1426
+ else if (group.kind === 'relation') {
1427
+ title = await formatRelatedGroupTitle(translationService, resolvedName);
1428
+ }
1429
+ const children = group.children?.length
1430
+ ? await Promise.all(group.children.map((child) => mapGroup(child)))
1431
+ : undefined;
1432
+ return {
1433
+ id: group.id,
1434
+ kind: group.kind,
1435
+ title,
1436
+ items: group.items.map((item) => ({
1437
+ path: item.path,
1438
+ text: resolveLabel(translationService, item.title, item.fallback),
1439
+ })),
1440
+ children,
1441
+ };
1442
+ };
1443
+ return Promise.all(groups.map((group) => mapGroup(group)));
1444
+ }
1445
+ async function formatRelatedGroupTitle(translationService, name) {
1446
+ const translated = await translationService.translateAsync(CREATE_USER_QUERY_I18N.placeholders.relatedTo, { params: { name } });
1447
+ return typeof translated === 'string' && translated.trim() ? translated.trim() : name;
1448
+ }
1449
+ function resolveLabel(translationService, value, fallback) {
1450
+ if (value == null || value === '') {
1451
+ return fallback;
1452
+ }
1453
+ const resolved = translationService.resolve(value);
1454
+ const text = typeof resolved === 'string' ? resolved.trim() : '';
1455
+ return text || fallback;
1456
+ }
1457
+ async function showWizardWarning(args, errorKey, params, color = 'warning') {
1458
+ const message = await args.translationService.translateAsync(errorKey, params ? { params } : undefined);
1459
+ args.toastService.show({
1460
+ color,
1461
+ content: message,
1462
+ closeButton: true,
1463
+ timeOut: 4000,
1464
+ timeOutProgress: true,
1465
+ });
1466
+ }
1467
+ function dialogTitleKey(sourceKind) {
1468
+ if (sourceKind === 'static') {
1469
+ return CREATE_USER_QUERY_I18N.titles.static;
1470
+ }
1471
+ if (sourceKind === 'api') {
1472
+ return CREATE_USER_QUERY_I18N.titles.api;
1473
+ }
1474
+ return CREATE_USER_QUERY_I18N.titles.entity;
1475
+ }
1476
+ function sourceStepIcon(sourceKind) {
1477
+ if (sourceKind === 'static') {
1478
+ return 'fa-light fa-list';
1479
+ }
1480
+ if (sourceKind === 'api') {
1481
+ return 'fa-light fa-cloud-arrow-up';
1482
+ }
1483
+ return 'fa-light fa-database';
1484
+ }
1485
+ //#endregion
1486
+
1487
+ //#region ----- Imports -----
1488
+ //#endregion
1489
+ //#region ----- Command -----
1490
+ /**
1491
+ * Opens the user-query create wizard. Step 1 configures the source; nothing is persisted.
1492
+ */
1493
+ class AXMCreateUserQueryCommand {
1494
+ constructor() {
1495
+ //#region ----- Services & Dependencies -----
1496
+ this.layoutBuilder = inject(AXPLayoutBuilderService);
1497
+ this.entityResolver = inject(AXPEntityResolver);
1498
+ this.toastService = inject(AXToastService);
1499
+ this.translationService = inject(AXTranslationService);
1500
+ }
1501
+ //#endregion
1502
+ //#region ----- Execution -----
1503
+ async execute(input = {}) {
1504
+ const sourceKind = resolveSourceKind(input);
1505
+ await openCreateUserQueryWizard({
1506
+ layoutBuilder: this.layoutBuilder,
1507
+ toastService: this.toastService,
1508
+ translationService: this.translationService,
1509
+ entityResolver: this.entityResolver,
1510
+ sourceKind,
1511
+ });
1512
+ return {
1513
+ success: true,
1514
+ kind: 'object',
1515
+ data: { sourceKind },
1516
+ metadata: { refresh: false },
1517
+ };
1518
+ }
1519
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateUserQueryCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1520
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateUserQueryCommand, providedIn: 'root' }); }
1521
+ }
1522
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateUserQueryCommand, decorators: [{
1523
+ type: Injectable,
1524
+ args: [{ providedIn: 'root' }]
1525
+ }] });
1526
+ //#endregion
1527
+ //#region ----- Helpers -----
1528
+ function resolveSourceKind(input) {
1529
+ const resolved = resolveEntityActionCommandInput(input);
1530
+ const data = entityActionDataRecord(resolved.data) ?? {};
1531
+ const options = (resolved.options ?? {});
1532
+ const process = (options['process'] ?? {});
1533
+ const processData = (process['data'] ?? {});
1534
+ const raw = data['sourceKind'] ?? processData['sourceKind'];
1535
+ if (raw === 'static' || raw === 'api' || raw === 'advanced' || raw === 'entity') {
1536
+ return raw;
1537
+ }
1538
+ return 'entity';
1539
+ }
1540
+
1541
+ export { AXMCreateUserQueryCommand };
1542
+ //# sourceMappingURL=acorex-platform-module-platform-management-client-create-user-query.command-2Qu7aoCp.mjs.map