@praxisui/crud 9.0.0-beta.71 → 9.0.0-beta.72

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.
@@ -6,7 +6,7 @@ import { Router, ActivatedRoute, RouterLink } from '@angular/router';
6
6
  import { MatSnackBar } from '@angular/material/snack-bar';
7
7
  import { firstValueFrom, BehaviorSubject, Subscription } from 'rxjs';
8
8
  import * as i2$1 from '@praxisui/core';
9
- import { ASYNC_CONFIG_STORAGE, GlobalConfigService, CrudOperationResolutionService, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, GLOBAL_SURFACE_SERVICE, SurfaceOutletRegistryService, ComponentKeyService, ResourceDiscoveryService, ResourceActionOpenAdapterService, ResourceSurfaceOpenAdapterService, translateUnavailableWorkflowMessage, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, GenericCrudService, ComponentMetadataRegistry } from '@praxisui/core';
9
+ import { ASYNC_CONFIG_STORAGE, GlobalConfigService, CrudOperationResolutionService, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, EnterpriseRuntimeContextService, GLOBAL_SURFACE_SERVICE, SurfaceOutletRegistryService, ComponentKeyService, ResourceDiscoveryService, ResourceActionOpenAdapterService, ResourceSurfaceOpenAdapterService, translateUnavailableWorkflowMessage, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, GenericCrudService, ComponentMetadataRegistry } from '@praxisui/core';
10
10
  import { SettingsPanelService } from '@praxisui/settings-panel';
11
11
  import { PraxisTableInlineAuthoringEditorComponent, PraxisTable } from '@praxisui/table';
12
12
  import { ConfirmDialogComponent } from '@praxisui/dynamic-fields';
