@praxisui/crud 3.0.0-beta.8 → 4.0.0-beta.0
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.
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, InjectionToken, inject, EventEmitter, DestroyRef, ViewChild, Output, Input, Component, Inject, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
2
|
+
import { Injectable, InjectionToken, inject, EventEmitter, DestroyRef, ChangeDetectorRef, ViewChild, Output, Input, Component, Inject, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
3
4
|
import { Router, ActivatedRoute, RouterLink } from '@angular/router';
|
|
4
|
-
import {
|
|
5
|
+
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
5
6
|
import { firstValueFrom } from 'rxjs';
|
|
7
|
+
import * as i2 from '@praxisui/core';
|
|
8
|
+
import { ASYNC_CONFIG_STORAGE, GlobalConfigService, fillUndefined, createDefaultTableConfig, PraxisI18nService, ResourceDiscoveryService, ResourceActionOpenAdapterService, ResourceSurfaceOpenAdapterService, GLOBAL_SURFACE_SERVICE, ComponentKeyService, translateUnavailableWorkflowMessage, EmptyStateCardComponent, providePraxisI18nConfig, RESOURCE_DISCOVERY_I18N_CONFIG, PraxisIconDirective, GenericCrudService, ComponentMetadataRegistry } from '@praxisui/core';
|
|
9
|
+
import { PraxisTable } from '@praxisui/table';
|
|
6
10
|
import * as i1 from '@angular/material/dialog';
|
|
7
11
|
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
|
8
12
|
export { MAT_DIALOG_DATA as DIALOG_DATA } from '@angular/material/dialog';
|
|
9
|
-
import * as i2 from '@praxisui/core';
|
|
10
|
-
import { ASYNC_CONFIG_STORAGE, GlobalConfigService, fillUndefined, createDefaultTableConfig, ComponentKeyService, EmptyStateCardComponent, PraxisIconDirective, GenericCrudService, ComponentMetadataRegistry } from '@praxisui/core';
|
|
11
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
12
|
-
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
13
13
|
import * as i1$1 from '@angular/common';
|
|
14
14
|
import { CommonModule } from '@angular/common';
|
|
15
15
|
import * as i4 from '@angular/material/button';
|
|
@@ -290,61 +290,125 @@ function assertCrudMetadata(meta, options = {}) {
|
|
|
290
290
|
});
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
const PRAXIS_CRUD_RUNTIME_I18N_NAMESPACE = 'praxisCrudRuntime';
|
|
294
|
+
const PRAXIS_CRUD_RUNTIME_I18N_CONFIG = {
|
|
295
|
+
namespaces: {
|
|
296
|
+
[PRAXIS_CRUD_RUNTIME_I18N_NAMESPACE]: {
|
|
297
|
+
'pt-BR': {
|
|
298
|
+
'crud.emptyState.title': 'Conecte o CRUD a um recurso',
|
|
299
|
+
'crud.emptyState.description': 'Informe os metadados (resourcePath / schema) ou forneça metadata.data para habilitar a tabela e as ações.',
|
|
300
|
+
'crud.emptyState.primaryAction': 'Configurar metadados',
|
|
301
|
+
'crud.preferences.resetSuccess': 'Overrides de CRUD redefinidos',
|
|
302
|
+
'crud.actions.create': 'Adicionar',
|
|
303
|
+
'crud.actions.view': 'Ver',
|
|
304
|
+
'crud.actions.edit': 'Editar',
|
|
305
|
+
'crud.actions.delete': 'Excluir',
|
|
306
|
+
},
|
|
307
|
+
'en-US': {
|
|
308
|
+
'crud.emptyState.title': 'Connect CRUD to a resource',
|
|
309
|
+
'crud.emptyState.description': 'Provide metadata (resourcePath / schema) or metadata.data to enable the table and actions.',
|
|
310
|
+
'crud.emptyState.primaryAction': 'Configure metadata',
|
|
311
|
+
'crud.preferences.resetSuccess': 'CRUD overrides reset',
|
|
312
|
+
'crud.actions.create': 'Add',
|
|
313
|
+
'crud.actions.view': 'View',
|
|
314
|
+
'crud.actions.edit': 'Edit',
|
|
315
|
+
'crud.actions.delete': 'Delete',
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
function getCrudRuntimeDictionary(locale) {
|
|
321
|
+
const dictionaries = PRAXIS_CRUD_RUNTIME_I18N_CONFIG.namespaces?.[PRAXIS_CRUD_RUNTIME_I18N_NAMESPACE];
|
|
322
|
+
if (!dictionaries) {
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
const normalized = String(locale || '').trim();
|
|
326
|
+
const candidates = [
|
|
327
|
+
normalized,
|
|
328
|
+
normalized.toLowerCase(),
|
|
329
|
+
normalized.includes('-') ? normalized.split('-')[0] : '',
|
|
330
|
+
normalized.includes('_') ? normalized.split('_')[0] : '',
|
|
331
|
+
'pt-BR',
|
|
332
|
+
'pt-br',
|
|
333
|
+
'en-US',
|
|
334
|
+
'en-us',
|
|
335
|
+
].filter(Boolean);
|
|
336
|
+
for (const candidate of candidates) {
|
|
337
|
+
const dictionary = dictionaries[candidate];
|
|
338
|
+
if (dictionary && typeof dictionary === 'object') {
|
|
339
|
+
return dictionary;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return undefined;
|
|
343
|
+
}
|
|
344
|
+
function resolveCrudRuntimeFallback(key, locale) {
|
|
345
|
+
return getCrudRuntimeDictionary(locale || 'pt-BR')?.[key];
|
|
346
|
+
}
|
|
347
|
+
function translateCrudRuntimeText(i18n, key, fallback, params, locale) {
|
|
348
|
+
const runtimeFallback = resolveCrudRuntimeFallback(key, locale)
|
|
349
|
+
|| resolveCrudRuntimeFallback(key, locale || i18n.getLocale?.() || i18n.getFallbackLocale?.() || 'pt-BR')
|
|
350
|
+
|| fallback
|
|
351
|
+
|| key;
|
|
352
|
+
return i18n.t(key, params, runtimeFallback, PRAXIS_CRUD_RUNTIME_I18N_NAMESPACE);
|
|
353
|
+
}
|
|
354
|
+
|
|
293
355
|
class PraxisCrudComponent {
|
|
294
|
-
/** JSON inline ou chave/URL resolvida pelo MetadataResolver */
|
|
295
356
|
metadata;
|
|
296
|
-
/** Identificador obrigatório do CRUD (base para tabela/formulário) */
|
|
297
357
|
crudId;
|
|
298
|
-
/** Identificador opcional para instâncias múltiplas */
|
|
299
358
|
componentInstanceId;
|
|
300
359
|
context;
|
|
301
|
-
/** Encaminha o modo de edição de layout para a tabela interna */
|
|
302
360
|
enableCustomization = false;
|
|
303
|
-
/** CTA: usado pelo Builder para abrir configuração de metadados quando vazio */
|
|
304
361
|
configureRequested = new EventEmitter();
|
|
305
362
|
afterOpen = new EventEmitter();
|
|
306
363
|
afterClose = new EventEmitter();
|
|
307
364
|
afterSave = new EventEmitter();
|
|
308
365
|
afterDelete = new EventEmitter();
|
|
309
366
|
error = new EventEmitter();
|
|
310
|
-
/**
|
|
311
|
-
* Emits the live PraxisTable configuration snapshot after runtime hydration or
|
|
312
|
-
* metadata refreshes. This reflects the effective table state in memory,
|
|
313
|
-
* deduplicated by structural JSON equality, and is intended for external hosts
|
|
314
|
-
* that need to mirror the table editor/runtime state.
|
|
315
|
-
*/
|
|
316
367
|
tableRuntimeConfigChange = new EventEmitter();
|
|
317
368
|
resolvedMetadata;
|
|
318
|
-
/** Configuração efetiva da tabela com melhorias automáticas (ex.: botão Adicionar). */
|
|
319
369
|
effectiveTableConfig;
|
|
320
|
-
/** Config passado ao PraxisTable — sempre definido (fallback seguro). */
|
|
321
370
|
tableConfigForBinding = createDefaultTableConfig();
|
|
371
|
+
tableQueryContext = null;
|
|
372
|
+
tableFilterCriteria = {};
|
|
373
|
+
tableCrudContext;
|
|
322
374
|
launcher = inject(CrudLauncherService);
|
|
323
375
|
destroyRef = inject(DestroyRef);
|
|
376
|
+
cdr = inject(ChangeDetectorRef);
|
|
324
377
|
table;
|
|
325
378
|
storage = inject(ASYNC_CONFIG_STORAGE);
|
|
326
379
|
snack = inject(MatSnackBar);
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
380
|
+
i18n = inject(PraxisI18nService);
|
|
381
|
+
resourceDiscovery = inject(ResourceDiscoveryService);
|
|
382
|
+
actionOpenAdapter = inject(ResourceActionOpenAdapterService);
|
|
383
|
+
surfaceOpenAdapter = inject(ResourceSurfaceOpenAdapterService);
|
|
384
|
+
global = (() => {
|
|
385
|
+
try {
|
|
386
|
+
return inject(GlobalConfigService);
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
return undefined;
|
|
390
|
+
}
|
|
391
|
+
})();
|
|
392
|
+
surfaceService = inject(GLOBAL_SURFACE_SERVICE, { optional: true });
|
|
333
393
|
componentKeys = inject(ComponentKeyService);
|
|
334
|
-
route = (() => {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
394
|
+
route = (() => {
|
|
395
|
+
try {
|
|
396
|
+
return inject(ActivatedRoute);
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
return undefined;
|
|
400
|
+
}
|
|
401
|
+
})();
|
|
340
402
|
warnedMissingId = false;
|
|
341
403
|
lastEmittedTableRuntimeConfigJson = '';
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
404
|
+
lastAssignedTableConfigJson = '';
|
|
405
|
+
lastAssignedTableCrudContextJson = '';
|
|
406
|
+
lastAppliedResourceIdentity = null;
|
|
407
|
+
tableCollectionLinks = null;
|
|
408
|
+
collectionCapabilities = null;
|
|
409
|
+
collectionCapabilitiesRequestHref = null;
|
|
410
|
+
collectionCapabilitiesResolvedHref = null;
|
|
411
|
+
collectionCapabilitiesRequestSeq = 0;
|
|
348
412
|
onResetPreferences() {
|
|
349
413
|
try {
|
|
350
414
|
const keyId = this.componentKeyId();
|
|
@@ -352,44 +416,51 @@ class PraxisCrudComponent {
|
|
|
352
416
|
return;
|
|
353
417
|
const key = `crud-overrides:${keyId}`;
|
|
354
418
|
void firstValueFrom(this.storage.clearConfig(key)).catch(() => { });
|
|
355
|
-
this.snack.open('
|
|
419
|
+
this.snack.open(this.tx('crud.preferences.resetSuccess', 'CRUD overrides reset'), undefined, {
|
|
420
|
+
duration: 2000,
|
|
421
|
+
});
|
|
356
422
|
}
|
|
357
423
|
catch { }
|
|
358
424
|
}
|
|
359
425
|
ngOnChanges(changes) {
|
|
360
|
-
if (changes['metadata']) {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
this.
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
426
|
+
if (!changes['metadata']) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
const parsed = typeof this.metadata === 'string'
|
|
431
|
+
? JSON.parse(this.metadata)
|
|
432
|
+
: this.metadata;
|
|
433
|
+
this.resolvedMetadata = parsed;
|
|
434
|
+
assertCrudMetadata(this.resolvedMetadata, {
|
|
435
|
+
allowDeferredActionBindings: true,
|
|
436
|
+
});
|
|
437
|
+
this.tableQueryContext = this.resolveQueryContext(this.resolvedMetadata);
|
|
438
|
+
this.tableFilterCriteria = this.resolveFilterCriteria(this.resolvedMetadata);
|
|
439
|
+
this.tableCollectionLinks = null;
|
|
440
|
+
this.collectionCapabilities = null;
|
|
441
|
+
this.collectionCapabilitiesRequestHref = null;
|
|
442
|
+
this.collectionCapabilitiesResolvedHref = null;
|
|
443
|
+
this.collectionCapabilitiesRequestSeq += 1;
|
|
444
|
+
this.applyResolvedCrudState(this.resolvedMetadata);
|
|
445
|
+
}
|
|
446
|
+
catch (err) {
|
|
447
|
+
this.error.emit(err);
|
|
383
448
|
}
|
|
384
449
|
}
|
|
385
|
-
async onAction(action, row) {
|
|
450
|
+
async onAction(action, row, runtimeEvent) {
|
|
386
451
|
try {
|
|
387
452
|
document.activeElement?.blur();
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
453
|
+
const openedByDiscovery = await this.tryOpenDiscoveredCrudSurface(action, row);
|
|
454
|
+
if (openedByDiscovery) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const handledByWorkflowAction = await this.tryOpenDiscoveredWorkflowAction(action, row, runtimeEvent);
|
|
458
|
+
if (handledByWorkflowAction) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
let actionMeta = this.resolvedMetadata.actions?.find((candidate) => candidate.action === action);
|
|
391
462
|
if (!actionMeta) {
|
|
392
|
-
const ctxAction = this.tableCrudContext?.actions?.find((
|
|
463
|
+
const ctxAction = this.tableCrudContext?.actions?.find((candidate) => candidate.action === action);
|
|
393
464
|
if (ctxAction) {
|
|
394
465
|
actionMeta = {
|
|
395
466
|
action: ctxAction.action,
|
|
@@ -399,7 +470,6 @@ class PraxisCrudComponent {
|
|
|
399
470
|
};
|
|
400
471
|
}
|
|
401
472
|
else {
|
|
402
|
-
// Minimal stub – CrudLauncherService will merge overrides/defaults
|
|
403
473
|
actionMeta = { action };
|
|
404
474
|
}
|
|
405
475
|
}
|
|
@@ -412,9 +482,7 @@ class PraxisCrudComponent {
|
|
|
412
482
|
this.afterClose.emit();
|
|
413
483
|
};
|
|
414
484
|
const { mode, ref } = await this.launcher.launch(effectiveAction, row, this.resolvedMetadata, this.componentKeyId(), {
|
|
415
|
-
onClose: () =>
|
|
416
|
-
emitDrawerClose();
|
|
417
|
-
},
|
|
485
|
+
onClose: () => emitDrawerClose(),
|
|
418
486
|
onResult: (result) => {
|
|
419
487
|
emitDrawerClose();
|
|
420
488
|
const data = (result?.data || {});
|
|
@@ -432,35 +500,33 @@ class PraxisCrudComponent {
|
|
|
432
500
|
},
|
|
433
501
|
});
|
|
434
502
|
this.afterOpen.emit({ mode, action: effectiveAction.action });
|
|
435
|
-
if (ref) {
|
|
436
|
-
|
|
437
|
-
ref
|
|
438
|
-
.afterClosed()
|
|
439
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
440
|
-
.subscribe((result) => {
|
|
441
|
-
this.afterClose.emit();
|
|
442
|
-
if (result?.type === 'save') {
|
|
443
|
-
const data = result.data;
|
|
444
|
-
const id = data?.[idField];
|
|
445
|
-
this.afterSave.emit({ id, data: result.data });
|
|
446
|
-
this.refreshTable();
|
|
447
|
-
}
|
|
448
|
-
if (result?.type === 'delete') {
|
|
449
|
-
const data = result.data;
|
|
450
|
-
const id = data?.[idField];
|
|
451
|
-
this.afterDelete.emit({ id });
|
|
452
|
-
this.refreshTable();
|
|
453
|
-
}
|
|
454
|
-
});
|
|
503
|
+
if (!ref) {
|
|
504
|
+
return;
|
|
455
505
|
}
|
|
506
|
+
const idField = this.getIdField();
|
|
507
|
+
ref
|
|
508
|
+
.afterClosed()
|
|
509
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
510
|
+
.subscribe((result) => {
|
|
511
|
+
this.afterClose.emit();
|
|
512
|
+
if (result?.type === 'save') {
|
|
513
|
+
const data = result.data;
|
|
514
|
+
const id = data?.[idField];
|
|
515
|
+
this.afterSave.emit({ id, data: result.data });
|
|
516
|
+
this.refreshTable();
|
|
517
|
+
}
|
|
518
|
+
if (result?.type === 'delete') {
|
|
519
|
+
const data = result.data;
|
|
520
|
+
const id = data?.[idField];
|
|
521
|
+
this.afterDelete.emit({ id });
|
|
522
|
+
this.refreshTable();
|
|
523
|
+
}
|
|
524
|
+
});
|
|
456
525
|
}
|
|
457
526
|
catch (err) {
|
|
458
527
|
this.error.emit(err);
|
|
459
528
|
}
|
|
460
529
|
}
|
|
461
|
-
refreshTable() {
|
|
462
|
-
this.table.refetch();
|
|
463
|
-
}
|
|
464
530
|
getCurrentTableConfigSnapshot() {
|
|
465
531
|
const current = this.table?.config || this.tableConfigForBinding || this.effectiveTableConfig;
|
|
466
532
|
if (!current)
|
|
@@ -483,20 +549,50 @@ class PraxisCrudComponent {
|
|
|
483
549
|
}
|
|
484
550
|
this.emitTableRuntimeConfigSnapshot();
|
|
485
551
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
552
|
+
onCollectionLinksChange(links) {
|
|
553
|
+
this.tableCollectionLinks = links || null;
|
|
554
|
+
const capabilitiesHref = this.resolveCollectionCapabilitiesHref(links);
|
|
555
|
+
if (!capabilitiesHref) {
|
|
556
|
+
this.collectionCapabilitiesRequestSeq += 1;
|
|
557
|
+
this.collectionCapabilitiesRequestHref = null;
|
|
558
|
+
this.collectionCapabilitiesResolvedHref = null;
|
|
559
|
+
if (this.collectionCapabilities) {
|
|
560
|
+
this.collectionCapabilities = null;
|
|
561
|
+
this.applyResolvedCrudState(this.resolvedMetadata);
|
|
562
|
+
this.syncRuntimeBindings();
|
|
563
|
+
}
|
|
489
564
|
return;
|
|
490
|
-
try {
|
|
491
|
-
const nextJson = JSON.stringify(snapshot);
|
|
492
|
-
if (nextJson === this.lastEmittedTableRuntimeConfigJson)
|
|
493
|
-
return;
|
|
494
|
-
this.lastEmittedTableRuntimeConfigJson = nextJson;
|
|
495
565
|
}
|
|
496
|
-
|
|
497
|
-
|
|
566
|
+
if (capabilitiesHref === this.collectionCapabilitiesRequestHref ||
|
|
567
|
+
(capabilitiesHref === this.collectionCapabilitiesResolvedHref && !!this.collectionCapabilities)) {
|
|
568
|
+
return;
|
|
498
569
|
}
|
|
499
|
-
this.
|
|
570
|
+
const requestSeq = ++this.collectionCapabilitiesRequestSeq;
|
|
571
|
+
this.collectionCapabilitiesRequestHref = capabilitiesHref;
|
|
572
|
+
void firstValueFrom(this.resourceDiscovery.getCapabilities(links || {}, this.buildDiscoveryOptions()))
|
|
573
|
+
.then((snapshot) => {
|
|
574
|
+
if (requestSeq !== this.collectionCapabilitiesRequestSeq) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
this.collectionCapabilitiesRequestHref = null;
|
|
578
|
+
this.collectionCapabilitiesResolvedHref = capabilitiesHref;
|
|
579
|
+
this.collectionCapabilities = snapshot;
|
|
580
|
+
this.applyResolvedCrudState(this.resolvedMetadata);
|
|
581
|
+
this.syncRuntimeBindings();
|
|
582
|
+
})
|
|
583
|
+
.catch(() => {
|
|
584
|
+
if (requestSeq !== this.collectionCapabilitiesRequestSeq) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
const hadCapabilities = !!this.collectionCapabilities;
|
|
588
|
+
this.collectionCapabilities = null;
|
|
589
|
+
this.collectionCapabilitiesRequestHref = null;
|
|
590
|
+
this.collectionCapabilitiesResolvedHref = null;
|
|
591
|
+
if (hadCapabilities) {
|
|
592
|
+
this.applyResolvedCrudState(this.resolvedMetadata);
|
|
593
|
+
this.syncRuntimeBindings();
|
|
594
|
+
}
|
|
595
|
+
});
|
|
500
596
|
}
|
|
501
597
|
resolveResourcePath(meta) {
|
|
502
598
|
return (meta?.resource?.path ||
|
|
@@ -516,73 +612,462 @@ class PraxisCrudComponent {
|
|
|
516
612
|
shouldRenderTable(meta) {
|
|
517
613
|
return this.resolveResourcePath(meta).length > 0 || Array.isArray(meta?.data);
|
|
518
614
|
}
|
|
519
|
-
|
|
520
|
-
return this.
|
|
615
|
+
getEmptyStateTitle() {
|
|
616
|
+
return this.tx('crud.emptyState.title', 'Connect CRUD to a resource');
|
|
617
|
+
}
|
|
618
|
+
getEmptyStateDescription() {
|
|
619
|
+
return this.tx('crud.emptyState.description', 'Provide metadata (resourcePath / schema) or metadata.data to enable the table and actions.');
|
|
620
|
+
}
|
|
621
|
+
getEmptyStatePrimaryAction() {
|
|
622
|
+
return this.tx('crud.emptyState.primaryAction', 'Configure metadata');
|
|
521
623
|
}
|
|
522
624
|
onConfigureRequested() {
|
|
523
625
|
this.configureRequested.emit();
|
|
524
626
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
627
|
+
refreshTable() {
|
|
628
|
+
this.table.refetch();
|
|
629
|
+
}
|
|
630
|
+
emitTableRuntimeConfigSnapshot() {
|
|
631
|
+
const snapshot = this.getCurrentTableConfigSnapshot();
|
|
632
|
+
if (!snapshot)
|
|
633
|
+
return;
|
|
634
|
+
try {
|
|
635
|
+
const nextJson = JSON.stringify(snapshot);
|
|
636
|
+
if (nextJson === this.lastEmittedTableRuntimeConfigJson)
|
|
637
|
+
return;
|
|
638
|
+
this.lastEmittedTableRuntimeConfigJson = nextJson;
|
|
639
|
+
}
|
|
640
|
+
catch { }
|
|
641
|
+
this.tableRuntimeConfigChange.emit(snapshot);
|
|
642
|
+
}
|
|
643
|
+
applyResolvedCrudState(meta) {
|
|
644
|
+
this.effectiveTableConfig = this.buildEffectiveTableConfig(meta, this.collectionCapabilities);
|
|
645
|
+
const nextTableConfig = this.effectiveTableConfig ||
|
|
646
|
+
(meta.table ?? createDefaultTableConfig());
|
|
647
|
+
this.assignTableConfigForBinding(nextTableConfig);
|
|
648
|
+
this.assignTableCrudContext(this.buildTableCrudContext(meta, this.collectionCapabilities));
|
|
649
|
+
this.lastAppliedResourceIdentity = this.resolveCrudResourceIdentity(meta);
|
|
650
|
+
}
|
|
651
|
+
assignTableConfigForBinding(next) {
|
|
652
|
+
const nextJson = this.safeSerialize(next);
|
|
653
|
+
if (nextJson !== null && nextJson === this.lastAssignedTableConfigJson) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
this.lastAssignedTableConfigJson = nextJson ?? '';
|
|
657
|
+
this.tableConfigForBinding = next;
|
|
658
|
+
}
|
|
659
|
+
assignTableCrudContext(next) {
|
|
660
|
+
const nextJson = this.safeSerialize(next);
|
|
661
|
+
if (nextJson !== null && nextJson === this.lastAssignedTableCrudContextJson) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
this.lastAssignedTableCrudContextJson = nextJson ?? '';
|
|
665
|
+
this.tableCrudContext = next;
|
|
666
|
+
}
|
|
667
|
+
safeSerialize(value) {
|
|
668
|
+
try {
|
|
669
|
+
return JSON.stringify(value);
|
|
670
|
+
}
|
|
671
|
+
catch {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
syncRuntimeBindings() {
|
|
676
|
+
try {
|
|
677
|
+
this.cdr.detectChanges();
|
|
678
|
+
}
|
|
679
|
+
catch {
|
|
680
|
+
try {
|
|
681
|
+
this.cdr.markForCheck();
|
|
682
|
+
}
|
|
683
|
+
catch { }
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
resolveQueryContext(meta) {
|
|
687
|
+
return this.isRecord(meta?.queryContext) ? meta.queryContext : null;
|
|
688
|
+
}
|
|
689
|
+
resolveFilterCriteria(meta) {
|
|
690
|
+
return this.isRecord(meta?.filterCriteria) ? { ...meta.filterCriteria } : {};
|
|
691
|
+
}
|
|
692
|
+
async tryOpenDiscoveredCrudSurface(action, row) {
|
|
693
|
+
const normalizedAction = String(action || '').trim().toLowerCase();
|
|
694
|
+
if (!this.isDiscoveryManagedCrudAction(normalizedAction) || !this.surfaceService) {
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
const catalog = await this.resolveDiscoveredSurfaceCatalog(normalizedAction, row);
|
|
698
|
+
const surface = this.selectSurfaceForCrudAction(normalizedAction, catalog?.surfaces || []);
|
|
699
|
+
const resourcePath = String(catalog?.resourcePath || this.resolveResourcePath(this.resolvedMetadata) || '').trim();
|
|
700
|
+
if (!catalog || !surface || !resourcePath) {
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
let payload;
|
|
704
|
+
try {
|
|
705
|
+
payload = this.surfaceOpenAdapter.toPayload(surface, {
|
|
706
|
+
resourcePath,
|
|
707
|
+
resourceId: this.resolveRowResourceId(row),
|
|
708
|
+
endpointKey: this.resolvedMetadata?.resource?.endpointKey,
|
|
709
|
+
apiUrlEntry: this.resolveDiscoveryApiEntry(),
|
|
710
|
+
group: catalog.group ?? null,
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
catch {
|
|
714
|
+
return false;
|
|
715
|
+
}
|
|
716
|
+
let openPromise;
|
|
717
|
+
try {
|
|
718
|
+
openPromise = Promise.resolve(this.surfaceService.open(payload, {
|
|
719
|
+
sourceId: this.componentKeyId() || undefined,
|
|
720
|
+
payload: { action: normalizedAction, row: row ?? null },
|
|
721
|
+
meta: {
|
|
722
|
+
crudId: this.crudId,
|
|
723
|
+
resourcePath,
|
|
724
|
+
surfaceId: surface.id,
|
|
725
|
+
operationId: surface.operationId,
|
|
726
|
+
},
|
|
727
|
+
runtime: {
|
|
728
|
+
row: row ?? null,
|
|
729
|
+
item: row ?? null,
|
|
730
|
+
state: {
|
|
731
|
+
action: normalizedAction,
|
|
732
|
+
resourcePath,
|
|
733
|
+
},
|
|
734
|
+
},
|
|
735
|
+
}));
|
|
736
|
+
}
|
|
737
|
+
catch {
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
this.afterOpen.emit({
|
|
741
|
+
mode: this.mapSurfacePresentationToCrudMode(payload?.presentation),
|
|
742
|
+
action,
|
|
743
|
+
});
|
|
744
|
+
void openPromise
|
|
745
|
+
.then((result) => this.handleDiscoveredSurfaceResult(result))
|
|
746
|
+
.catch((err) => this.error.emit(err));
|
|
747
|
+
return true;
|
|
748
|
+
}
|
|
749
|
+
async resolveDiscoveredSurfaceCatalog(action, row) {
|
|
750
|
+
try {
|
|
751
|
+
if (action === 'create') {
|
|
752
|
+
if (!this.tableCollectionLinks) {
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
755
|
+
return await firstValueFrom(this.resourceDiscovery.getSurfaces(this.tableCollectionLinks, this.buildDiscoveryOptions()));
|
|
756
|
+
}
|
|
757
|
+
const rowLinks = row?._links;
|
|
758
|
+
if (!rowLinks) {
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
return await firstValueFrom(this.resourceDiscovery.getSurfaces(rowLinks, this.buildDiscoveryOptions()));
|
|
762
|
+
}
|
|
763
|
+
catch {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
async tryOpenDiscoveredWorkflowAction(action, row, runtimeEvent) {
|
|
768
|
+
const normalizedAction = String(action || '').trim().toLowerCase();
|
|
769
|
+
if (!normalizedAction || this.isDiscoveryManagedCrudAction(normalizedAction) || !this.surfaceService) {
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
const providedAction = this.resolveProvidedWorkflowAction(normalizedAction, runtimeEvent?.actionConfig);
|
|
773
|
+
const catalog = providedAction ? null : await this.resolveDiscoveredActionCatalog(row);
|
|
774
|
+
const discoveredAction = providedAction || this.selectDiscoveredWorkflowAction(normalizedAction, catalog?.actions || []);
|
|
775
|
+
const resourcePath = String(catalog?.resourcePath || this.resolveResourcePath(this.resolvedMetadata) || '').trim();
|
|
776
|
+
if (!discoveredAction || !resourcePath) {
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
if (discoveredAction.availability?.allowed === false) {
|
|
780
|
+
this.snack.open(translateUnavailableWorkflowMessage(this.i18n, discoveredAction.availability), undefined, { duration: 2500 });
|
|
781
|
+
return true;
|
|
782
|
+
}
|
|
783
|
+
let payload;
|
|
784
|
+
try {
|
|
785
|
+
payload = this.actionOpenAdapter.toPayload(discoveredAction, {
|
|
786
|
+
resourcePath,
|
|
787
|
+
resourceId: this.resolveRowResourceId(row),
|
|
788
|
+
endpointKey: this.resolvedMetadata?.resource?.endpointKey,
|
|
789
|
+
apiUrlEntry: this.resolveDiscoveryApiEntry(),
|
|
790
|
+
group: catalog?.group ?? null,
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
catch {
|
|
794
|
+
return false;
|
|
795
|
+
}
|
|
796
|
+
let openPromise;
|
|
797
|
+
try {
|
|
798
|
+
openPromise = Promise.resolve(this.surfaceService.open(payload, {
|
|
799
|
+
sourceId: this.componentKeyId() || undefined,
|
|
800
|
+
payload: { action: normalizedAction, row: row ?? null },
|
|
801
|
+
meta: {
|
|
802
|
+
crudId: this.crudId,
|
|
803
|
+
resourcePath,
|
|
804
|
+
actionId: discoveredAction.id,
|
|
805
|
+
operationId: discoveredAction.operationId,
|
|
806
|
+
},
|
|
807
|
+
runtime: {
|
|
808
|
+
row: row ?? null,
|
|
809
|
+
item: row ?? null,
|
|
810
|
+
state: {
|
|
811
|
+
action: normalizedAction,
|
|
812
|
+
resourcePath,
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
}));
|
|
816
|
+
}
|
|
817
|
+
catch {
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
this.afterOpen.emit({
|
|
821
|
+
mode: this.mapSurfacePresentationToCrudMode(payload?.presentation),
|
|
822
|
+
action,
|
|
823
|
+
});
|
|
824
|
+
void openPromise
|
|
825
|
+
.then((result) => this.handleDiscoveredSurfaceResult(result))
|
|
826
|
+
.catch((err) => this.error.emit(err));
|
|
827
|
+
return true;
|
|
828
|
+
}
|
|
829
|
+
async resolveDiscoveredActionCatalog(row) {
|
|
830
|
+
try {
|
|
831
|
+
if (row) {
|
|
832
|
+
const rowLinks = row?._links;
|
|
833
|
+
if (!rowLinks) {
|
|
834
|
+
return null;
|
|
835
|
+
}
|
|
836
|
+
return await firstValueFrom(this.resourceDiscovery.getActions(rowLinks, this.buildDiscoveryOptions()));
|
|
837
|
+
}
|
|
838
|
+
if (!this.tableCollectionLinks) {
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
return await firstValueFrom(this.resourceDiscovery.getActions(this.tableCollectionLinks, this.buildDiscoveryOptions()));
|
|
842
|
+
}
|
|
843
|
+
catch {
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
selectDiscoveredWorkflowAction(action, actions) {
|
|
848
|
+
const normalizedAction = String(action || '').trim().toLowerCase();
|
|
849
|
+
return (actions
|
|
850
|
+
.slice()
|
|
851
|
+
.sort((left, right) => (left.order ?? 0) - (right.order ?? 0))
|
|
852
|
+
.find((candidate) => String(candidate.id || '').trim().toLowerCase() === normalizedAction) ||
|
|
853
|
+
null);
|
|
854
|
+
}
|
|
855
|
+
resolveProvidedWorkflowAction(action, candidate) {
|
|
856
|
+
if (!candidate || typeof candidate !== 'object') {
|
|
857
|
+
return null;
|
|
858
|
+
}
|
|
859
|
+
const normalizedId = String(candidate.id || '').trim().toLowerCase();
|
|
860
|
+
if (!normalizedId || normalizedId !== action) {
|
|
861
|
+
return null;
|
|
862
|
+
}
|
|
863
|
+
if (!candidate.operationId || !candidate.path || !candidate.method) {
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
866
|
+
return candidate;
|
|
867
|
+
}
|
|
868
|
+
selectSurfaceForCrudAction(action, surfaces) {
|
|
869
|
+
const candidates = surfaces
|
|
870
|
+
.filter((surface) => surface.availability?.allowed !== false)
|
|
871
|
+
.sort((left, right) => (left.order ?? 0) - (right.order ?? 0));
|
|
872
|
+
const preferredIds = this.getPreferredSurfaceIdsForCrudAction(action);
|
|
873
|
+
const preferred = candidates.find((surface) => preferredIds.includes(String(surface.id || '').trim().toLowerCase()));
|
|
874
|
+
if (preferred) {
|
|
875
|
+
return preferred;
|
|
876
|
+
}
|
|
877
|
+
if (action === 'create') {
|
|
878
|
+
return (candidates.find((surface) => surface.scope === 'COLLECTION' &&
|
|
879
|
+
this.isWritableCrudSurface(surface) &&
|
|
880
|
+
String(surface.method || '').toUpperCase() === 'POST') ||
|
|
881
|
+
candidates.find((surface) => surface.scope === 'COLLECTION' && this.isWritableCrudSurface(surface)) ||
|
|
882
|
+
null);
|
|
883
|
+
}
|
|
884
|
+
if (action === 'view') {
|
|
885
|
+
return (candidates.find((surface) => surface.scope === 'ITEM' &&
|
|
886
|
+
this.isReadableCrudSurface(surface) &&
|
|
887
|
+
String(surface.method || '').toUpperCase() === 'GET') ||
|
|
888
|
+
candidates.find((surface) => surface.scope === 'ITEM' && this.isReadableCrudSurface(surface)) ||
|
|
889
|
+
null);
|
|
890
|
+
}
|
|
891
|
+
if (action === 'edit') {
|
|
892
|
+
return (candidates.find((surface) => surface.scope === 'ITEM' &&
|
|
893
|
+
this.isWritableCrudSurface(surface) &&
|
|
894
|
+
['PUT', 'PATCH'].includes(String(surface.method || '').toUpperCase())) ||
|
|
895
|
+
candidates.find((surface) => surface.scope === 'ITEM' && this.isWritableCrudSurface(surface)) ||
|
|
896
|
+
null);
|
|
897
|
+
}
|
|
898
|
+
return null;
|
|
899
|
+
}
|
|
900
|
+
getPreferredSurfaceIdsForCrudAction(action) {
|
|
901
|
+
switch (action) {
|
|
902
|
+
case 'create':
|
|
903
|
+
return ['create'];
|
|
904
|
+
case 'view':
|
|
905
|
+
return ['detail', 'view'];
|
|
906
|
+
case 'edit':
|
|
907
|
+
return ['edit', 'update'];
|
|
908
|
+
default:
|
|
909
|
+
return [];
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
isDiscoveryManagedCrudAction(action) {
|
|
913
|
+
return action === 'create' || action === 'view' || action === 'edit';
|
|
914
|
+
}
|
|
915
|
+
isWritableCrudSurface(surface) {
|
|
916
|
+
return surface.kind === 'FORM' || surface.kind === 'PARTIAL_FORM';
|
|
917
|
+
}
|
|
918
|
+
isReadableCrudSurface(surface) {
|
|
919
|
+
return surface.kind === 'VIEW' || surface.kind === 'READ_PROJECTION';
|
|
920
|
+
}
|
|
921
|
+
resolveRowResourceId(row) {
|
|
922
|
+
if (!row) {
|
|
923
|
+
return null;
|
|
924
|
+
}
|
|
925
|
+
const value = row[this.getIdField()];
|
|
926
|
+
return typeof value === 'string' || typeof value === 'number' ? value : null;
|
|
927
|
+
}
|
|
928
|
+
mapSurfacePresentationToCrudMode(presentation) {
|
|
929
|
+
return presentation === 'modal' ? 'modal' : 'drawer';
|
|
930
|
+
}
|
|
931
|
+
handleDiscoveredSurfaceResult(result) {
|
|
932
|
+
this.afterClose.emit();
|
|
933
|
+
const data = (result?.data || {});
|
|
934
|
+
const idField = this.getIdField();
|
|
935
|
+
if (result?.type === 'save') {
|
|
936
|
+
const id = data?.[idField];
|
|
937
|
+
this.afterSave.emit({ id, data });
|
|
938
|
+
this.refreshTable();
|
|
939
|
+
}
|
|
940
|
+
if (result?.type === 'delete') {
|
|
941
|
+
const id = data?.[idField];
|
|
942
|
+
this.afterDelete.emit({ id });
|
|
943
|
+
this.refreshTable();
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
buildEffectiveTableConfig(meta, capabilities) {
|
|
532
947
|
const base = meta.table;
|
|
533
|
-
|
|
948
|
+
const current = this.getCurrentTableConfigSnapshot();
|
|
949
|
+
const nextResourceIdentity = this.resolveCrudResourceIdentity(meta);
|
|
534
950
|
const cfg = base
|
|
535
951
|
? JSON.parse(JSON.stringify(base))
|
|
536
952
|
: createDefaultTableConfig();
|
|
537
953
|
let changed = false;
|
|
954
|
+
if (nextResourceIdentity !== null &&
|
|
955
|
+
nextResourceIdentity === this.lastAppliedResourceIdentity &&
|
|
956
|
+
(cfg.columns?.length ?? 0) === 0 &&
|
|
957
|
+
(current?.columns?.length ?? 0) > 0) {
|
|
958
|
+
cfg.columns = JSON.parse(JSON.stringify(current.columns));
|
|
959
|
+
changed = true;
|
|
960
|
+
}
|
|
961
|
+
if (Array.isArray(meta?.data) && this.resolveResourcePath(meta).length === 0) {
|
|
962
|
+
const behavior = cfg.behavior || {};
|
|
963
|
+
const localDataMode = behavior.localDataMode || {};
|
|
964
|
+
if (localDataMode.enabled !== true) {
|
|
965
|
+
cfg.behavior = {
|
|
966
|
+
...behavior,
|
|
967
|
+
localDataMode: {
|
|
968
|
+
...localDataMode,
|
|
969
|
+
enabled: true,
|
|
970
|
+
},
|
|
971
|
+
};
|
|
972
|
+
changed = true;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
538
975
|
const ensureToolbar = () => {
|
|
539
976
|
if (!cfg.toolbar) {
|
|
540
977
|
cfg.toolbar = { visible: true, position: 'top' };
|
|
541
978
|
}
|
|
542
979
|
return cfg.toolbar;
|
|
543
980
|
};
|
|
544
|
-
|
|
545
|
-
const
|
|
546
|
-
const addAction = (meta.actions || []).find((a) => this.isAddLike(a));
|
|
981
|
+
const hasToolbarAdd = (cfg.toolbar?.actions || []).some((action) => this.isAddLike(action));
|
|
982
|
+
const addAction = this.resolveCreateToolbarAction(meta, capabilities);
|
|
547
983
|
if (addAction) {
|
|
548
984
|
if (hasToolbarAdd) {
|
|
549
|
-
const
|
|
550
|
-
|
|
985
|
+
const toolbar = ensureToolbar();
|
|
986
|
+
toolbar.visible = true;
|
|
551
987
|
}
|
|
552
988
|
else {
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
if (!
|
|
556
|
-
|
|
557
|
-
|
|
989
|
+
const toolbar = ensureToolbar();
|
|
990
|
+
toolbar.visible = true;
|
|
991
|
+
if (!toolbar.actions) {
|
|
992
|
+
toolbar.actions = [];
|
|
993
|
+
}
|
|
994
|
+
toolbar.actions.push({
|
|
558
995
|
id: addAction.id || 'add',
|
|
559
|
-
label: addAction.label || '
|
|
996
|
+
label: addAction.label || this.getCrudActionLabel('create'),
|
|
560
997
|
icon: addAction.icon || 'add',
|
|
561
998
|
type: 'button',
|
|
562
999
|
color: 'primary',
|
|
563
1000
|
position: 'end',
|
|
564
1001
|
action: addAction.action || 'add',
|
|
565
|
-
};
|
|
566
|
-
tb.actions.push(injected);
|
|
1002
|
+
});
|
|
567
1003
|
}
|
|
568
1004
|
changed = true;
|
|
569
1005
|
}
|
|
570
|
-
|
|
1006
|
+
const discoveredToolbarActions = this.resolveCollectionWorkflowToolbarActions(capabilities);
|
|
1007
|
+
if (discoveredToolbarActions.length) {
|
|
1008
|
+
const toolbar = ensureToolbar();
|
|
1009
|
+
toolbar.visible = true;
|
|
1010
|
+
if (!toolbar.actions) {
|
|
1011
|
+
toolbar.actions = [];
|
|
1012
|
+
}
|
|
1013
|
+
for (const action of discoveredToolbarActions) {
|
|
1014
|
+
const actionId = String(action.action || action.id || '')
|
|
1015
|
+
.trim()
|
|
1016
|
+
.toLowerCase();
|
|
1017
|
+
const alreadyExists = toolbar.actions.some((candidate) => String(candidate?.action || candidate?.id || candidate?.code || candidate?.key || '')
|
|
1018
|
+
.trim()
|
|
1019
|
+
.toLowerCase() === actionId);
|
|
1020
|
+
if (!alreadyExists) {
|
|
1021
|
+
toolbar.actions.push(action);
|
|
1022
|
+
changed = true;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
571
1026
|
const hostDefinedRow = !!(base && base.actions && base.actions.row);
|
|
572
|
-
const crudDefaults = this.global
|
|
573
|
-
const autoRow = crudDefaults.autoRowActions !== false;
|
|
1027
|
+
const crudDefaults = this.global?.get('crud.defaults') || {};
|
|
1028
|
+
const autoRow = crudDefaults.autoRowActions !== false;
|
|
574
1029
|
if (!hostDefinedRow && autoRow) {
|
|
575
|
-
const acts = (meta
|
|
576
|
-
const hasView =
|
|
577
|
-
|
|
578
|
-
|
|
1030
|
+
const acts = this.resolveContextCrudActions(meta, capabilities);
|
|
1031
|
+
const hasView = capabilities
|
|
1032
|
+
? this.supportsViewCapability(capabilities)
|
|
1033
|
+
: acts.some((action) => String(action.action).toLowerCase() === 'view');
|
|
1034
|
+
const hasEdit = capabilities
|
|
1035
|
+
? this.supportsEditCapability(capabilities)
|
|
1036
|
+
: acts.some((action) => String(action.action).toLowerCase() === 'edit');
|
|
1037
|
+
const includeDelete = capabilities
|
|
1038
|
+
? !!crudDefaults.includeDeleteInRow &&
|
|
1039
|
+
this.supportsDeleteCapability(capabilities) &&
|
|
1040
|
+
acts.some((action) => String(action.action).toLowerCase() === 'delete')
|
|
1041
|
+
: !!crudDefaults.includeDeleteInRow &&
|
|
1042
|
+
acts.some((action) => String(action.action).toLowerCase() === 'delete');
|
|
579
1043
|
const rowActions = [];
|
|
580
|
-
if (hasView)
|
|
581
|
-
rowActions.push({
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
1044
|
+
if (hasView) {
|
|
1045
|
+
rowActions.push({
|
|
1046
|
+
id: 'view',
|
|
1047
|
+
label: this.getCrudActionLabel('view'),
|
|
1048
|
+
icon: 'visibility',
|
|
1049
|
+
action: 'view',
|
|
1050
|
+
tooltip: this.getCrudActionLabel('view'),
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
if (hasEdit) {
|
|
1054
|
+
rowActions.push({
|
|
1055
|
+
id: 'edit',
|
|
1056
|
+
label: this.getCrudActionLabel('edit'),
|
|
1057
|
+
icon: 'edit',
|
|
1058
|
+
action: 'edit',
|
|
1059
|
+
tooltip: this.getCrudActionLabel('edit'),
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
if (includeDelete) {
|
|
1063
|
+
rowActions.push({
|
|
1064
|
+
id: 'delete',
|
|
1065
|
+
label: this.getCrudActionLabel('delete'),
|
|
1066
|
+
icon: 'delete',
|
|
1067
|
+
action: 'delete',
|
|
1068
|
+
tooltip: this.getCrudActionLabel('delete'),
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
586
1071
|
if (rowActions.length) {
|
|
587
1072
|
cfg.actions = cfg.actions || {};
|
|
588
1073
|
cfg.actions.row = {
|
|
@@ -597,53 +1082,197 @@ class PraxisCrudComponent {
|
|
|
597
1082
|
changed = true;
|
|
598
1083
|
}
|
|
599
1084
|
}
|
|
600
|
-
// Se nada foi alterado e havia base, não retornar para preservar semântica anterior
|
|
601
1085
|
if (!changed) {
|
|
602
1086
|
return base ? undefined : cfg;
|
|
603
1087
|
}
|
|
604
1088
|
return cfg;
|
|
605
1089
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
if (!a)
|
|
1090
|
+
isAddLike(action) {
|
|
1091
|
+
if (!action)
|
|
609
1092
|
return false;
|
|
610
|
-
const normalize = (
|
|
611
|
-
const id = normalize(
|
|
612
|
-
const icon = normalize(
|
|
613
|
-
const label = normalize(
|
|
1093
|
+
const normalize = (value) => String(value || '').trim().toLowerCase();
|
|
1094
|
+
const id = normalize(action.action || action.id || action.code || action.key || action.name || action.type);
|
|
1095
|
+
const icon = normalize(action.icon);
|
|
1096
|
+
const label = normalize(action.label);
|
|
614
1097
|
const addIds = new Set(['add', 'create', 'novo', 'new', 'incluir', 'inserir']);
|
|
615
1098
|
if (addIds.has(id))
|
|
616
1099
|
return true;
|
|
617
1100
|
if (icon === 'add' || icon === 'add_circle' || icon === 'add_box')
|
|
618
1101
|
return true;
|
|
619
|
-
if (label === 'adicionar' || label === 'novo' || label === 'criar' || label === 'incluir')
|
|
1102
|
+
if (label === 'adicionar' || label === 'novo' || label === 'criar' || label === 'incluir') {
|
|
620
1103
|
return true;
|
|
1104
|
+
}
|
|
621
1105
|
return false;
|
|
622
1106
|
}
|
|
623
|
-
|
|
624
|
-
buildTableCrudContext(meta) {
|
|
1107
|
+
buildTableCrudContext(meta, capabilities) {
|
|
625
1108
|
if (!meta)
|
|
626
1109
|
return undefined;
|
|
627
|
-
|
|
628
|
-
// so the editor can expose per-action overrides entirely via UI.
|
|
629
|
-
const baseActions = meta.actions && meta.actions.length
|
|
630
|
-
? meta.actions
|
|
631
|
-
: [{ action: 'create' }, { action: 'view' }, { action: 'edit' }];
|
|
1110
|
+
const baseActions = this.resolveContextCrudActions(meta, capabilities);
|
|
632
1111
|
return {
|
|
633
1112
|
tableId: this.crudId || 'default',
|
|
634
1113
|
componentKeyId: this.componentKeyId() || undefined,
|
|
635
1114
|
resourcePath: this.resolveResourcePath(meta) || undefined,
|
|
1115
|
+
endpointKey: meta.resource?.endpointKey,
|
|
636
1116
|
defaults: meta.defaults,
|
|
637
|
-
actions: baseActions.map((
|
|
638
|
-
action:
|
|
639
|
-
label:
|
|
640
|
-
formId:
|
|
641
|
-
route:
|
|
642
|
-
openMode:
|
|
1117
|
+
actions: baseActions.map((action) => ({
|
|
1118
|
+
action: action.action,
|
|
1119
|
+
label: action.label,
|
|
1120
|
+
formId: action.formId,
|
|
1121
|
+
route: action.route,
|
|
1122
|
+
openMode: action.openMode,
|
|
643
1123
|
})),
|
|
644
1124
|
idField: meta.resource?.idField || 'id',
|
|
645
1125
|
};
|
|
646
1126
|
}
|
|
1127
|
+
resolveCreateToolbarAction(meta, capabilities) {
|
|
1128
|
+
if (capabilities && !this.supportsCreateCapability(capabilities)) {
|
|
1129
|
+
return null;
|
|
1130
|
+
}
|
|
1131
|
+
const declared = (meta.actions || []).find((action) => this.isAddLike(action));
|
|
1132
|
+
if (declared) {
|
|
1133
|
+
return declared;
|
|
1134
|
+
}
|
|
1135
|
+
return capabilities ? { action: 'create', label: this.getCrudActionLabel('create') } : null;
|
|
1136
|
+
}
|
|
1137
|
+
resolveContextCrudActions(meta, capabilities) {
|
|
1138
|
+
const declaredActions = (meta.actions || []).filter(Boolean);
|
|
1139
|
+
if (!capabilities) {
|
|
1140
|
+
return declaredActions.length
|
|
1141
|
+
? declaredActions
|
|
1142
|
+
: [
|
|
1143
|
+
{ action: 'create', label: this.getCrudActionLabel('create') },
|
|
1144
|
+
{ action: 'view', label: this.getCrudActionLabel('view') },
|
|
1145
|
+
{ action: 'edit', label: this.getCrudActionLabel('edit') },
|
|
1146
|
+
];
|
|
1147
|
+
}
|
|
1148
|
+
const actionMap = new Map(declaredActions.map((action) => [String(action.action || '').trim().toLowerCase(), action]));
|
|
1149
|
+
const runtimeActions = [];
|
|
1150
|
+
if (this.supportsCreateCapability(capabilities)) {
|
|
1151
|
+
runtimeActions.push(actionMap.get('create') || {
|
|
1152
|
+
action: 'create',
|
|
1153
|
+
label: this.getCrudActionLabel('create'),
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
if (this.supportsViewCapability(capabilities)) {
|
|
1157
|
+
runtimeActions.push(actionMap.get('view') || {
|
|
1158
|
+
action: 'view',
|
|
1159
|
+
label: this.getCrudActionLabel('view'),
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
if (this.supportsEditCapability(capabilities)) {
|
|
1163
|
+
runtimeActions.push(actionMap.get('edit') || {
|
|
1164
|
+
action: 'edit',
|
|
1165
|
+
label: this.getCrudActionLabel('edit'),
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
if (this.supportsDeleteCapability(capabilities) && actionMap.has('delete')) {
|
|
1169
|
+
runtimeActions.push(actionMap.get('delete'));
|
|
1170
|
+
}
|
|
1171
|
+
for (const action of this.resolveCollectionWorkflowCrudActions(capabilities)) {
|
|
1172
|
+
const normalizedAction = String(action.action || '').trim().toLowerCase();
|
|
1173
|
+
if (!normalizedAction ||
|
|
1174
|
+
runtimeActions.some((candidate) => String(candidate.action || '').trim().toLowerCase() === normalizedAction)) {
|
|
1175
|
+
continue;
|
|
1176
|
+
}
|
|
1177
|
+
runtimeActions.push(action);
|
|
1178
|
+
}
|
|
1179
|
+
for (const action of declaredActions) {
|
|
1180
|
+
const normalizedAction = String(action.action || '').trim().toLowerCase();
|
|
1181
|
+
if (normalizedAction === 'create' ||
|
|
1182
|
+
normalizedAction === 'view' ||
|
|
1183
|
+
normalizedAction === 'edit' ||
|
|
1184
|
+
normalizedAction === 'delete') {
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
runtimeActions.push(action);
|
|
1188
|
+
}
|
|
1189
|
+
return runtimeActions;
|
|
1190
|
+
}
|
|
1191
|
+
resolveCollectionWorkflowCrudActions(capabilities) {
|
|
1192
|
+
if (!capabilities) {
|
|
1193
|
+
return [];
|
|
1194
|
+
}
|
|
1195
|
+
return (capabilities.actions || [])
|
|
1196
|
+
.filter((action) => action.scope === 'COLLECTION')
|
|
1197
|
+
.sort((left, right) => (left.order ?? 0) - (right.order ?? 0))
|
|
1198
|
+
.map((action) => ({
|
|
1199
|
+
action: action.id,
|
|
1200
|
+
label: action.title,
|
|
1201
|
+
}));
|
|
1202
|
+
}
|
|
1203
|
+
resolveCollectionWorkflowToolbarActions(capabilities) {
|
|
1204
|
+
if (!capabilities) {
|
|
1205
|
+
return [];
|
|
1206
|
+
}
|
|
1207
|
+
return (capabilities.actions || [])
|
|
1208
|
+
.filter((action) => action.scope === 'COLLECTION')
|
|
1209
|
+
.sort((left, right) => (left.order ?? 0) - (right.order ?? 0))
|
|
1210
|
+
.map((action) => ({
|
|
1211
|
+
id: action.id,
|
|
1212
|
+
label: action.title || action.id,
|
|
1213
|
+
type: 'button',
|
|
1214
|
+
appearance: 'outlined',
|
|
1215
|
+
position: 'end',
|
|
1216
|
+
action: action.id,
|
|
1217
|
+
disabled: action.availability?.allowed === false,
|
|
1218
|
+
tooltip: action.description ||
|
|
1219
|
+
(action.availability?.allowed === false
|
|
1220
|
+
? translateUnavailableWorkflowMessage(this.i18n, action.availability)
|
|
1221
|
+
: undefined),
|
|
1222
|
+
}));
|
|
1223
|
+
}
|
|
1224
|
+
supportsCreateCapability(snapshot) {
|
|
1225
|
+
return (this.hasCanonicalOperation(snapshot, 'create') ||
|
|
1226
|
+
snapshot.surfaces.some((surface) => surface.scope === 'COLLECTION' &&
|
|
1227
|
+
this.isWritableCrudSurface(surface) &&
|
|
1228
|
+
surface.availability?.allowed !== false));
|
|
1229
|
+
}
|
|
1230
|
+
supportsViewCapability(snapshot) {
|
|
1231
|
+
return this.hasCanonicalOperation(snapshot, 'byId');
|
|
1232
|
+
}
|
|
1233
|
+
supportsEditCapability(snapshot) {
|
|
1234
|
+
return this.hasCanonicalOperation(snapshot, 'update');
|
|
1235
|
+
}
|
|
1236
|
+
supportsDeleteCapability(snapshot) {
|
|
1237
|
+
return this.hasCanonicalOperation(snapshot, 'delete');
|
|
1238
|
+
}
|
|
1239
|
+
hasCanonicalOperation(snapshot, operation) {
|
|
1240
|
+
return snapshot.canonicalOperations?.[operation] === true;
|
|
1241
|
+
}
|
|
1242
|
+
resolveCollectionCapabilitiesHref(links) {
|
|
1243
|
+
try {
|
|
1244
|
+
return this.resourceDiscovery.resolveLinkHref(links || undefined, 'capabilities', this.buildDiscoveryOptions());
|
|
1245
|
+
}
|
|
1246
|
+
catch {
|
|
1247
|
+
return null;
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
resolveCrudResourceIdentity(meta) {
|
|
1251
|
+
const resourcePath = meta ? this.resolveResourcePath(meta) : '';
|
|
1252
|
+
if (!resourcePath) {
|
|
1253
|
+
return null;
|
|
1254
|
+
}
|
|
1255
|
+
const endpointKey = String(meta?.resource?.endpointKey || '').trim().toLowerCase();
|
|
1256
|
+
return `${resourcePath.toLowerCase()}|${endpointKey}`;
|
|
1257
|
+
}
|
|
1258
|
+
buildDiscoveryOptions() {
|
|
1259
|
+
const endpointKey = this.resolvedMetadata?.resource?.endpointKey;
|
|
1260
|
+
return endpointKey ? { endpointKey: endpointKey } : undefined;
|
|
1261
|
+
}
|
|
1262
|
+
resolveDiscoveryApiEntry() {
|
|
1263
|
+
try {
|
|
1264
|
+
return this.resourceDiscovery.resolveApiEntry(this.buildDiscoveryOptions());
|
|
1265
|
+
}
|
|
1266
|
+
catch {
|
|
1267
|
+
return null;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
isRecord(value) {
|
|
1271
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
1272
|
+
}
|
|
1273
|
+
getIdField() {
|
|
1274
|
+
return this.resolvedMetadata?.resource?.idField || 'id';
|
|
1275
|
+
}
|
|
647
1276
|
componentKeyId() {
|
|
648
1277
|
const key = this.componentKeys.buildComponentId({
|
|
649
1278
|
componentType: 'praxis-crud',
|
|
@@ -663,18 +1292,46 @@ class PraxisCrudComponent {
|
|
|
663
1292
|
this.warnedMissingId = true;
|
|
664
1293
|
console.warn('[PraxisCrud] crudId is required for config persistence.');
|
|
665
1294
|
}
|
|
1295
|
+
getCrudActionLabel(action) {
|
|
1296
|
+
switch (action) {
|
|
1297
|
+
case 'create':
|
|
1298
|
+
return this.tx('crud.actions.create', 'Add');
|
|
1299
|
+
case 'view':
|
|
1300
|
+
return this.tx('crud.actions.view', 'View');
|
|
1301
|
+
case 'edit':
|
|
1302
|
+
return this.tx('crud.actions.edit', 'Edit');
|
|
1303
|
+
case 'delete':
|
|
1304
|
+
return this.tx('crud.actions.delete', 'Delete');
|
|
1305
|
+
default:
|
|
1306
|
+
return action;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
tx(key, fallback) {
|
|
1310
|
+
try {
|
|
1311
|
+
return translateCrudRuntimeText(this.i18n, key, fallback);
|
|
1312
|
+
}
|
|
1313
|
+
catch {
|
|
1314
|
+
return fallback;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
666
1317
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisCrudComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
667
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisCrudComponent, isStandalone: true, selector: "praxis-crud", inputs: { metadata: "metadata", crudId: "crudId", componentInstanceId: "componentInstanceId", context: "context", enableCustomization: "enableCustomization" }, outputs: { configureRequested: "configureRequested", afterOpen: "afterOpen", afterClose: "afterClose", afterSave: "afterSave", afterDelete: "afterDelete", error: "error", tableRuntimeConfigChange: "tableRuntimeConfigChange" },
|
|
1318
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisCrudComponent, isStandalone: true, selector: "praxis-crud", inputs: { metadata: "metadata", crudId: "crudId", componentInstanceId: "componentInstanceId", context: "context", enableCustomization: "enableCustomization" }, outputs: { configureRequested: "configureRequested", afterOpen: "afterOpen", afterClose: "afterClose", afterSave: "afterSave", afterDelete: "afterDelete", error: "error", tableRuntimeConfigChange: "tableRuntimeConfigChange" }, providers: [
|
|
1319
|
+
providePraxisI18nConfig(RESOURCE_DISCOVERY_I18N_CONFIG),
|
|
1320
|
+
providePraxisI18nConfig(PRAXIS_CRUD_RUNTIME_I18N_CONFIG),
|
|
1321
|
+
], viewQueries: [{ propertyName: "table", first: true, predicate: PraxisTable, descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
668
1322
|
@if (shouldRenderTable(resolvedMetadata)) {
|
|
669
1323
|
<praxis-table
|
|
670
1324
|
[config]="tableConfigForBinding"
|
|
671
1325
|
[resourcePath]="resolveResourcePath(resolvedMetadata)"
|
|
672
1326
|
[data]="resolveTableData(resolvedMetadata)"
|
|
1327
|
+
[queryContext]="tableQueryContext"
|
|
1328
|
+
[filterCriteria]="tableFilterCriteria"
|
|
673
1329
|
[tableId]="crudId || 'default'"
|
|
674
1330
|
[crudContext]="tableCrudContext"
|
|
675
1331
|
[enableCustomization]="enableCustomization"
|
|
676
|
-
(rowAction)="onAction($event.action, $event.row)"
|
|
1332
|
+
(rowAction)="onAction($event.action, $event.row, $event)"
|
|
677
1333
|
(toolbarAction)="onAction($event.action)"
|
|
1334
|
+
(collectionLinksChange)="onCollectionLinksChange($event)"
|
|
678
1335
|
(reset)="onResetPreferences()"
|
|
679
1336
|
(metadataChange)="onTableMetadataChange()"
|
|
680
1337
|
(loadingStateChange)="onTableLoadingStateChange($event)"
|
|
@@ -683,31 +1340,33 @@ class PraxisCrudComponent {
|
|
|
683
1340
|
@if (enableCustomization) {
|
|
684
1341
|
<praxis-empty-state-card
|
|
685
1342
|
icon="table_rows"
|
|
686
|
-
[title]="
|
|
687
|
-
[description]="
|
|
688
|
-
[primaryAction]="{ label:
|
|
1343
|
+
[title]="getEmptyStateTitle()"
|
|
1344
|
+
[description]="getEmptyStateDescription()"
|
|
1345
|
+
[primaryAction]="{ label: getEmptyStatePrimaryAction(), icon: 'bolt', action: onConfigureRequested.bind(this) }"
|
|
689
1346
|
/>
|
|
690
1347
|
}
|
|
691
1348
|
}
|
|
692
|
-
`, isInline: true, dependencies: [{ kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "data", "tableId", "componentInstanceId", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "dslFunctionRegistry", "filterCriteria", "queryContext", "enableCustomization", "dense"], outputs: ["rowClick", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "columnReorder", "columnReorderAttempt", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "metadataChange", "loadingStateChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }] });
|
|
1349
|
+
`, isInline: true, styles: [":host{display:block;width:100%;min-width:0;max-width:100%}\n"], dependencies: [{ kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "data", "tableId", "componentInstanceId", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "dslFunctionRegistry", "filterCriteria", "queryContext", "enableCustomization", "dense"], outputs: ["rowClick", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "columnReorder", "columnReorderAttempt", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "metadataChange", "loadingStateChange", "collectionLinksChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }] });
|
|
693
1350
|
}
|
|
694
1351
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisCrudComponent, decorators: [{
|
|
695
1352
|
type: Component,
|
|
696
|
-
args: [{
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
template: `
|
|
1353
|
+
args: [{ selector: 'praxis-crud', standalone: true, imports: [PraxisTable, EmptyStateCardComponent], providers: [
|
|
1354
|
+
providePraxisI18nConfig(RESOURCE_DISCOVERY_I18N_CONFIG),
|
|
1355
|
+
providePraxisI18nConfig(PRAXIS_CRUD_RUNTIME_I18N_CONFIG),
|
|
1356
|
+
], template: `
|
|
701
1357
|
@if (shouldRenderTable(resolvedMetadata)) {
|
|
702
1358
|
<praxis-table
|
|
703
1359
|
[config]="tableConfigForBinding"
|
|
704
1360
|
[resourcePath]="resolveResourcePath(resolvedMetadata)"
|
|
705
1361
|
[data]="resolveTableData(resolvedMetadata)"
|
|
1362
|
+
[queryContext]="tableQueryContext"
|
|
1363
|
+
[filterCriteria]="tableFilterCriteria"
|
|
706
1364
|
[tableId]="crudId || 'default'"
|
|
707
1365
|
[crudContext]="tableCrudContext"
|
|
708
1366
|
[enableCustomization]="enableCustomization"
|
|
709
|
-
(rowAction)="onAction($event.action, $event.row)"
|
|
1367
|
+
(rowAction)="onAction($event.action, $event.row, $event)"
|
|
710
1368
|
(toolbarAction)="onAction($event.action)"
|
|
1369
|
+
(collectionLinksChange)="onCollectionLinksChange($event)"
|
|
711
1370
|
(reset)="onResetPreferences()"
|
|
712
1371
|
(metadataChange)="onTableMetadataChange()"
|
|
713
1372
|
(loadingStateChange)="onTableLoadingStateChange($event)"
|
|
@@ -716,14 +1375,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
716
1375
|
@if (enableCustomization) {
|
|
717
1376
|
<praxis-empty-state-card
|
|
718
1377
|
icon="table_rows"
|
|
719
|
-
[title]="
|
|
720
|
-
[description]="
|
|
721
|
-
[primaryAction]="{ label:
|
|
1378
|
+
[title]="getEmptyStateTitle()"
|
|
1379
|
+
[description]="getEmptyStateDescription()"
|
|
1380
|
+
[primaryAction]="{ label: getEmptyStatePrimaryAction(), icon: 'bolt', action: onConfigureRequested.bind(this) }"
|
|
722
1381
|
/>
|
|
723
1382
|
}
|
|
724
1383
|
}
|
|
725
|
-
`,
|
|
726
|
-
}]
|
|
1384
|
+
`, styles: [":host{display:block;width:100%;min-width:0;max-width:100%}\n"] }]
|
|
727
1385
|
}], propDecorators: { metadata: [{
|
|
728
1386
|
type: Input,
|
|
729
1387
|
args: [{ required: true }]
|
|
@@ -771,6 +1429,11 @@ class DynamicFormDialogHostComponent {
|
|
|
771
1429
|
destroyRef = inject(DestroyRef);
|
|
772
1430
|
resourcePath;
|
|
773
1431
|
resourceId;
|
|
1432
|
+
schemaUrl;
|
|
1433
|
+
submitUrl;
|
|
1434
|
+
submitMethod;
|
|
1435
|
+
apiEndpointKey;
|
|
1436
|
+
apiUrlEntry;
|
|
774
1437
|
mode = 'create';
|
|
775
1438
|
backConfig;
|
|
776
1439
|
idField = 'id';
|
|
@@ -815,6 +1478,14 @@ class DynamicFormDialogHostComponent {
|
|
|
815
1478
|
}
|
|
816
1479
|
this.idField = this.data.metadata?.resource?.idField ?? 'id';
|
|
817
1480
|
this.resourceId = this.data.inputs?.[this.idField];
|
|
1481
|
+
this.schemaUrl = this.data.inputs?.['schemaUrl'] ?? null;
|
|
1482
|
+
this.submitUrl = this.data.inputs?.['submitUrl'] ?? null;
|
|
1483
|
+
this.submitMethod = this.data.inputs?.['submitMethod'] ?? null;
|
|
1484
|
+
this.apiEndpointKey =
|
|
1485
|
+
this.data.inputs?.['apiEndpointKey'] ??
|
|
1486
|
+
this.data.metadata?.resource?.endpointKey ??
|
|
1487
|
+
null;
|
|
1488
|
+
this.apiUrlEntry = this.data.inputs?.['apiUrlEntry'] ?? null;
|
|
818
1489
|
const act = this.data.action?.action;
|
|
819
1490
|
this.mode = act === 'edit' ? 'edit' : act === 'view' ? 'view' : 'create';
|
|
820
1491
|
// Back config: defaults from metadata/action, overridden by saved per-form config
|
|
@@ -1004,13 +1675,18 @@ class DynamicFormDialogHostComponent {
|
|
|
1004
1675
|
[resourcePath]="resourcePath"
|
|
1005
1676
|
[resourceId]="resourceId"
|
|
1006
1677
|
[mode]="mode"
|
|
1678
|
+
[schemaUrl]="schemaUrl"
|
|
1679
|
+
[submitUrl]="submitUrl"
|
|
1680
|
+
[submitMethod]="submitMethod"
|
|
1681
|
+
[apiEndpointKey]="apiEndpointKey"
|
|
1682
|
+
[apiUrlEntry]="apiUrlEntry"
|
|
1007
1683
|
[presentationModeGlobal]="mode === 'view' ? true : null"
|
|
1008
1684
|
[backConfig]="backConfig"
|
|
1009
1685
|
(formSubmit)="onSave($event)"
|
|
1010
1686
|
(formCancel)="onCancel()"
|
|
1011
1687
|
></praxis-dynamic-form>
|
|
1012
1688
|
</mat-dialog-content>
|
|
1013
|
-
`, isInline: true, styles: [":host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}:host([data-density=\"compact\"]){--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}.dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);padding:0 var(--dlg-pad);height:var(--dlg-header-h);margin:0;background:var(--md-sys-color-surface-container-high);border-bottom:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}.dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}.spacer{flex:1}.dialog-content{overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}.dialog-header button.mat-icon-button{color:var(--md-sys-color-on-surface-variant)}.dialog-header button.mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "editorialContext", "mode", "config", "schemaSource", "enableCustomization", "formId", "componentInstanceId", "layout", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "widgetEvent"] }] });
|
|
1689
|
+
`, isInline: true, styles: [":host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}:host([data-density=\"compact\"]){--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}.dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);padding:0 var(--dlg-pad);height:var(--dlg-header-h);margin:0;background:var(--md-sys-color-surface-container-high);border-bottom:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}.dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}.spacer{flex:1}.dialog-content{overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}.dialog-header button.mat-icon-button{color:var(--md-sys-color-on-surface-variant)}.dialog-header button.mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "editorialContext", "mode", "config", "schemaSource", "schemaUrl", "submitUrl", "submitMethod", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "formId", "componentInstanceId", "layout", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "widgetEvent"] }] });
|
|
1014
1690
|
}
|
|
1015
1691
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicFormDialogHostComponent, decorators: [{
|
|
1016
1692
|
type: Component,
|
|
@@ -1060,6 +1736,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1060
1736
|
[resourcePath]="resourcePath"
|
|
1061
1737
|
[resourceId]="resourceId"
|
|
1062
1738
|
[mode]="mode"
|
|
1739
|
+
[schemaUrl]="schemaUrl"
|
|
1740
|
+
[submitUrl]="submitUrl"
|
|
1741
|
+
[submitMethod]="submitMethod"
|
|
1742
|
+
[apiEndpointKey]="apiEndpointKey"
|
|
1743
|
+
[apiUrlEntry]="apiUrlEntry"
|
|
1063
1744
|
[presentationModeGlobal]="mode === 'view' ? true : null"
|
|
1064
1745
|
[backConfig]="backConfig"
|
|
1065
1746
|
(formSubmit)="onSave($event)"
|
|
@@ -1115,6 +1796,16 @@ const PRAXIS_CRUD_COMPONENT_METADATA = {
|
|
|
1115
1796
|
type: 'Record<string, unknown>',
|
|
1116
1797
|
description: 'Contexto opaco do host. A implementacao atual o expõe como Input, mas nao o consome no launcher nem no tableCrudContext.',
|
|
1117
1798
|
},
|
|
1799
|
+
{
|
|
1800
|
+
name: 'metadata.queryContext',
|
|
1801
|
+
type: 'PraxisDataQueryContext | null',
|
|
1802
|
+
description: 'Contexto semantico de consulta encaminhado para a tabela interna do CRUD. Preferir este contrato para novo authoring remoto.',
|
|
1803
|
+
},
|
|
1804
|
+
{
|
|
1805
|
+
name: 'metadata.filterCriteria',
|
|
1806
|
+
type: 'Record<string, unknown> | null',
|
|
1807
|
+
description: 'Bridge declarativa de filtros encaminhada para a tabela interna. Para novo authoring, prefira metadata.queryContext.',
|
|
1808
|
+
},
|
|
1118
1809
|
{
|
|
1119
1810
|
name: 'enableCustomization',
|
|
1120
1811
|
type: 'boolean',
|
|
@@ -1448,4 +2139,3 @@ const CRUD_AI_CAPABILITIES = {
|
|
|
1448
2139
|
*/
|
|
1449
2140
|
|
|
1450
2141
|
export { CRUD_AI_CAPABILITIES, CrudLauncherService, CrudPageHeaderComponent, DialogService, DynamicFormDialogHostComponent, PRAXIS_CRUD_COMPONENT_METADATA, PraxisCrudComponent, assertCrudMetadata, providePraxisCrudMetadata };
|
|
1451
|
-
//# sourceMappingURL=praxisui-crud.mjs.map
|