@@ -874,6 +874,11 @@ const PRAXIS_CRUD_RUNTIME_I18N_CONFIG = {
874
874
  'crud.emptyState.title': 'Conecte o CRUD a um recurso',
875
875
  'crud.emptyState.description': 'Informe os metadados (resourcePath / schema) ou forneça metadata.data para habilitar a tabela e as ações.',
876
876
  'crud.emptyState.primaryAction': 'Configurar metadados',
877
+ 'crud.table.emptyState.initial.title': 'Sem registros em {label}',
878
+ 'crud.table.emptyState.initial.titleFallback': 'Nenhum registro disponível.',
879
+ 'crud.table.emptyState.initial.descriptionWithAction': 'Use a ação principal para adicionar o primeiro registro quando houver informações para cadastrar.',
880
+ 'crud.table.emptyState.filtered.title': 'Nenhum resultado encontrado.',
881
+ 'crud.table.emptyState.filtered.description': 'Revise os filtros ou ajuste o termo de busca.',
877
882
  'crud.preferences.resetSuccess': 'Overrides de CRUD redefinidos',
878
883
  'crud.actions.create': 'Adicionar',
879
884
  'crud.actions.view': 'Ver',
@@ -886,6 +891,11 @@ const PRAXIS_CRUD_RUNTIME_I18N_CONFIG = {
886
891
  'crud.emptyState.title': 'Connect CRUD to a resource',
887
892
  'crud.emptyState.description': 'Provide metadata (resourcePath / schema) or metadata.data to enable the table and actions.',
888
893
  'crud.emptyState.primaryAction': 'Configure metadata',
894
+ 'crud.table.emptyState.initial.title': 'No records in {label}',
895
+ 'crud.table.emptyState.initial.titleFallback': 'No records available.',
896
+ 'crud.table.emptyState.initial.descriptionWithAction': 'Use the primary action to add the first record when there is information to register.',
897
+ 'crud.table.emptyState.filtered.title': 'No results found.',
898
+ 'crud.table.emptyState.filtered.description': 'Review the filters or adjust the search term.',
889
899
  'crud.preferences.resetSuccess': 'CRUD overrides reset',
890
900
  'crud.actions.create': 'Add',
891
901
  'crud.actions.view': 'View',
@@ -3620,6 +3630,8 @@ class PraxisCrudComponent {
3620
3630
  componentInstanceId;
3621
3631
  context;
3622
3632
  enableCustomization = false;
3633
+ /** Capability publica exigida para authoring governado do CRUD e da tabela interna. */
3634
+ authoringCapability = null;
3623
3635
  configureRequested = new EventEmitter();
3624
3636
  afterOpen = new EventEmitter();
3625
3637
  afterClose = new EventEmitter();
@@ -3644,6 +3656,7 @@ class PraxisCrudComponent {
3644
3656
  table;
3645
3657
  storage = inject(ASYNC_CONFIG_STORAGE);
3646
3658
  settingsPanel = inject(SettingsPanelService);
3659
+ enterpriseRuntimeContext = inject(EnterpriseRuntimeContextService, { optional: true });
3647
3660
  snack = inject(MatSnackBar);
3648
3661
  dialog = inject(DialogService);
3649
3662
  i18n = inject(PraxisI18nService);
@@ -3678,6 +3691,18 @@ class PraxisCrudComponent {
3678
3691
  lastAppliedResourceIdentity = null;
3679
3692
  tableCollectionLinks = null;
3680
3693
  collectionCapabilities = null;
3694
+ activeAuthoringPanelRef;
3695
+ constructor() {
3696
+ this.enterpriseRuntimeContext?.contextChanges$
3697
+ .pipe(takeUntilDestroyed(this.destroyRef))
3698
+ .subscribe(() => {
3699
+ if (!this.isCustomizationAvailable()) {
3700
+ this.activeAuthoringPanelRef?.close();
3701
+ this.activeAuthoringPanelRef = undefined;
3702
+ }
3703
+ this.cdr.markForCheck();
3704
+ });
3705
+ }
3681
3706
  collectionCapabilitiesRequestHref = null;
3682
3707
  collectionCapabilitiesResolvedHref = null;
3683
3708
  collectionCapabilitiesRequestSeq = 0;
@@ -3792,6 +3817,10 @@ class PraxisCrudComponent {
3792
3817
  ...(actionMeta || { action: normalizedAction }),
3793
3818
  action: this.normalizeCrudActionName(actionMeta?.action ?? normalizedAction),
3794
3819
  };
3820
+ const handledByDuplicateDraft = await this.tryHandleCanonicalDuplicateDraftAction(effectiveAction, contextualRow);
3821
+ if (handledByDuplicateDraft) {
3822
+ return;
3823
+ }
3795
3824
  if (!this.hasExplicitOpenBinding(effectiveAction)) {
3796
3825
  const openedByDiscovery = await this.tryOpenDiscoveredCrudSurface(effectiveAction.action, contextualRow, runtimeEvent);
3797
3826
  if (openedByDiscovery) {
@@ -3975,6 +4004,9 @@ class PraxisCrudComponent {
3975
4004
  return this.tx('crud.emptyState.primaryAction', 'Configure metadata');
3976
4005
  }
3977
4006
  onConfigureRequested() {
4007
+ if (!this.isCustomizationAvailable()) {
4008
+ return;
4009
+ }
3978
4010
  this.configureRequested.emit();
3979
4011
  this.openCrudAuthoringFromTable();
3980
4012
  }
@@ -4025,6 +4057,68 @@ class PraxisCrudComponent {
4025
4057
  this.refreshTable();
4026
4058
  return true;
4027
4059
  }
4060
+ async tryHandleCanonicalDuplicateDraftAction(action, row) {
4061
+ const normalizedAction = this.normalizeCrudActionName(action.action);
4062
+ if (normalizedAction !== 'duplicate-draft' || !row) {
4063
+ return false;
4064
+ }
4065
+ const duplicateDraftUrl = this.getResourceDiscovery().resolveLinkHref(row['_links'], 'duplicate-draft', this.buildDiscoveryOptions());
4066
+ if (!duplicateDraftUrl) {
4067
+ return false;
4068
+ }
4069
+ const response = await firstValueFrom(this.http.post(duplicateDraftUrl, {}));
4070
+ const draft = this.unwrapRestData(response);
4071
+ if (!this.isRecord(draft)) {
4072
+ throw new Error('Duplicate draft action returned an invalid create draft.');
4073
+ }
4074
+ const configuredCreateAction = this.resolvedMetadata.actions?.find((candidate) => this.normalizeCrudActionName(candidate.action) === 'create') ?? { action: 'create' };
4075
+ const createAction = {
4076
+ ...configuredCreateAction,
4077
+ action: 'create',
4078
+ openMode: configuredCreateAction.openMode ??
4079
+ action.openMode ??
4080
+ this.resolvedMetadata.defaults?.openMode ??
4081
+ 'drawer',
4082
+ form: {
4083
+ ...(configuredCreateAction.form ?? {}),
4084
+ initialValue: { ...draft },
4085
+ },
4086
+ };
4087
+ let drawerCloseEmitted = false;
4088
+ const emitDrawerClose = () => {
4089
+ if (drawerCloseEmitted)
4090
+ return;
4091
+ drawerCloseEmitted = true;
4092
+ this.afterClose.emit();
4093
+ };
4094
+ const handleResult = (result) => {
4095
+ if (result?.type !== 'save') {
4096
+ return;
4097
+ }
4098
+ const data = this.isRecord(result.data) ? result.data : {};
4099
+ const id = data[this.getIdField()];
4100
+ this.afterSave.emit({ id, data });
4101
+ this.refreshTable();
4102
+ };
4103
+ const { mode, ref } = await this.launcher.launch(createAction, undefined, this.resolvedMetadata, this.componentKeyId(), {
4104
+ onClose: () => emitDrawerClose(),
4105
+ onResult: (result) => handleResult(result),
4106
+ }, {
4107
+ capabilities: this.collectionCapabilities,
4108
+ links: this.tableCollectionLinks,
4109
+ });
4110
+ this.afterOpen.emit({ mode, action: normalizedAction });
4111
+ if (mode !== 'drawer' && ref) {
4112
+ ref
4113
+ .afterClosed()
4114
+ .pipe(takeUntilDestroyed(this.destroyRef))
4115
+ .subscribe((result) => {
4116
+ this.afterClose.emit();
4117
+ handleResult(result);
4118
+ });
4119
+ }
4120
+ return true;
4121
+ }
4028
4122
  refreshTable() {
4029
4123
  if (this.tryRefreshMaterializedLocalData()) {
4030
4124
  return;
@@ -4544,6 +4638,7 @@ class PraxisCrudComponent {
4544
4638
  });
4545
4639
  }
4546
4640
  changed = true;
4641
+ changed = this.ensureCanonicalCollectionEmptyState(cfg, meta, addAction) || changed;
4547
4642
  }
4548
4643
  const discoveredToolbarActions = this.resolveCollectionWorkflowToolbarActions(capabilities);
4549
4644
  if (discoveredToolbarActions.length) {
@@ -4704,6 +4799,9 @@ class PraxisCrudComponent {
4704
4799
  };
4705
4800
  }
4706
4801
  openCrudAuthoringFromTable = () => {
4802
+ if (!this.isCustomizationAvailable()) {
4803
+ return;
4804
+ }
4707
4805
  const seed = this.currentAuthoringDocument
4708
4806
  || createCrudAuthoringDocument({ metadata: this.resolvedMetadata });
4709
4807
  const ref = openCrudMetadataEditor(this.settingsPanel, {
@@ -4712,13 +4810,33 @@ class PraxisCrudComponent {
4712
4810
  title: this.tx('crud.authoring.title', 'Configurações do CRUD'),
4713
4811
  titleIcon: 'table_chart',
4714
4812
  });
4813
+ this.activeAuthoringPanelRef = ref;
4715
4814
  ref.applied$
4716
4815
  .pipe(takeUntilDestroyed(this.destroyRef))
4717
4816
  .subscribe((payload) => this.applyCrudAuthoringPayload(payload, 'applied'));
4718
4817
  ref.saved$
4719
4818
  .pipe(takeUntilDestroyed(this.destroyRef))
4720
4819
  .subscribe((payload) => this.applyCrudAuthoringPayload(payload, 'saved'));
4820
+ ref.closed$
4821
+ .pipe(takeUntilDestroyed(this.destroyRef))
4822
+ .subscribe(() => {
4823
+ if (this.activeAuthoringPanelRef === ref) {
4824
+ this.activeAuthoringPanelRef = undefined;
4825
+ }
4826
+ });
4721
4827
  };
4828
+ isCustomizationAvailable() {
4829
+ if (!this.enableCustomization) {
4830
+ return false;
4831
+ }
4832
+ const requiredCapability = String(this.authoringCapability || '').trim();
4833
+ if (!requiredCapability) {
4834
+ return true;
4835
+ }
4836
+ const capabilities = this.enterpriseRuntimeContext?.snapshot?.capabilities;
4837
+ return Array.isArray(capabilities)
4838
+ && capabilities.some((capability) => capability === requiredCapability);
4839
+ }
4722
4840
  applyCrudAuthoringPayload(payload, eventName) {
4723
4841
  const next = parseLegacyOrCrudDocument(payload);
4724
4842
  this.currentAuthoringDocument = next;
@@ -4935,6 +5053,48 @@ class PraxisCrudComponent {
4935
5053
  return action;
4936
5054
  }
4937
5055
  }
5056
+ ensureCanonicalCollectionEmptyState(config, metadata, createAction) {
5057
+ const behavior = config.behavior || {};
5058
+ if (behavior.emptyState) {
5059
+ return false;
5060
+ }
5061
+ const resourceLabel = String(metadata.resource?.label
5062
+ || metadata.resource?.title
5063
+ || metadata.resource?.path
5064
+ || '').trim();
5065
+ const title = resourceLabel
5066
+ ? this.txWithParams('crud.table.emptyState.initial.title', 'Sem registros em {label}', { label: resourceLabel })
5067
+ : this.tx('crud.table.emptyState.initial.titleFallback', 'Nenhum registro disponível.');
5068
+ const actionId = String(createAction.action || createAction.id || 'create').trim() || 'create';
5069
+ const actionLabel = String(createAction.label || this.getCrudActionLabel('create')).trim();
5070
+ const actionIcon = String(createAction.icon || 'add').trim();
5071
+ config.behavior = {
5072
+ ...behavior,
5073
+ emptyState: {
5074
+ message: '',
5075
+ contexts: {
5076
+ initial: {
5077
+ title,
5078
+ description: this.tx('crud.table.emptyState.initial.descriptionWithAction', 'Use a ação principal para adicionar o primeiro registro quando houver informações para cadastrar.'),
5079
+ actions: [
5080
+ {
5081
+ label: actionLabel,
5082
+ action: actionId,
5083
+ icon: actionIcon,
5084
+ primary: true,
5085
+ },
5086
+ ],
5087
+ },
5088
+ filtered: {
5089
+ title: this.tx('crud.table.emptyState.filtered.title', 'Nenhum resultado encontrado.'),
5090
+ description: this.tx('crud.table.emptyState.filtered.description', 'Revise os filtros ou ajuste o termo de busca.'),
5091
+ actions: [],
5092
+ },
5093
+ },
5094
+ },
5095
+ };
5096
+ return true;
5097
+ }
4938
5098
  tx(key, fallback) {
4939
5099
  try {
4940
5100
  return translateCrudRuntimeText(this.i18n, key, fallback);
@@ -4943,8 +5103,19 @@ class PraxisCrudComponent {
4943
5103
  return fallback;
4944
5104
  }
4945
5105
  }
5106
+ txWithParams(key, fallback, params) {
5107
+ try {
5108
+ return this.interpolateTranslationParams(translateCrudRuntimeText(this.i18n, key, fallback, params), params);
5109
+ }
5110
+ catch {
5111
+ return this.interpolateTranslationParams(fallback, params);
5112
+ }
5113
+ }
5114
+ interpolateTranslationParams(text, params) {
5115
+ return Object.entries(params).reduce((current, [name, value]) => current.replaceAll(`{${name}}`, value), text);
5116
+ }
4946
5117
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisCrudComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4947
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", 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", rowClick: "rowClick", selectionChange: "selectionChange", tableRuntimeConfigChange: "tableRuntimeConfigChange", crudAuthoringDocumentApplied: "crudAuthoringDocumentApplied", crudAuthoringDocumentSaved: "crudAuthoringDocumentSaved" }, providers: [
5118
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisCrudComponent, isStandalone: true, selector: "praxis-crud", inputs: { metadata: "metadata", crudId: "crudId", componentInstanceId: "componentInstanceId", context: "context", enableCustomization: "enableCustomization", authoringCapability: "authoringCapability" }, outputs: { configureRequested: "configureRequested", afterOpen: "afterOpen", afterClose: "afterClose", afterSave: "afterSave", afterDelete: "afterDelete", error: "error", rowClick: "rowClick", selectionChange: "selectionChange", tableRuntimeConfigChange: "tableRuntimeConfigChange", crudAuthoringDocumentApplied: "crudAuthoringDocumentApplied", crudAuthoringDocumentSaved: "crudAuthoringDocumentSaved" }, providers: [
4948
5119
  providePraxisI18nConfig(RESOURCE_DISCOVERY_I18N_CONFIG),
4949
5120
  providePraxisI18nConfig(PRAXIS_CRUD_RUNTIME_I18N_CONFIG),
4950
5121
  ], viewQueries: [{ propertyName: "table", first: true, predicate: PraxisTable, descendants: true }], usesOnChanges: true, ngImport: i0, template: `
@@ -4958,6 +5129,7 @@ class PraxisCrudComponent {
4958
5129
  [tableId]="crudId || 'default'"
4959
5130
  [crudContext]="tableCrudContext"
4960
5131
  [enableCustomization]="enableCustomization"
5132
+ [authoringCapability]="authoringCapability"
4961
5133
  (rowClick)="onTableRowClick($event)"
4962
5134
  (selectionChange)="onTableSelectionChange($event)"
4963
5135
  (rowAction)="onAction($event.action, $event.row, $event)"
@@ -4969,7 +5141,7 @@ class PraxisCrudComponent {
4969
5141
  (loadingStateChange)="onTableLoadingStateChange($event)"
4970
5142
  ></praxis-table>
4971
5143
  } @else {
4972
- @if (enableCustomization) {
5144
+ @if (isCustomizationAvailable()) {
4973
5145
  <praxis-empty-state-card
4974
5146
  icon="table_rows"
4975
5147
  [title]="getEmptyStateTitle()"
@@ -4978,7 +5150,7 @@ class PraxisCrudComponent {
4978
5150
  />
4979
5151
  }
4980
5152
  }
4981
- `, 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", "configPersistenceStrategy", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "filterCriteria", "queryContext", "aiContext", "aiAssistantVoiceInputMode", "aiAssistantVoiceLanguage", "horizontalScroll", "enableCustomization", "dense"], outputs: ["rowClick", "widgetEvent", "resourceEvent", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "exportAction", "columnReorder", "columnReorderAttempt", "columnResize", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "configChange", "metadataChange", "loadingStateChange", "collectionLinksChange", "selectionChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone", "variant", "alignment", "density", "iconContainer"] }] });
5153
+ `, 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", "configPersistenceStrategy", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "filterCriteria", "queryContext", "aiContext", "aiAssistantVoiceInputMode", "aiAssistantVoiceLanguage", "horizontalScroll", "enableCustomization", "authoringCapability", "dense"], outputs: ["rowClick", "widgetEvent", "resourceEvent", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "exportAction", "columnReorder", "columnReorderAttempt", "columnResize", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "configChange", "metadataChange", "loadingStateChange", "collectionLinksChange", "selectionChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone", "variant", "alignment", "density", "iconContainer"] }] });
4982
5154
  }
4983
5155
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisCrudComponent, decorators: [{
4984
5156
  type: Component,
@@ -4996,6 +5168,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
4996
5168
  [tableId]="crudId || 'default'"
4997
5169
  [crudContext]="tableCrudContext"
4998
5170
  [enableCustomization]="enableCustomization"
5171
+ [authoringCapability]="authoringCapability"
4999
5172
  (rowClick)="onTableRowClick($event)"
5000
5173
  (selectionChange)="onTableSelectionChange($event)"
5001
5174
  (rowAction)="onAction($event.action, $event.row, $event)"
@@ -5007,7 +5180,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5007
5180
  (loadingStateChange)="onTableLoadingStateChange($event)"
5008
5181
  ></praxis-table>
5009
5182
  } @else {
5010
- @if (enableCustomization) {
5183
+ @if (isCustomizationAvailable()) {
5011
5184
  <praxis-empty-state-card
5012
5185
  icon="table_rows"
5013
5186
  [title]="getEmptyStateTitle()"
@@ -5017,7 +5190,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5017
5190
  }
5018
5191
  }
5019
5192
  `, styles: [":host{display:block;width:100%;min-width:0;max-width:100%}\n"] }]
5020
- }], propDecorators: { metadata: [{
5193
+ }], ctorParameters: () => [], propDecorators: { metadata: [{
5021
5194
  type: Input,
5022
5195
  args: [{ required: true }]
5023
5196
  }], crudId: [{
@@ -5029,6 +5202,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5029
5202
  type: Input
5030
5203
  }], enableCustomization: [{
5031
5204
  type: Input
5205
+ }], authoringCapability: [{
5206
+ type: Input
5032
5207
  }], configureRequested: [{
5033
5208
  type: Output
5034
5209
  }], afterOpen: [{
@@ -5703,6 +5878,7 @@ class PraxisCrudWidgetConfigEditor {
5703
5878
  componentInstanceId: this.inputs?.componentInstanceId ?? this.widgetKey,
5704
5879
  context: this.inputs?.context,
5705
5880
  enableCustomization: this.inputs?.enableCustomization ?? false,
5881
+ authoringCapability: this.inputs?.authoringCapability ?? null,
5706
5882
  };
5707
5883
  return {
5708
5884
  inputs,
@@ -5835,6 +6011,12 @@ const PRAXIS_CRUD_COMPONENT_METADATA = {
5835
6011
  default: false,
5836
6012
  description: 'Habilita modo de customização do layout.',
5837
6013
  },
6014
+ {
6015
+ name: 'authoringCapability',
6016
+ type: 'string | null',
6017
+ default: null,
6018
+ description: 'Capability publica do contexto corporativo exigida para disponibilizar authoring governado no CRUD e na tabela interna.',
6019
+ },
5838
6020
  ],
5839
6021
  outputs: [
5840
6022
  {
@@ -6345,6 +6527,8 @@ const PRAXIS_CRUD_AUTHORING_MANIFEST = {
6345
6527
  { name: 'crudId', type: 'string', description: 'Stable CRUD instance id used for table/form identity and persistence.' },
6346
6528
  { name: 'componentInstanceId', type: 'string', description: 'Optional stable host instance id for multiple CRUD widgets on the same route.' },
6347
6529
  { name: 'context', type: 'Record<string, unknown>', description: 'Opaque host context used for authoring seeds and launcher inputs.' },
6530
+ { name: 'enableCustomization', type: 'boolean', description: 'Explicit host opt-in for CRUD authoring surfaces.' },
6531
+ { name: 'authoringCapability', type: 'string | null', description: 'Public runtime capability required before CRUD and delegated table authoring can open.' },
6348
6532
  { name: 'afterOpen', type: '{ mode: FormOpenMode; action: string }', description: 'Emitted after a CRUD action opens.' },
6349
6533
  { name: 'afterSave', type: '{ id: string | number; data: unknown }', description: 'Emitted after save; CRUD refetches the list.' },
6350
6534
  { name: 'afterDelete', type: '{ id: string | number }', description: 'Emitted after delete; CRUD refetches the list.' },
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@praxisui/crud",
3
- "version": "9.0.0-beta.71",
3
+ "version": "9.0.0-beta.72",
4
4
  "description": "CRUD building blocks for Praxis UI: integrates dynamic forms and tables with unified configuration and services.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
7
7
  "@angular/core": "^21.0.0",
8
- "@praxisui/dynamic-form": "^9.0.0-beta.71",
9
- "@praxisui/table": "^9.0.0-beta.71",
10
- "@praxisui/core": "^9.0.0-beta.71",
11
- "@praxisui/dynamic-fields": "^9.0.0-beta.71",
12
- "@praxisui/settings-panel": "^9.0.0-beta.71",
8
+ "@praxisui/dynamic-form": "^9.0.0-beta.72",
9
+ "@praxisui/table": "^9.0.0-beta.72",
10
+ "@praxisui/core": "^9.0.0-beta.72",
11
+ "@praxisui/dynamic-fields": "^9.0.0-beta.72",
12
+ "@praxisui/settings-panel": "^9.0.0-beta.72",
13
13
  "@angular/cdk": "^21.0.0",
14
14
  "@angular/forms": "^21.0.0",
15
15
  "@angular/material": "^21.0.0",
16
16
  "@angular/router": "^21.0.0",
17
- "@praxisui/ai": "^9.0.0-beta.71",
17
+ "@praxisui/ai": "^9.0.0-beta.72",
18
18
  "rxjs": "~7.8.0"
19
19
  },
20
20
  "dependencies": {
@@ -157,6 +157,8 @@ declare class PraxisCrudComponent implements OnChanges {
157
157
  componentInstanceId?: string;
158
158
  context?: Record<string, unknown>;
159
159
  enableCustomization: boolean;
160
+ /** Capability publica exigida para authoring governado do CRUD e da tabela interna. */
161
+ authoringCapability: string | null;
160
162
  configureRequested: EventEmitter<void>;
161
163
  afterOpen: EventEmitter<{
162
164
  mode: FormOpenMode;
@@ -189,6 +191,7 @@ declare class PraxisCrudComponent implements OnChanges {
189
191
  private table;
190
192
  private readonly storage;
191
193
  private readonly settingsPanel;
194
+ private readonly enterpriseRuntimeContext;
192
195
  private readonly snack;
193
196
  private readonly dialog;
194
197
  private readonly i18n;
@@ -209,6 +212,8 @@ declare class PraxisCrudComponent implements OnChanges {
209
212
  private lastAppliedResourceIdentity;
210
213
  private tableCollectionLinks;
211
214
  private collectionCapabilities;
215
+ private activeAuthoringPanelRef;
216
+ constructor();
212
217
  private collectionCapabilitiesRequestHref;
213
218
  private collectionCapabilitiesResolvedHref;
214
219
  private collectionCapabilitiesRequestSeq;
@@ -237,6 +242,7 @@ declare class PraxisCrudComponent implements OnChanges {
237
242
  getEmptyStatePrimaryAction(): string;
238
243
  onConfigureRequested(): void;
239
244
  private tryHandleCanonicalDeleteAction;
245
+ private tryHandleCanonicalDuplicateDraftAction;
240
246
  private refreshTable;
241
247
  private tryRefreshMaterializedLocalData;
242
248
  private isCurrentMaterializedRefresh;
@@ -277,6 +283,7 @@ declare class PraxisCrudComponent implements OnChanges {
277
283
  private isCreateActionAliasName;
278
284
  private buildTableCrudContext;
279
285
  private readonly openCrudAuthoringFromTable;
286
+ isCustomizationAvailable(): boolean;
280
287
  private applyCrudAuthoringPayload;
281
288
  private buildWidgetPersistencePayload;
282
289
  private resolveCreateToolbarAction;
@@ -298,9 +305,12 @@ declare class PraxisCrudComponent implements OnChanges {
298
305
  private componentKeyId;
299
306
  private warnMissingId;
300
307
  private getCrudActionLabel;
308
+ private ensureCanonicalCollectionEmptyState;
301
309
  private tx;
310
+ private txWithParams;
311
+ private interpolateTranslationParams;
302
312
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<PraxisCrudComponent, never>;
303
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<PraxisCrudComponent, "praxis-crud", never, { "metadata": { "alias": "metadata"; "required": true; }; "crudId": { "alias": "crudId"; "required": true; }; "componentInstanceId": { "alias": "componentInstanceId"; "required": false; }; "context": { "alias": "context"; "required": false; }; "enableCustomization": { "alias": "enableCustomization"; "required": false; }; }, { "configureRequested": "configureRequested"; "afterOpen": "afterOpen"; "afterClose": "afterClose"; "afterSave": "afterSave"; "afterDelete": "afterDelete"; "error": "error"; "rowClick": "rowClick"; "selectionChange": "selectionChange"; "tableRuntimeConfigChange": "tableRuntimeConfigChange"; "crudAuthoringDocumentApplied": "crudAuthoringDocumentApplied"; "crudAuthoringDocumentSaved": "crudAuthoringDocumentSaved"; }, never, never, true, never>;
313
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PraxisCrudComponent, "praxis-crud", never, { "metadata": { "alias": "metadata"; "required": true; }; "crudId": { "alias": "crudId"; "required": true; }; "componentInstanceId": { "alias": "componentInstanceId"; "required": false; }; "context": { "alias": "context"; "required": false; }; "enableCustomization": { "alias": "enableCustomization"; "required": false; }; "authoringCapability": { "alias": "authoringCapability"; "required": false; }; }, { "configureRequested": "configureRequested"; "afterOpen": "afterOpen"; "afterClose": "afterClose"; "afterSave": "afterSave"; "afterDelete": "afterDelete"; "error": "error"; "rowClick": "rowClick"; "selectionChange": "selectionChange"; "tableRuntimeConfigChange": "tableRuntimeConfigChange"; "crudAuthoringDocumentApplied": "crudAuthoringDocumentApplied"; "crudAuthoringDocumentSaved": "crudAuthoringDocumentSaved"; }, never, never, true, never>;
304
314
  }
305
315
 
306
316
  type CrudDrawerResultType = 'save' | 'delete' | 'close';
@@ -642,6 +652,7 @@ interface PraxisCrudWidgetEditorInputs {
642
652
  componentInstanceId?: string;
643
653
  context?: Record<string, unknown>;
644
654
  enableCustomization?: boolean;
655
+ authoringCapability?: string | null;
645
656
  [key: string]: unknown;
646
657
  }
647
658
  interface PraxisCrudWidgetEditorValue {