@praxisui/crud 9.0.5-rc.9 → 9.0.6

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,12 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, InjectionToken, inject, input, signal, computed, effect, ChangeDetectionStrategy, Component, EventEmitter, DestroyRef, ChangeDetectorRef, Injector, ViewChild, Output, Input, Optional, Inject, ViewEncapsulation, ENVIRONMENT_INITIALIZER } from '@angular/core';
2
+ import { Injectable, InjectionToken, inject, input, signal, computed, effect, ChangeDetectionStrategy, Component, EventEmitter, output, DestroyRef, ChangeDetectorRef, Injector, ViewChild, Output, Input, Optional, Inject, ViewEncapsulation, ENVIRONMENT_INITIALIZER } from '@angular/core';
3
3
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
4
  import { HttpClient } from '@angular/common/http';
5
5
  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, LoggerService, SurfaceNavigationError, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, EnterpriseRuntimeContextService, GLOBAL_SURFACE_SERVICE, SurfaceOutletRegistryService, ComponentKeyService, ResourceDiscoveryService, ResourceActionOpenAdapterService, ResourceSurfaceOpenAdapterService, normalizeSurfaceOperationContext, translateUnavailableWorkflowMessage, isSurfaceNavigationError, translateSurfaceNavigationRejected, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, SURFACE_DRAWER_REF, SURFACE_DRAWER_CONTENT_DATA, PraxisIconButtonComponent, PraxisResourceIdentityComponent, GenericCrudService, ComponentMetadataRegistry } from '@praxisui/core';
9
+ import { ASYNC_CONFIG_STORAGE, GlobalConfigService, CrudOperationResolutionService, LoggerService, SurfaceNavigationError, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, EnterpriseRuntimeContextService, GLOBAL_SURFACE_SERVICE, SurfaceOutletRegistryService, ComponentKeyService, ResourceDiscoveryService, ResourceActionOpenAdapterService, ResourceSurfaceOpenAdapterService, normalizeSurfaceOperationContext, translateUnavailableWorkflowMessage, isSurfaceNavigationError, translateSurfaceNavigationRejected, normalizeUnknownError, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, SURFACE_DRAWER_REF, SURFACE_DRAWER_CONTENT_DATA, PraxisIconButtonComponent, PraxisResourceIdentityComponent, 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';
@@ -193,6 +193,12 @@ class CrudLauncherService {
193
193
  presentation: mode === 'drawer' ? 'drawer' : 'modal',
194
194
  resourceIdentity,
195
195
  contextIdentity,
196
+ showResourceIdentityKey: runtime?.showResourceIdentityKey !== false,
197
+ reactiveDeterminationCallbacks: {
198
+ onEvent: drawerCallbacks?.onReactiveDetermination,
199
+ onPendingChange: drawerCallbacks?.onReactiveDeterminationPendingChange,
200
+ onStabilityChange: drawerCallbacks?.onReactiveDeterminationStabilityChange,
201
+ },
196
202
  };
197
203
  const modalCfg = { ...(merged.metadata.defaults?.modal || {}) };
198
204
  const restoreFocus = resolveCrudRestoreFocus(modalCfg.restoreFocus, actionFocusOrigin);
@@ -240,9 +246,13 @@ class CrudLauncherService {
240
246
  inputs,
241
247
  resourceIdentity,
242
248
  contextIdentity,
249
+ showResourceIdentityKey: runtime?.showResourceIdentityKey !== false,
243
250
  restoreFocus,
244
251
  onClose: drawerCallbacks?.onClose,
245
252
  onResult: drawerCallbacks?.onResult,
253
+ onReactiveDetermination: drawerCallbacks?.onReactiveDetermination,
254
+ onReactiveDeterminationPendingChange: drawerCallbacks?.onReactiveDeterminationPendingChange,
255
+ onReactiveDeterminationStabilityChange: drawerCallbacks?.onReactiveDeterminationStabilityChange,
246
256
  }));
247
257
  return { mode };
248
258
  }
@@ -760,6 +770,9 @@ function validateCrudAuthoringDocument(doc, context) {
760
770
  if (!trimString(metadata.resource?.path) && !Array.isArray(metadata.data)) {
761
771
  diagnostics.push(warnDiagnostic('crud.metadata.resource-or-data.missing', 'Provide metadata.resource.path or metadata.data so the CRUD can resolve its source', 'metadata.resource.path'));
762
772
  }
773
+ if (trimString(metadata.resource?.path) && Array.isArray(metadata.data)) {
774
+ diagnostics.push(warnDiagnostic('crud.metadata.data.ignored-by-resource', 'metadata.data is ignored because metadata.resource.path activates remote table mode', 'metadata.data'));
775
+ }
763
776
  if (context?.requireCanonicalActions) {
764
777
  for (const actionName of CANONICAL_ACTIONS) {
765
778
  if (!findCrudAction(metadata.actions, actionName)) {
@@ -1121,6 +1134,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1121
1134
  'crud.authoring.section.connection': 'Conexão',
1122
1135
  'crud.authoring.section.actions': 'Ações',
1123
1136
  'crud.authoring.section.defaults': 'Abertura e cabeçalho',
1137
+ 'crud.authoring.tab.defaults': 'Abertura',
1124
1138
  'crud.authoring.section.table': 'Tabela',
1125
1139
  'crud.authoring.section.json': 'JSON',
1126
1140
  'crud.authoring.overview.connection': 'Conexão',
@@ -1131,6 +1145,9 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1131
1145
  'crud.authoring.overview.connectionIdField': 'ID: {value}',
1132
1146
  'crud.authoring.overview.connectionEndpoint': 'API: {value}',
1133
1147
  'crud.authoring.overview.connectionDetailsPending': 'Identificador e endpoint ainda são opcionais aqui.',
1148
+ 'crud.authoring.overview.sourceRemote': 'Fonte remota',
1149
+ 'crud.authoring.overview.sourceRemoteLocalIgnored': 'Fonte remota · {count} registros locais ignorados',
1150
+ 'crud.authoring.overview.sourceLocal': 'Fonte local · {count} registros',
1134
1151
  'crud.authoring.overview.backVisible': 'Voltar visível',
1135
1152
  'crud.authoring.overview.backHidden': 'Voltar oculto',
1136
1153
  'crud.authoring.overview.modalDensity': 'Modal: {value}',
@@ -1181,6 +1198,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1181
1198
  'crud.authoring.diagnostics.warnings': 'Alertas',
1182
1199
  'crud.authoring.diagnostics.errorSummary': '{count} diagnósticos bloqueantes nesta seção.',
1183
1200
  'crud.authoring.diagnostics.warningSummary': '{count} diagnósticos que valem revisão nesta seção.',
1201
+ 'crud.authoring.diagnostics.warningSummarySingle': '1 diagnóstico que vale revisão nesta seção.',
1184
1202
  'crud.authoring.connection.resourcePath': 'Recurso',
1185
1203
  'crud.authoring.connection.resourcePath.placeholder': 'ex.: api/human-resources/cargos',
1186
1204
  'crud.authoring.connection.idField': 'Campo identificador',
@@ -1304,6 +1322,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1304
1322
  'crud.authoring.action.create': 'Criar',
1305
1323
  'crud.authoring.action.view': 'Visualizar',
1306
1324
  'crud.authoring.action.edit': 'Editar',
1325
+ 'crud.authoring.action.delete': 'Excluir',
1307
1326
  'crud.authoring.table.summary': 'A configuração detalhada da tabela continua sendo tratada pela superfície canônica de TableConfig. Nesta fase, o editor de CRUD preserva o snapshot atual da tabela sem duplicar o editor completo.',
1308
1327
  'crud.authoring.json.hint': 'O JSON abaixo representa o documento canônico de authoring do CRUD.',
1309
1328
  'crud.authoring.table.structureSummary': '{count} colunas | {density}',
@@ -1323,6 +1342,13 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1323
1342
  'crud.authoring.json.summaryWarnings': 'O documento canônico possui diagnósticos que valem revisão.',
1324
1343
  'crud.authoring.json.summaryClean': 'O documento canônico está estruturalmente consistente.',
1325
1344
  'crud.authoring.validation.routeRequired': 'Informe uma rota quando o modo efetivo for route.',
1345
+ 'crud.authoring.validation.componentInvalid': 'O componente deve ser praxis-crud.',
1346
+ 'crud.authoring.validation.tableRequired': 'Informe a configuração canônica da tabela.',
1347
+ 'crud.authoring.validation.resourcePathRequired': 'Informe o caminho do recurso configurado.',
1348
+ 'crud.authoring.validation.sourceMissing': 'Informe um recurso remoto ou dados locais para o CRUD.',
1349
+ 'crud.authoring.validation.dataIgnoredByResource': 'Os registros locais são ignorados porque o recurso remoto está ativo.',
1350
+ 'crud.authoring.validation.openModeInvalid': 'Use route, modal ou drawer como modo de abertura.',
1351
+ 'crud.authoring.validation.canonicalActionMissing': 'A ação CRUD canônica “{action}” ainda não foi declarada.',
1326
1352
  'crud.authoring.validation.formIdRequired': 'Informe um formId quando o modo efetivo for modal ou drawer.',
1327
1353
  'crud.authoring.validation.submitContract': 'submitUrl e submitMethod devem ser informados juntos.',
1328
1354
  'crud.authoring.overview.actionsNextCompact': 'Próxima: {action}. {summary}',
@@ -1341,6 +1367,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1341
1367
  'crud.authoring.section.connection': 'Connection',
1342
1368
  'crud.authoring.section.actions': 'Actions',
1343
1369
  'crud.authoring.section.defaults': 'Open mode and header',
1370
+ 'crud.authoring.tab.defaults': 'Opening',
1344
1371
  'crud.authoring.section.table': 'Table',
1345
1372
  'crud.authoring.section.json': 'JSON',
1346
1373
  'crud.authoring.overview.connection': 'Connection',
@@ -1351,6 +1378,9 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1351
1378
  'crud.authoring.overview.connectionIdField': 'ID: {value}',
1352
1379
  'crud.authoring.overview.connectionEndpoint': 'API: {value}',
1353
1380
  'crud.authoring.overview.connectionDetailsPending': 'Identifier and endpoint details are still optional here.',
1381
+ 'crud.authoring.overview.sourceRemote': 'Remote source',
1382
+ 'crud.authoring.overview.sourceRemoteLocalIgnored': 'Remote source · {count} local rows ignored',
1383
+ 'crud.authoring.overview.sourceLocal': 'Local source · {count} rows',
1354
1384
  'crud.authoring.overview.backVisible': 'Back visible',
1355
1385
  'crud.authoring.overview.backHidden': 'Back hidden',
1356
1386
  'crud.authoring.overview.modalDensity': 'Modal: {value}',
@@ -1401,6 +1431,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1401
1431
  'crud.authoring.diagnostics.warnings': 'Warnings',
1402
1432
  'crud.authoring.diagnostics.errorSummary': '{count} blocking diagnostics in this section.',
1403
1433
  'crud.authoring.diagnostics.warningSummary': '{count} diagnostics worth reviewing in this section.',
1434
+ 'crud.authoring.diagnostics.warningSummarySingle': '1 diagnostic worth reviewing in this section.',
1404
1435
  'crud.authoring.connection.resourcePath': 'Resource',
1405
1436
  'crud.authoring.connection.resourcePath.placeholder': 'e.g. api/human-resources/cargos',
1406
1437
  'crud.authoring.connection.idField': 'Identifier field',
@@ -1524,6 +1555,7 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1524
1555
  'crud.authoring.action.create': 'Create',
1525
1556
  'crud.authoring.action.view': 'View',
1526
1557
  'crud.authoring.action.edit': 'Edit',
1558
+ 'crud.authoring.action.delete': 'Delete',
1527
1559
  'crud.authoring.table.summary': 'Detailed table configuration remains owned by the canonical TableConfig surface. In this phase, the CRUD editor preserves the current table snapshot without duplicating the full table editor.',
1528
1560
  'crud.authoring.table.structureSummary': '{count} columns | {density}',
1529
1561
  'crud.authoring.table.paginationSummary': 'Page size {value}',
@@ -1543,6 +1575,13 @@ const PRAXIS_CRUD_AUTHORING_I18N_CONFIG = {
1543
1575
  'crud.authoring.json.summaryWarnings': 'The canonical document has diagnostics worth reviewing.',
1544
1576
  'crud.authoring.json.summaryClean': 'The canonical document is structurally consistent.',
1545
1577
  'crud.authoring.validation.routeRequired': 'Provide a route when the effective mode is route.',
1578
+ 'crud.authoring.validation.componentInvalid': 'The component must be praxis-crud.',
1579
+ 'crud.authoring.validation.tableRequired': 'Provide the canonical table configuration.',
1580
+ 'crud.authoring.validation.resourcePathRequired': 'Provide the configured resource path.',
1581
+ 'crud.authoring.validation.sourceMissing': 'Provide a remote resource or local data for the CRUD.',
1582
+ 'crud.authoring.validation.dataIgnoredByResource': 'Local rows are ignored because the remote resource is active.',
1583
+ 'crud.authoring.validation.openModeInvalid': 'Use route, modal, or drawer as the open mode.',
1584
+ 'crud.authoring.validation.canonicalActionMissing': 'Canonical CRUD action “{action}” is not declared yet.',
1546
1585
  'crud.authoring.validation.formIdRequired': 'Provide a formId when the effective mode is modal or drawer.',
1547
1586
  'crud.authoring.validation.submitContract': 'submitUrl and submitMethod must be provided together.',
1548
1587
  'crud.authoring.overview.actionsNextCompact': 'Next: {action}. {summary}',
@@ -1701,6 +1740,10 @@ class CrudMetadataEditorComponent {
1701
1740
  actionLabel(actionName) {
1702
1741
  return this.tx(`crud.authoring.action.${actionName}`, actionName);
1703
1742
  }
1743
+ actionUsesRoute(actionName) {
1744
+ const action = this.actionValue(actionName);
1745
+ return (action.openMode || this.defaultsOpenMode()) === 'route';
1746
+ }
1704
1747
  actionSummary(actionName) {
1705
1748
  const action = this.actionValue(actionName);
1706
1749
  const mode = (action.openMode || this.defaultsOpenMode());
@@ -1799,7 +1842,8 @@ class CrudMetadataEditorComponent {
1799
1842
  const apiStatus = this.actionGroupStatus(actionName, 'api');
1800
1843
  const inputsSummary = this.actionInputsSummaryBadge(actionName);
1801
1844
  if (submitStatus === 'ready' && apiStatus === 'ready') {
1802
- return this.tx('crud.authoring.action.advancedReady', 'Advanced ready');
1845
+ const summary = this.tx('crud.authoring.action.advancedReady', 'Advanced ready');
1846
+ return inputsSummary ? `${summary} · ${inputsSummary}` : summary;
1803
1847
  }
1804
1848
  const pending = [];
1805
1849
  if (submitStatus !== 'ready') {
@@ -1907,20 +1951,16 @@ class CrudMetadataEditorComponent {
1907
1951
  return hasBinding ? 'ready' : 'pending';
1908
1952
  }
1909
1953
  if (group === 'schema') {
1910
- return String(action.form?.schemaUrl || '').trim() ? 'ready' : 'pending';
1954
+ return 'ready';
1911
1955
  }
1912
1956
  if (group === 'submit') {
1913
1957
  const hasSubmitError = actionDiagnostics.some((issue) => issue.code === 'crud.metadata.actions.form.submit-contract.invalid');
1914
1958
  if (hasSubmitError) {
1915
1959
  return 'invalid';
1916
1960
  }
1917
- const hasSubmitPair = !!String(action.form?.submitUrl || '').trim()
1918
- && !!String(action.form?.submitMethod || '').trim();
1919
- return hasSubmitPair ? 'ready' : 'pending';
1961
+ return 'ready';
1920
1962
  }
1921
- const hasApiEndpoint = !!String(action.form?.apiEndpointKey || '').trim();
1922
- const hasApiEntry = !!String(this.actionApiUrlEntryDraft(actionName) || '').trim();
1923
- return hasApiEndpoint || hasApiEntry ? 'ready' : 'pending';
1963
+ return 'ready';
1924
1964
  }
1925
1965
  actionGroupStatusLabel(actionName, group) {
1926
1966
  return {
@@ -1980,13 +2020,7 @@ class CrudMetadataEditorComponent {
1980
2020
  const hasBinding = effectiveMode === 'route'
1981
2021
  ? !!String(action.route || '').trim()
1982
2022
  : !!String(action.formId || '').trim();
1983
- const hasSubmitPair = !!String(action.form?.submitUrl || '').trim()
1984
- && !!String(action.form?.submitMethod || '').trim();
1985
- const hasSchema = !!String(action.form?.schemaUrl || '').trim();
1986
- if (hasBinding && (hasSubmitPair || hasSchema)) {
1987
- return 'ready';
1988
- }
1989
- return 'pending';
2023
+ return hasBinding ? 'ready' : 'pending';
1990
2024
  }
1991
2025
  actionStatusLabel(actionName) {
1992
2026
  return {
@@ -2015,6 +2049,16 @@ class CrudMetadataEditorComponent {
2015
2049
  if (endpointKey) {
2016
2050
  parts.push(this.tx('crud.authoring.overview.connectionEndpoint', 'API: {value}').replace('{value}', endpointKey));
2017
2051
  }
2052
+ const localRows = this.currentDocument().metadata.data;
2053
+ if (this.resourcePath().trim()) {
2054
+ parts.push(Array.isArray(localRows)
2055
+ ? this.tx('crud.authoring.overview.sourceRemoteLocalIgnored', 'Fonte remota · {count} registros locais ignorados').replace('{count}', String(localRows.length))
2056
+ : this.tx('crud.authoring.overview.sourceRemote', 'Fonte remota'));
2057
+ }
2058
+ else if (Array.isArray(localRows)) {
2059
+ parts.push(this.tx('crud.authoring.overview.sourceLocal', 'Fonte local · {count} registros')
2060
+ .replace('{count}', String(localRows.length)));
2061
+ }
2018
2062
  if (!parts.length) {
2019
2063
  return this.tx('crud.authoring.overview.connectionDetailsPending', 'Identifier and endpoint details are still optional here.');
2020
2064
  }
@@ -2210,9 +2254,13 @@ class CrudMetadataEditorComponent {
2210
2254
  return this.tx('crud.authoring.json.summaryClean', 'The canonical document is structurally consistent.');
2211
2255
  }
2212
2256
  sectionStatus(section) {
2213
- if (this.sectionDiagnostics(section).some((issue) => issue.level === 'error')) {
2257
+ const diagnostics = this.sectionDiagnostics(section);
2258
+ if (diagnostics.some((issue) => issue.level === 'error')) {
2214
2259
  return 'invalid';
2215
2260
  }
2261
+ if (diagnostics.some((issue) => issue.level === 'warning')) {
2262
+ return 'pending';
2263
+ }
2216
2264
  if (section === 'connection') {
2217
2265
  return this.resourcePath().trim() ? 'ready' : 'pending';
2218
2266
  }
@@ -2347,7 +2395,12 @@ class CrudMetadataEditorComponent {
2347
2395
  return this.tx('crud.authoring.diagnostics.errorSummary', '{count} blocking diagnostics in this section.')
2348
2396
  .replace('{count}', String(errorCount));
2349
2397
  }
2350
- return this.tx('crud.authoring.diagnostics.warningSummary', '{count} diagnostics worth reviewing in this section.')
2398
+ const key = warningCount === 1
2399
+ ? 'crud.authoring.diagnostics.warningSummarySingle'
2400
+ : 'crud.authoring.diagnostics.warningSummary';
2401
+ return this.tx(key, warningCount === 1
2402
+ ? '1 diagnostic worth reviewing in this section.'
2403
+ : '{count} diagnostics worth reviewing in this section.')
2351
2404
  .replace('{count}', String(warningCount));
2352
2405
  }
2353
2406
  modeLabel(mode) {
@@ -2380,11 +2433,25 @@ class CrudMetadataEditorComponent {
2380
2433
  }
2381
2434
  diagnosticMessage(code, fallback) {
2382
2435
  const key = {
2436
+ 'crud.metadata.component.invalid': 'crud.authoring.validation.componentInvalid',
2437
+ 'crud.metadata.table.required': 'crud.authoring.validation.tableRequired',
2438
+ 'crud.metadata.resource.path.required': 'crud.authoring.validation.resourcePathRequired',
2439
+ 'crud.metadata.resource-or-data.missing': 'crud.authoring.validation.sourceMissing',
2440
+ 'crud.metadata.data.ignored-by-resource': 'crud.authoring.validation.dataIgnoredByResource',
2441
+ 'crud.metadata.actions.openMode.invalid': 'crud.authoring.validation.openModeInvalid',
2383
2442
  'crud.metadata.actions.route.required': 'crud.authoring.validation.routeRequired',
2384
2443
  'crud.metadata.actions.formId.required': 'crud.authoring.validation.formIdRequired',
2385
2444
  'crud.metadata.actions.form.submit-contract.invalid': 'crud.authoring.validation.submitContract',
2386
2445
  }[code];
2387
- return key ? this.tx(key, fallback) : fallback;
2446
+ if (key) {
2447
+ return this.tx(key, fallback);
2448
+ }
2449
+ const canonicalActionMatch = code.match(/^crud\.metadata\.actions\.(create|view|edit|delete)\.missing$/);
2450
+ if (canonicalActionMatch) {
2451
+ return this.tx('crud.authoring.validation.canonicalActionMissing', 'Canonical CRUD action {action} is not declared yet')
2452
+ .replace('{action}', this.actionLabel(canonicalActionMatch[1]));
2453
+ }
2454
+ return fallback;
2388
2455
  }
2389
2456
  setResourceField(field, value) {
2390
2457
  this.updateMetadata((metadata) => ({
@@ -2615,13 +2682,13 @@ class CrudMetadataEditorComponent {
2615
2682
  .some((section) => this.sectionStatus(section) === bucket);
2616
2683
  }
2617
2684
  sectionDiagnostics(section) {
2618
- const prefix = {
2619
- connection: 'metadata.resource',
2620
- defaults: 'metadata.defaults',
2621
- actions: 'metadata.actions',
2622
- table: 'metadata.table',
2685
+ const prefixes = {
2686
+ connection: ['metadata.component', 'metadata.resource', 'metadata.data'],
2687
+ defaults: ['metadata.defaults'],
2688
+ actions: ['metadata.actions'],
2689
+ table: ['metadata.table'],
2623
2690
  }[section];
2624
- return this.diagnostics().filter((issue) => String(issue.path || '').includes(prefix));
2691
+ return this.diagnostics().filter((issue) => prefixes.some((prefix) => String(issue.path || '').includes(prefix)));
2625
2692
  }
2626
2693
  actionDiagnostics(actionName) {
2627
2694
  return this.diagnostics()
@@ -2727,7 +2794,7 @@ class CrudMetadataEditorComponent {
2727
2794
  </div>
2728
2795
  </section>
2729
2796
 
2730
- <mat-tab-group class="editor-tabs" data-testid="crud-editor-tabs" dynamicHeight>
2797
+ <mat-tab-group class="editor-tabs" data-testid="crud-editor-tabs" dynamicHeight animationDuration="0ms">
2731
2798
  <mat-tab>
2732
2799
  <ng-template mat-tab-label>
2733
2800
  <mat-icon [praxisIcon]="'link'"></mat-icon>
@@ -2771,7 +2838,7 @@ class CrudMetadataEditorComponent {
2771
2838
  <mat-tab>
2772
2839
  <ng-template mat-tab-label>
2773
2840
  <mat-icon [praxisIcon]="'tune'"></mat-icon>
2774
- <span data-testid="crud-editor-tab-defaults">{{ tx('crud.authoring.section.defaults', 'Open mode and header') }}</span>
2841
+ <span data-testid="crud-editor-tab-defaults">{{ tx('crud.authoring.tab.defaults', 'Opening') }}</span>
2775
2842
  <span
2776
2843
  class="tab-status-dot"
2777
2844
  [class.tab-status-dot--warn]="sectionStatus('defaults') === 'pending'"
@@ -2956,16 +3023,20 @@ class CrudMetadataEditorComponent {
2956
3023
  }
2957
3024
  </mat-select>
2958
3025
  </mat-form-field>
2959
- <mat-form-field appearance="outline">
2960
- <mat-label>{{ tx('crud.authoring.action.route', 'Route') }}</mat-label>
2961
- <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-route'" [ngModel]="actionValue(actionName).route || ''" (ngModelChange)="setActionField(actionName, 'route', $event)" [disabled]="isReadonly()" />
2962
- </mat-form-field>
2963
- <mat-form-field appearance="outline">
2964
- <mat-label>{{ tx('crud.authoring.action.formId', 'formId') }}</mat-label>
2965
- <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-formId'" [ngModel]="actionValue(actionName).formId || ''" (ngModelChange)="setActionField(actionName, 'formId', $event)" [disabled]="isReadonly()" />
2966
- </mat-form-field>
3026
+ @if (actionUsesRoute(actionName)) {
3027
+ <mat-form-field appearance="outline">
3028
+ <mat-label>{{ tx('crud.authoring.action.route', 'Route') }}</mat-label>
3029
+ <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-route'" [ngModel]="actionValue(actionName).route || ''" (ngModelChange)="setActionField(actionName, 'route', $event)" [disabled]="isReadonly()" />
3030
+ </mat-form-field>
3031
+ } @else {
3032
+ <mat-form-field appearance="outline">
3033
+ <mat-label>{{ tx('crud.authoring.action.formId', 'formId') }}</mat-label>
3034
+ <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-formId'" [ngModel]="actionValue(actionName).formId || ''" (ngModelChange)="setActionField(actionName, 'formId', $event)" [disabled]="isReadonly()" />
3035
+ </mat-form-field>
3036
+ }
2967
3037
  </div>
2968
3038
  </section>
3039
+ @if (!actionUsesRoute(actionName)) {
2969
3040
  <section class="action-group" [attr.data-testid]="'crud-editor-action-' + actionName + '-schema-group'">
2970
3041
  <div class="action-group__header">
2971
3042
  <div>
@@ -3118,6 +3189,7 @@ class CrudMetadataEditorComponent {
3118
3189
  </div>
3119
3190
  </section>
3120
3191
  </mat-expansion-panel>
3192
+ }
3121
3193
  </mat-expansion-panel>
3122
3194
  }
3123
3195
  </div>
@@ -3206,7 +3278,7 @@ class CrudMetadataEditorComponent {
3206
3278
  </mat-tab>
3207
3279
  </mat-tab-group>
3208
3280
  </section>
3209
- `, isInline: true, styles: [":host{display:block;min-width:0;color:var(--md-sys-color-on-surface,#1a1b20)}.editor-shell{display:grid;gap:16px}.editor-header{display:flex;justify-content:space-between;gap:16px;align-items:start}.editor-overview,.editor-health-map{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.editor-health-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-health-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-health-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus{display:flex;gap:12px;align-items:start;padding:14px 16px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-focus--success{background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.editor-focus-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent);white-space:nowrap}.editor-focus-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-focus-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus-copy{display:grid;gap:4px}.editor-focus-copy p{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-tabs{display:flex;flex-direction:column;min-width:0}.editor-tabs .mat-mdc-tab{min-width:118px}.editor-tabs ::ng-deep .mdc-tab__content{gap:8px}.editor-tabs ::ng-deep .mat-mdc-tab-body-wrapper,.editor-tabs ::ng-deep .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.editor-tabs ::ng-deep .mat-mdc-tab-body-content{overflow:visible}.tab-status-dot{width:8px;height:8px;border-radius:999px;background:var(--md-sys-color-primary,#1263b4);flex:0 0 auto}.tab-status-dot--warn{background:var(--md-sys-color-tertiary,#7d5700)}.tab-status-dot--error{background:var(--md-sys-color-error,#b3261e)}.tab-content{display:grid;gap:14px;min-width:0;padding:12px 2px 4px;box-sizing:border-box}.editor-overview-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-overview-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-overview-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-overview-label{font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-overview-value{line-height:1.3;overflow-wrap:anywhere}.editor-overview-note{margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-header h2,.editor-card h3{margin:0}.editor-header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-chip{padding:6px 10px;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary,#1263b4) 12%,transparent);font-size:12px}.editor-card{display:grid;gap:14px;padding:16px;border-radius:20px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 70%,transparent)}.editor-section-header{display:flex;justify-content:space-between;gap:12px;align-items:start}.editor-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid--compact{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.editor-toggles{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-actions,.action-grid{display:grid;gap:14px}.action-summary-strip{display:flex;flex-wrap:wrap;gap:8px}.action-summary-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.action-summary-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.action-summary-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.action-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 88%,transparent)}.action-group--advanced{background:color-mix(in srgb,var(--md-sys-color-surface-container,#f5f7fb) 90%,transparent)}.action-group__header{display:grid;gap:4px}.action-subgroup{display:grid;gap:10px;padding:10px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low,#fafbfd) 92%,transparent)}.action-subgroup__header{display:grid;gap:4px}.action-param-list{display:grid;gap:12px}.action-param-row{display:grid;gap:8px}.action-advanced-panel{border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 65%,transparent);border-radius:16px}.editor-section-note{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.diagnostics-groups{display:grid;gap:12px}.diagnostics-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.diagnostics{display:grid;gap:8px;margin:0;padding-left:18px}.diagnostics .error{color:var(--md-sys-color-error,#b3261e)}pre{margin:0;overflow:auto;max-height:320px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i4.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i4.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i8.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i9.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i10.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i10.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i10.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisTableInlineAuthoringEditorComponent, selector: "praxis-table-inline-authoring-editor", inputs: ["config", "readonly"], outputs: ["configChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3281
+ `, isInline: true, styles: [":host{display:block;min-width:0;color:var(--md-sys-color-on-surface,#1a1b20)}.editor-shell{display:grid;gap:16px}.editor-header{display:flex;justify-content:space-between;gap:16px;align-items:start}.editor-overview,.editor-health-map{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.editor-health-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-health-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-health-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus{display:flex;gap:12px;align-items:start;padding:14px 16px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-focus--success{background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.editor-focus-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent);white-space:nowrap}.editor-focus-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-focus-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus-copy{display:grid;gap:4px}.editor-focus-copy p{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-tabs{display:flex;flex-direction:column;min-width:0}.editor-tabs .mat-mdc-tab{min-width:118px}.editor-tabs ::ng-deep .mdc-tab__content{gap:8px}.editor-tabs ::ng-deep .mat-mdc-tab-body-wrapper,.editor-tabs ::ng-deep .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.editor-tabs ::ng-deep .mat-mdc-tab-body-content{overflow:hidden}.tab-status-dot{width:8px;height:8px;border-radius:999px;background:var(--md-sys-color-primary,#1263b4);flex:0 0 auto}.tab-status-dot--warn{background:var(--md-sys-color-tertiary,#7d5700)}.tab-status-dot--error{background:var(--md-sys-color-error,#b3261e)}.tab-content{display:grid;gap:14px;min-width:0;padding:12px 2px 4px;box-sizing:border-box}.editor-overview-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-overview-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-overview-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-overview-label{font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-overview-value{line-height:1.3;overflow-wrap:anywhere}.editor-overview-note{margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-header h2,.editor-card h3{margin:0}.editor-header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-chip{padding:6px 10px;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary,#1263b4) 12%,transparent);font-size:12px}.editor-card{display:grid;gap:14px;padding:16px;border-radius:20px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 70%,transparent)}.editor-section-header{display:flex;justify-content:space-between;gap:12px;align-items:start}.editor-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid--compact{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.editor-toggles{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-actions,.action-grid{display:grid;gap:14px}.action-summary-strip{display:flex;flex-wrap:wrap;gap:8px}.action-summary-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.action-summary-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.action-summary-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.action-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 88%,transparent)}.action-group--advanced{background:color-mix(in srgb,var(--md-sys-color-surface-container,#f5f7fb) 90%,transparent)}.action-group__header{display:grid;gap:4px}.action-subgroup{display:grid;gap:10px;padding:10px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low,#fafbfd) 92%,transparent)}.action-subgroup__header{display:grid;gap:4px}.action-param-list{display:grid;gap:12px}.action-param-row{display:grid;gap:8px}.action-advanced-panel{border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 65%,transparent);border-radius:16px}.editor-section-note{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.diagnostics-groups{display:grid;gap:12px}.diagnostics-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.diagnostics{display:grid;gap:8px;margin:0;padding-left:18px}.diagnostics .error{color:var(--md-sys-color-error,#b3261e)}pre{margin:0;overflow:auto;max-height:320px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i3.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i4.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i4.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i8.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i9.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i10.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i10.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i10.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisTableInlineAuthoringEditorComponent, selector: "praxis-table-inline-authoring-editor", inputs: ["config", "readonly"], outputs: ["configChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3210
3282
  }
3211
3283
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CrudMetadataEditorComponent, decorators: [{
3212
3284
  type: Component,
@@ -3291,7 +3363,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3291
3363
  </div>
3292
3364
  </section>
3293
3365
 
3294
- <mat-tab-group class="editor-tabs" data-testid="crud-editor-tabs" dynamicHeight>
3366
+ <mat-tab-group class="editor-tabs" data-testid="crud-editor-tabs" dynamicHeight animationDuration="0ms">
3295
3367
  <mat-tab>
3296
3368
  <ng-template mat-tab-label>
3297
3369
  <mat-icon [praxisIcon]="'link'"></mat-icon>
@@ -3335,7 +3407,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3335
3407
  <mat-tab>
3336
3408
  <ng-template mat-tab-label>
3337
3409
  <mat-icon [praxisIcon]="'tune'"></mat-icon>
3338
- <span data-testid="crud-editor-tab-defaults">{{ tx('crud.authoring.section.defaults', 'Open mode and header') }}</span>
3410
+ <span data-testid="crud-editor-tab-defaults">{{ tx('crud.authoring.tab.defaults', 'Opening') }}</span>
3339
3411
  <span
3340
3412
  class="tab-status-dot"
3341
3413
  [class.tab-status-dot--warn]="sectionStatus('defaults') === 'pending'"
@@ -3520,16 +3592,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3520
3592
  }
3521
3593
  </mat-select>
3522
3594
  </mat-form-field>
3523
- <mat-form-field appearance="outline">
3524
- <mat-label>{{ tx('crud.authoring.action.route', 'Route') }}</mat-label>
3525
- <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-route'" [ngModel]="actionValue(actionName).route || ''" (ngModelChange)="setActionField(actionName, 'route', $event)" [disabled]="isReadonly()" />
3526
- </mat-form-field>
3527
- <mat-form-field appearance="outline">
3528
- <mat-label>{{ tx('crud.authoring.action.formId', 'formId') }}</mat-label>
3529
- <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-formId'" [ngModel]="actionValue(actionName).formId || ''" (ngModelChange)="setActionField(actionName, 'formId', $event)" [disabled]="isReadonly()" />
3530
- </mat-form-field>
3595
+ @if (actionUsesRoute(actionName)) {
3596
+ <mat-form-field appearance="outline">
3597
+ <mat-label>{{ tx('crud.authoring.action.route', 'Route') }}</mat-label>
3598
+ <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-route'" [ngModel]="actionValue(actionName).route || ''" (ngModelChange)="setActionField(actionName, 'route', $event)" [disabled]="isReadonly()" />
3599
+ </mat-form-field>
3600
+ } @else {
3601
+ <mat-form-field appearance="outline">
3602
+ <mat-label>{{ tx('crud.authoring.action.formId', 'formId') }}</mat-label>
3603
+ <input matInput [attr.data-testid]="'crud-editor-action-' + actionName + '-formId'" [ngModel]="actionValue(actionName).formId || ''" (ngModelChange)="setActionField(actionName, 'formId', $event)" [disabled]="isReadonly()" />
3604
+ </mat-form-field>
3605
+ }
3531
3606
  </div>
3532
3607
  </section>
3608
+ @if (!actionUsesRoute(actionName)) {
3533
3609
  <section class="action-group" [attr.data-testid]="'crud-editor-action-' + actionName + '-schema-group'">
3534
3610
  <div class="action-group__header">
3535
3611
  <div>
@@ -3682,6 +3758,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3682
3758
  </div>
3683
3759
  </section>
3684
3760
  </mat-expansion-panel>
3761
+ }
3685
3762
  </mat-expansion-panel>
3686
3763
  }
3687
3764
  </div>
@@ -3770,7 +3847,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
3770
3847
  </mat-tab>
3771
3848
  </mat-tab-group>
3772
3849
  </section>
3773
- `, styles: [":host{display:block;min-width:0;color:var(--md-sys-color-on-surface,#1a1b20)}.editor-shell{display:grid;gap:16px}.editor-header{display:flex;justify-content:space-between;gap:16px;align-items:start}.editor-overview,.editor-health-map{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.editor-health-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-health-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-health-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus{display:flex;gap:12px;align-items:start;padding:14px 16px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-focus--success{background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.editor-focus-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent);white-space:nowrap}.editor-focus-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-focus-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus-copy{display:grid;gap:4px}.editor-focus-copy p{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-tabs{display:flex;flex-direction:column;min-width:0}.editor-tabs .mat-mdc-tab{min-width:118px}.editor-tabs ::ng-deep .mdc-tab__content{gap:8px}.editor-tabs ::ng-deep .mat-mdc-tab-body-wrapper,.editor-tabs ::ng-deep .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.editor-tabs ::ng-deep .mat-mdc-tab-body-content{overflow:visible}.tab-status-dot{width:8px;height:8px;border-radius:999px;background:var(--md-sys-color-primary,#1263b4);flex:0 0 auto}.tab-status-dot--warn{background:var(--md-sys-color-tertiary,#7d5700)}.tab-status-dot--error{background:var(--md-sys-color-error,#b3261e)}.tab-content{display:grid;gap:14px;min-width:0;padding:12px 2px 4px;box-sizing:border-box}.editor-overview-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-overview-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-overview-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-overview-label{font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-overview-value{line-height:1.3;overflow-wrap:anywhere}.editor-overview-note{margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-header h2,.editor-card h3{margin:0}.editor-header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-chip{padding:6px 10px;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary,#1263b4) 12%,transparent);font-size:12px}.editor-card{display:grid;gap:14px;padding:16px;border-radius:20px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 70%,transparent)}.editor-section-header{display:flex;justify-content:space-between;gap:12px;align-items:start}.editor-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid--compact{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.editor-toggles{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-actions,.action-grid{display:grid;gap:14px}.action-summary-strip{display:flex;flex-wrap:wrap;gap:8px}.action-summary-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.action-summary-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.action-summary-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.action-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 88%,transparent)}.action-group--advanced{background:color-mix(in srgb,var(--md-sys-color-surface-container,#f5f7fb) 90%,transparent)}.action-group__header{display:grid;gap:4px}.action-subgroup{display:grid;gap:10px;padding:10px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low,#fafbfd) 92%,transparent)}.action-subgroup__header{display:grid;gap:4px}.action-param-list{display:grid;gap:12px}.action-param-row{display:grid;gap:8px}.action-advanced-panel{border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 65%,transparent);border-radius:16px}.editor-section-note{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.diagnostics-groups{display:grid;gap:12px}.diagnostics-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.diagnostics{display:grid;gap:8px;margin:0;padding-left:18px}.diagnostics .error{color:var(--md-sys-color-error,#b3261e)}pre{margin:0;overflow:auto;max-height:320px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}\n"] }]
3850
+ `, styles: [":host{display:block;min-width:0;color:var(--md-sys-color-on-surface,#1a1b20)}.editor-shell{display:grid;gap:16px}.editor-header{display:flex;justify-content:space-between;gap:16px;align-items:start}.editor-overview,.editor-health-map{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.editor-health-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-health-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-health-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus{display:flex;gap:12px;align-items:start;padding:14px 16px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-focus--success{background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.editor-focus-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent);white-space:nowrap}.editor-focus-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-focus-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-focus-copy{display:grid;gap:4px}.editor-focus-copy p{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-tabs{display:flex;flex-direction:column;min-width:0}.editor-tabs .mat-mdc-tab{min-width:118px}.editor-tabs ::ng-deep .mdc-tab__content{gap:8px}.editor-tabs ::ng-deep .mat-mdc-tab-body-wrapper,.editor-tabs ::ng-deep .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.editor-tabs ::ng-deep .mat-mdc-tab-body-content{overflow:hidden}.tab-status-dot{width:8px;height:8px;border-radius:999px;background:var(--md-sys-color-primary,#1263b4);flex:0 0 auto}.tab-status-dot--warn{background:var(--md-sys-color-tertiary,#7d5700)}.tab-status-dot--error{background:var(--md-sys-color-error,#b3261e)}.tab-content{display:grid;gap:14px;min-width:0;padding:12px 2px 4px;box-sizing:border-box}.editor-overview-card{display:grid;gap:4px;padding:14px;border-radius:18px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.editor-overview-card--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.editor-overview-card--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.editor-overview-label{font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-overview-value{line-height:1.3;overflow-wrap:anywhere}.editor-overview-note{margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-header h2,.editor-card h3{margin:0}.editor-header p{margin:6px 0 0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.editor-chip{padding:6px 10px;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary,#1263b4) 12%,transparent);font-size:12px}.editor-card{display:grid;gap:14px;padding:16px;border-radius:20px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 70%,transparent)}.editor-section-header{display:flex;justify-content:space-between;gap:12px;align-items:start}.editor-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-grid--compact{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.editor-toggles{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.editor-actions,.action-grid{display:grid;gap:14px}.action-summary-strip{display:flex;flex-wrap:wrap;gap:8px}.action-summary-chip{padding:6px 10px;border-radius:999px;font-size:12px;background:color-mix(in srgb,var(--md-sys-color-secondary-container,#d7e3ff) 70%,transparent)}.action-summary-chip--warn{background:color-mix(in srgb,var(--md-sys-color-tertiary-container,#f8e08e) 78%,transparent)}.action-summary-chip--error{background:color-mix(in srgb,var(--md-sys-color-error-container,#f9dedc) 82%,transparent);color:var(--md-sys-color-on-error-container,#410e0b)}.action-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 88%,transparent)}.action-group--advanced{background:color-mix(in srgb,var(--md-sys-color-surface-container,#f5f7fb) 90%,transparent)}.action-group__header{display:grid;gap:4px}.action-subgroup{display:grid;gap:10px;padding:10px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low,#fafbfd) 92%,transparent)}.action-subgroup__header{display:grid;gap:4px}.action-param-list{display:grid;gap:12px}.action-param-row{display:grid;gap:8px}.action-advanced-panel{border:1px solid color-mix(in srgb,var(--md-sys-color-outline,#c5c7ce) 65%,transparent);border-radius:16px}.editor-section-note{margin:0;color:var(--md-sys-color-on-surface-variant,#5a5d67)}.diagnostics-groups{display:grid;gap:12px}.diagnostics-group{display:grid;gap:10px;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}.diagnostics{display:grid;gap:8px;margin:0;padding-left:18px}.diagnostics .error{color:var(--md-sys-color-error,#b3261e)}pre{margin:0;overflow:auto;max-height:320px;padding:12px;border-radius:14px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high,#eef1f6) 92%,transparent)}\n"] }]
3774
3851
  }], ctorParameters: () => [], propDecorators: { documentInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "document", required: false }] }], metadataInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "metadata", required: false }] }], crudIdInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "crudId", required: false }] }], readonlyInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }] } });
3775
3852
  function cloneTableConfig(config) {
3776
3853
  return JSON.parse(JSON.stringify(config || { columns: [] }));
@@ -3801,6 +3878,8 @@ class PraxisCrudComponent {
3801
3878
  tableConfigPersistenceStrategy = 'local-first';
3802
3879
  context;
3803
3880
  enableCustomization = false;
3881
+ /** Controls whether existing-record hosts expose the canonical identity key. */
3882
+ showResourceIdentityKey = true;
3804
3883
  /** Capability publica exigida para authoring governado do CRUD e da tabela interna. */
3805
3884
  authoringCapability = null;
3806
3885
  configureRequested = new EventEmitter();
@@ -3808,11 +3887,17 @@ class PraxisCrudComponent {
3808
3887
  afterClose = new EventEmitter();
3809
3888
  afterSave = new EventEmitter();
3810
3889
  afterDelete = new EventEmitter();
3890
+ /** Metadata-only Reactive Determination evidence from an open form host. */
3891
+ reactiveDeterminationExecuted = new EventEmitter();
3892
+ reactiveDeterminationPendingChange = new EventEmitter();
3893
+ reactiveDeterminationStabilityChange = new EventEmitter();
3811
3894
  error = new EventEmitter();
3812
3895
  rowClick = new EventEmitter();
3813
3896
  /** Emits the canonical table row-action envelope before the CRUD handles its surface/action. */
3814
3897
  rowAction = new EventEmitter();
3815
3898
  selectionChange = new EventEmitter();
3899
+ /** Forwards the canonical resource interaction envelope emitted by the internal table. */
3900
+ resourceEvent = output();
3816
3901
  tableRuntimeConfigChange = new EventEmitter();
3817
3902
  crudAuthoringDocumentApplied = new EventEmitter();
3818
3903
  crudAuthoringDocumentSaved = new EventEmitter();
@@ -3834,11 +3919,13 @@ class PraxisCrudComponent {
3834
3919
  snack = inject(MatSnackBar);
3835
3920
  dialog = inject(DialogService);
3836
3921
  i18n = inject(PraxisI18nService);
3922
+ logger = inject(LoggerService, { optional: true });
3837
3923
  injector = inject(Injector);
3838
3924
  resourceDiscoveryInstance;
3839
3925
  actionOpenAdapterInstance;
3840
3926
  surfaceOpenAdapterInstance;
3841
3927
  materializedRefreshSequence = 0;
3928
+ surfaceOpenAttemptSequence = 0;
3842
3929
  global = (() => {
3843
3930
  try {
3844
3931
  return inject(GlobalConfigService);
@@ -3860,6 +3947,7 @@ class PraxisCrudComponent {
3860
3947
  })();
3861
3948
  warnedMissingId = false;
3862
3949
  lastEmittedTableRuntimeConfigJson = '';
3950
+ lastTableRuntimeConfigSnapshot = null;
3863
3951
  lastAssignedTableConfigJson = '';
3864
3952
  lastAssignedTableCrudContextJson = '';
3865
3953
  lastAppliedResourceIdentity = null;
@@ -3922,6 +4010,9 @@ class PraxisCrudComponent {
3922
4010
  this.selectedRow = this.extractSelectedRowFromEvent(event);
3923
4011
  this.selectionChange.emit(event);
3924
4012
  }
4013
+ onTableResourceEvent(event) {
4014
+ this.resourceEvent.emit(event);
4015
+ }
3925
4016
  async onBulkAction(event) {
3926
4017
  const action = String(event?.action || '').trim();
3927
4018
  if (!action) {
@@ -3947,6 +4038,8 @@ class PraxisCrudComponent {
3947
4038
  return;
3948
4039
  }
3949
4040
  this.materializedRefreshSequence += 1;
4041
+ this.lastTableRuntimeConfigSnapshot = null;
4042
+ this.lastEmittedTableRuntimeConfigJson = '';
3950
4043
  try {
3951
4044
  const parsed = typeof this.metadata === 'string'
3952
4045
  ? JSON.parse(this.metadata)
@@ -4051,6 +4144,18 @@ class PraxisCrudComponent {
4051
4144
  this.refreshTable();
4052
4145
  }
4053
4146
  },
4147
+ onReactiveDetermination: (event) => this.reactiveDeterminationExecuted.emit({
4148
+ action: effectiveAction.action,
4149
+ event,
4150
+ }),
4151
+ onReactiveDeterminationPendingChange: (pending) => this.reactiveDeterminationPendingChange.emit({
4152
+ action: effectiveAction.action,
4153
+ pending,
4154
+ }),
4155
+ onReactiveDeterminationStabilityChange: (stability) => this.reactiveDeterminationStabilityChange.emit({
4156
+ action: effectiveAction.action,
4157
+ ...stability,
4158
+ }),
4054
4159
  }, {
4055
4160
  capabilities: effectiveAction.action === 'create'
4056
4161
  ? this.collectionCapabilities
@@ -4058,6 +4163,7 @@ class PraxisCrudComponent {
4058
4163
  links: this.resolveCrudRuntimeLinks(effectiveAction.action, contextualRow),
4059
4164
  resourceIdentity,
4060
4165
  contextIdentity,
4166
+ showResourceIdentityKey: this.showResourceIdentityKey,
4061
4167
  focusOrigin: runtimeEvent?.focusOrigin ?? null,
4062
4168
  surfaceRuntime: this.resolveSurfaceRuntime(),
4063
4169
  });
@@ -4141,6 +4247,10 @@ class PraxisCrudComponent {
4141
4247
  return current;
4142
4248
  }
4143
4249
  }
4250
+ onTableConfigChange(config) {
4251
+ this.lastTableRuntimeConfigSnapshot = this.cloneTableConfigSnapshot(config);
4252
+ this.emitTableRuntimeConfigSnapshot(this.lastTableRuntimeConfigSnapshot);
4253
+ }
4144
4254
  onTableMetadataChange() {
4145
4255
  this.emitTableRuntimeConfigSnapshot();
4146
4256
  }
@@ -4324,9 +4434,16 @@ class PraxisCrudComponent {
4324
4434
  const { mode, ref } = await this.launcher.launch(createAction, undefined, this.resolvedMetadata, this.componentKeyId(), {
4325
4435
  onClose: () => emitDrawerClose(),
4326
4436
  onResult: (result) => handleResult(result),
4437
+ onReactiveDetermination: (event) => this.reactiveDeterminationExecuted.emit({ action: normalizedAction, event }),
4438
+ onReactiveDeterminationPendingChange: (pending) => this.reactiveDeterminationPendingChange.emit({ action: normalizedAction, pending }),
4439
+ onReactiveDeterminationStabilityChange: (stability) => this.reactiveDeterminationStabilityChange.emit({
4440
+ action: normalizedAction,
4441
+ ...stability,
4442
+ }),
4327
4443
  }, {
4328
4444
  capabilities: this.collectionCapabilities,
4329
4445
  links: this.tableCollectionLinks,
4446
+ showResourceIdentityKey: this.showResourceIdentityKey,
4330
4447
  surfaceRuntime: this.resolveSurfaceRuntime(),
4331
4448
  });
4332
4449
  this.afterOpen.emit({ mode, action: normalizedAction });
@@ -4421,10 +4538,11 @@ class PraxisCrudComponent {
4421
4538
  }
4422
4539
  return null;
4423
4540
  }
4424
- emitTableRuntimeConfigSnapshot() {
4425
- const snapshot = this.getCurrentTableConfigSnapshot();
4541
+ emitTableRuntimeConfigSnapshot(preferredSnapshot) {
4542
+ const snapshot = preferredSnapshot || this.getCurrentTableConfigSnapshot();
4426
4543
  if (!snapshot)
4427
4544
  return;
4545
+ this.lastTableRuntimeConfigSnapshot = this.cloneTableConfigSnapshot(snapshot);
4428
4546
  try {
4429
4547
  const nextJson = JSON.stringify(snapshot);
4430
4548
  if (nextJson === this.lastEmittedTableRuntimeConfigJson)
@@ -4434,6 +4552,14 @@ class PraxisCrudComponent {
4434
4552
  catch { }
4435
4553
  this.tableRuntimeConfigChange.emit(snapshot);
4436
4554
  }
4555
+ cloneTableConfigSnapshot(config) {
4556
+ try {
4557
+ return JSON.parse(JSON.stringify(config));
4558
+ }
4559
+ catch {
4560
+ return config;
4561
+ }
4562
+ }
4437
4563
  applyResolvedCrudState(meta) {
4438
4564
  this.effectiveTableConfig = this.buildEffectiveTableConfig(meta, this.collectionCapabilities);
4439
4565
  const nextTableConfig = this.effectiveTableConfig ||
@@ -4582,6 +4708,12 @@ class PraxisCrudComponent {
4582
4708
  return true;
4583
4709
  }
4584
4710
  const focusOrigin = this.resolveRuntimeFocusOrigin(runtimeEvent);
4711
+ const correlationId = [
4712
+ this.componentKeyId() || this.crudId || 'praxis-crud',
4713
+ 'surface.open',
4714
+ surface.id,
4715
+ ++this.surfaceOpenAttemptSequence,
4716
+ ].join(':');
4585
4717
  let openPromise;
4586
4718
  try {
4587
4719
  const surfaceContext = {
@@ -4592,6 +4724,7 @@ class PraxisCrudComponent {
4592
4724
  resourcePath,
4593
4725
  surfaceId: surface.id,
4594
4726
  operationId: surface.operationId,
4727
+ correlationId,
4595
4728
  },
4596
4729
  runtime: {
4597
4730
  row: row ?? null,
@@ -4607,11 +4740,8 @@ class PraxisCrudComponent {
4607
4740
  openPromise = handledInline ? Promise.resolve(undefined) : Promise.resolve(this.surfaceService.open(payload, surfaceContext));
4608
4741
  }
4609
4742
  catch (error) {
4610
- if (this.notifySurfaceNavigationFailure(error)) {
4611
- this.error.emit(error);
4612
- return true;
4613
- }
4614
- return false;
4743
+ this.reportDiscoveredSurfaceOpenFailure(error, normalizedAction, surface, resourcePath, correlationId);
4744
+ return true;
4615
4745
  }
4616
4746
  try {
4617
4747
  this.bindDiscoveredSurfaceLifecycle(await openPromise);
@@ -4621,8 +4751,7 @@ class PraxisCrudComponent {
4621
4751
  });
4622
4752
  }
4623
4753
  catch (err) {
4624
- this.error.emit(err);
4625
- this.notifySurfaceNavigationFailure(err);
4754
+ this.reportDiscoveredSurfaceOpenFailure(err, normalizedAction, surface, resourcePath, correlationId);
4626
4755
  }
4627
4756
  return true;
4628
4757
  }
@@ -4742,6 +4871,31 @@ class PraxisCrudComponent {
4742
4871
  this.snack.open(translateSurfaceNavigationRejected(this.i18n), undefined, { duration: 4500 });
4743
4872
  return true;
4744
4873
  }
4874
+ reportDiscoveredSurfaceOpenFailure(error, action, surface, resourcePath, correlationId) {
4875
+ const normalizedCause = normalizeUnknownError(error);
4876
+ this.error.emit(error);
4877
+ this.notifySurfaceNavigationFailure(error);
4878
+ this.logger?.warn('Discovered CRUD surface failed to open.', {
4879
+ context: {
4880
+ lib: '@praxisui/crud',
4881
+ component: 'PraxisCrudComponent',
4882
+ actionId: action,
4883
+ correlationId,
4884
+ },
4885
+ data: {
4886
+ surfaceId: surface.id,
4887
+ mode: 'drawer',
4888
+ resourcePath,
4889
+ selectedPath: 'surface.open',
4890
+ fallback: 'none',
4891
+ cause: {
4892
+ name: normalizedCause.name,
4893
+ message: normalizedCause.message,
4894
+ },
4895
+ },
4896
+ throttleKey: `crud:surface.open:${action}:${surface.id}`,
4897
+ });
4898
+ }
4745
4899
  async resolveDiscoveredActionCatalog(row) {
4746
4900
  try {
4747
4901
  if (row) {
@@ -5139,8 +5293,18 @@ class PraxisCrudComponent {
5139
5293
  if (!this.isCustomizationAvailable()) {
5140
5294
  return;
5141
5295
  }
5142
- const seed = this.currentAuthoringDocument
5296
+ const canonicalSeed = this.currentAuthoringDocument
5143
5297
  || createCrudAuthoringDocument({ metadata: this.resolvedMetadata });
5298
+ const liveTableConfig = this.lastTableRuntimeConfigSnapshot
5299
+ || this.getCurrentTableConfigSnapshot();
5300
+ const seed = liveTableConfig
5301
+ ? createCrudAuthoringDocument({
5302
+ metadata: {
5303
+ ...canonicalSeed.metadata,
5304
+ table: this.cloneTableConfigSnapshot(liveTableConfig),
5305
+ },
5306
+ })
5307
+ : canonicalSeed;
5144
5308
  const ref = openCrudMetadataEditor(this.settingsPanel, {
5145
5309
  crudId: this.crudId,
5146
5310
  document: seed,
@@ -5178,6 +5342,9 @@ class PraxisCrudComponent {
5178
5342
  const next = parseLegacyOrCrudDocument(payload);
5179
5343
  this.currentAuthoringDocument = next;
5180
5344
  this.resolvedMetadata = next.metadata;
5345
+ this.lastTableRuntimeConfigSnapshot = next.metadata.table
5346
+ ? this.cloneTableConfigSnapshot(next.metadata.table)
5347
+ : null;
5181
5348
  this.tableQueryContext = this.resolveQueryContext(this.resolvedMetadata);
5182
5349
  this.tableFilterCriteria = this.resolveFilterCriteria(this.resolvedMetadata);
5183
5350
  this.applyResolvedCrudState(this.resolvedMetadata);
@@ -5467,7 +5634,7 @@ class PraxisCrudComponent {
5467
5634
  return Object.entries(params).reduce((current, [name, value]) => current.split(`{${name}}`).join(value), text);
5468
5635
  }
5469
5636
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisCrudComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5470
- 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", tableConfigPersistenceStrategy: "tableConfigPersistenceStrategy", context: "context", enableCustomization: "enableCustomization", authoringCapability: "authoringCapability" }, outputs: { configureRequested: "configureRequested", afterOpen: "afterOpen", afterClose: "afterClose", afterSave: "afterSave", afterDelete: "afterDelete", error: "error", rowClick: "rowClick", rowAction: "rowAction", selectionChange: "selectionChange", tableRuntimeConfigChange: "tableRuntimeConfigChange", crudAuthoringDocumentApplied: "crudAuthoringDocumentApplied", crudAuthoringDocumentSaved: "crudAuthoringDocumentSaved" }, providers: [
5637
+ 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", tableConfigPersistenceStrategy: "tableConfigPersistenceStrategy", context: "context", enableCustomization: "enableCustomization", showResourceIdentityKey: "showResourceIdentityKey", authoringCapability: "authoringCapability" }, outputs: { configureRequested: "configureRequested", afterOpen: "afterOpen", afterClose: "afterClose", afterSave: "afterSave", afterDelete: "afterDelete", reactiveDeterminationExecuted: "reactiveDeterminationExecuted", reactiveDeterminationPendingChange: "reactiveDeterminationPendingChange", reactiveDeterminationStabilityChange: "reactiveDeterminationStabilityChange", error: "error", rowClick: "rowClick", rowAction: "rowAction", selectionChange: "selectionChange", resourceEvent: "resourceEvent", tableRuntimeConfigChange: "tableRuntimeConfigChange", crudAuthoringDocumentApplied: "crudAuthoringDocumentApplied", crudAuthoringDocumentSaved: "crudAuthoringDocumentSaved" }, providers: [
5471
5638
  providePraxisI18nConfig(RESOURCE_DISCOVERY_I18N_CONFIG),
5472
5639
  providePraxisI18nConfig(PRAXIS_CRUD_RUNTIME_I18N_CONFIG),
5473
5640
  ], viewQueries: [{ propertyName: "table", first: true, predicate: PraxisTable, descendants: true }], usesOnChanges: true, ngImport: i0, template: `
@@ -5492,11 +5659,13 @@ class PraxisCrudComponent {
5492
5659
  [authoringCapability]="authoringCapability"
5493
5660
  (rowClick)="onTableRowClick($event)"
5494
5661
  (selectionChange)="onTableSelectionChange($event)"
5662
+ (resourceEvent)="onTableResourceEvent($event)"
5495
5663
  (rowAction)="onTableRowAction($event)"
5496
5664
  (toolbarAction)="onToolbarAction($event)"
5497
5665
  (bulkAction)="onBulkAction($event)"
5498
5666
  (collectionLinksChange)="onCollectionLinksChange($event)"
5499
5667
  (reset)="onResetPreferences()"
5668
+ (configChange)="onTableConfigChange($event)"
5500
5669
  (metadataChange)="onTableMetadataChange()"
5501
5670
  (loadingStateChange)="onTableLoadingStateChange($event)"
5502
5671
  ></praxis-table>
@@ -5540,11 +5709,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5540
5709
  [authoringCapability]="authoringCapability"
5541
5710
  (rowClick)="onTableRowClick($event)"
5542
5711
  (selectionChange)="onTableSelectionChange($event)"
5712
+ (resourceEvent)="onTableResourceEvent($event)"
5543
5713
  (rowAction)="onTableRowAction($event)"
5544
5714
  (toolbarAction)="onToolbarAction($event)"
5545
5715
  (bulkAction)="onBulkAction($event)"
5546
5716
  (collectionLinksChange)="onCollectionLinksChange($event)"
5547
5717
  (reset)="onResetPreferences()"
5718
+ (configChange)="onTableConfigChange($event)"
5548
5719
  (metadataChange)="onTableMetadataChange()"
5549
5720
  (loadingStateChange)="onTableLoadingStateChange($event)"
5550
5721
  ></praxis-table>
@@ -5574,6 +5745,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5574
5745
  type: Input
5575
5746
  }], enableCustomization: [{
5576
5747
  type: Input
5748
+ }], showResourceIdentityKey: [{
5749
+ type: Input
5577
5750
  }], authoringCapability: [{
5578
5751
  type: Input
5579
5752
  }], configureRequested: [{
@@ -5586,6 +5759,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5586
5759
  type: Output
5587
5760
  }], afterDelete: [{
5588
5761
  type: Output
5762
+ }], reactiveDeterminationExecuted: [{
5763
+ type: Output
5764
+ }], reactiveDeterminationPendingChange: [{
5765
+ type: Output
5766
+ }], reactiveDeterminationStabilityChange: [{
5767
+ type: Output
5589
5768
  }], error: [{
5590
5769
  type: Output
5591
5770
  }], rowClick: [{
@@ -5594,7 +5773,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5594
5773
  type: Output
5595
5774
  }], selectionChange: [{
5596
5775
  type: Output
5597
- }], tableRuntimeConfigChange: [{
5776
+ }], resourceEvent: [{ type: i0.Output, args: ["resourceEvent"] }], tableRuntimeConfigChange: [{
5598
5777
  type: Output
5599
5778
  }], crudAuthoringDocumentApplied: [{
5600
5779
  type: Output
@@ -5633,6 +5812,9 @@ class DynamicFormDialogHostComponent {
5633
5812
  configStorage;
5634
5813
  surfaceRef;
5635
5814
  formComp;
5815
+ reactiveDeterminationExecuted = new EventEmitter();
5816
+ reactiveDeterminationPendingChange = new EventEmitter();
5817
+ reactiveDeterminationStabilityChange = new EventEmitter();
5636
5818
  modal = {};
5637
5819
  presentation = 'modal';
5638
5820
  maximized = false;
@@ -5644,11 +5826,13 @@ class DynamicFormDialogHostComponent {
5644
5826
  terminalClose = false;
5645
5827
  destroyRef = inject(DestroyRef);
5646
5828
  i18n = inject(PraxisI18nService);
5829
+ reactiveDeterminationStabilitySignature = '';
5647
5830
  resourcePath;
5648
5831
  resourceId;
5649
5832
  initialValue;
5650
5833
  resourceIdentity = null;
5651
5834
  contextIdentity = null;
5835
+ showResourceIdentityKey = true;
5652
5836
  schemaUrl;
5653
5837
  submitUrl;
5654
5838
  submitMethod;
@@ -5737,6 +5921,7 @@ class DynamicFormDialogHostComponent {
5737
5921
  this.resourceIdentity = this.mode !== 'create' && this.data.resourceIdentity
5738
5922
  ? this.data.resourceIdentity
5739
5923
  : null;
5924
+ this.showResourceIdentityKey = this.data.showResourceIdentityKey !== false;
5740
5925
  this.contextIdentity = this.data.contextIdentity
5741
5926
  ? this.data.contextIdentity
5742
5927
  : null;
@@ -5967,6 +6152,9 @@ class DynamicFormDialogHostComponent {
5967
6152
  if (stage === 'before' || stage === 'error') {
5968
6153
  return;
5969
6154
  }
6155
+ if (this.formComp?.hasPendingReactiveDeterminations?.()
6156
+ || this.formComp?.hasUnsatisfiedReactiveDeterminations?.())
6157
+ return;
5970
6158
  if (this.modal.closeOnSave === false) {
5971
6159
  this.saveState();
5972
6160
  return;
@@ -5982,6 +6170,47 @@ class DynamicFormDialogHostComponent {
5982
6170
  }
5983
6171
  }
5984
6172
  onCancel() {
6173
+ if (this.formComp?.hasPendingReactiveDeterminations?.()
6174
+ || this.formComp?.hasUnsatisfiedReactiveDeterminations?.()) {
6175
+ void this.formComp.waitForReactiveDeterminations().then((stable) => {
6176
+ if (stable)
6177
+ this.continueCancel();
6178
+ });
6179
+ return;
6180
+ }
6181
+ this.continueCancel();
6182
+ }
6183
+ onReactiveDeterminationExecuted(event) {
6184
+ this.reactiveDeterminationExecuted.emit(event);
6185
+ this.data.reactiveDeterminationCallbacks?.onEvent?.(event);
6186
+ this.emitReactiveDeterminationStability();
6187
+ }
6188
+ onReactiveDeterminationPendingChange(pending) {
6189
+ this.reactiveDeterminationPendingChange.emit(pending);
6190
+ this.data.reactiveDeterminationCallbacks?.onPendingChange?.(pending);
6191
+ this.emitReactiveDeterminationStability();
6192
+ }
6193
+ emitReactiveDeterminationStability() {
6194
+ const pending = this.formComp?.hasPendingReactiveDeterminations?.() === true;
6195
+ const unsatisfied = this.formComp?.hasUnsatisfiedReactiveDeterminations?.() === true;
6196
+ const status = pending
6197
+ ? 'pending'
6198
+ : unsatisfied
6199
+ ? 'settled-unsatisfied'
6200
+ : 'settled-satisfied';
6201
+ const signature = `${status}:${pending}:${unsatisfied}`;
6202
+ if (signature === this.reactiveDeterminationStabilitySignature)
6203
+ return;
6204
+ this.reactiveDeterminationStabilitySignature = signature;
6205
+ const stability = {
6206
+ status,
6207
+ pending,
6208
+ unsatisfied,
6209
+ };
6210
+ this.reactiveDeterminationStabilityChange.emit(stability);
6211
+ this.data.reactiveDeterminationCallbacks?.onStabilityChange?.(stability);
6212
+ }
6213
+ continueCancel() {
5985
6214
  if (this.surfaceRef) {
5986
6215
  void this.canLeaveSurfaceFrame().then((allowed) => {
5987
6216
  if (!allowed)
@@ -6018,6 +6247,12 @@ class DynamicFormDialogHostComponent {
6018
6247
  }
6019
6248
  }
6020
6249
  async canLeaveSurfaceFrame() {
6250
+ if (this.formComp?.hasPendingReactiveDeterminations?.()
6251
+ || this.formComp?.hasUnsatisfiedReactiveDeterminations?.()) {
6252
+ const stable = await this.formComp.waitForReactiveDeterminations();
6253
+ if (!stable)
6254
+ return false;
6255
+ }
6021
6256
  if (this.terminalClose || !this.formComp?.form.dirty) {
6022
6257
  return true;
6023
6258
  }
@@ -6157,7 +6392,7 @@ class DynamicFormDialogHostComponent {
6157
6392
  return { ...horizontal, ...vertical };
6158
6393
  }
6159
6394
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormDialogHostComponent, deps: [{ token: MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }, { token: DialogService }, { token: i2$1.GenericCrudService }, { token: ASYNC_CONFIG_STORAGE }, { token: SURFACE_DRAWER_REF, optional: true }, { token: SURFACE_DRAWER_CONTENT_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component });
6160
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicFormDialogHostComponent, isStandalone: true, selector: "praxis-dynamic-form-dialog-host", host: { properties: { "attr.data-density": "modal.density || \"default\"", "attr.data-presentation": "presentation", "class.praxis-drawer": "presentation === \"drawer\"", "class.praxis-surface-frame": "isSurfaceFrame" }, classAttribute: "praxis-dialog" }, providers: [
6395
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicFormDialogHostComponent, isStandalone: true, selector: "praxis-dynamic-form-dialog-host", outputs: { reactiveDeterminationExecuted: "reactiveDeterminationExecuted", reactiveDeterminationPendingChange: "reactiveDeterminationPendingChange", reactiveDeterminationStabilityChange: "reactiveDeterminationStabilityChange" }, host: { properties: { "attr.data-density": "modal.density || \"default\"", "attr.data-presentation": "presentation", "class.praxis-drawer": "presentation === \"drawer\"", "class.praxis-surface-frame": "isSurfaceFrame" }, classAttribute: "praxis-dialog" }, providers: [
6161
6396
  GenericCrudService,
6162
6397
  providePraxisI18nConfig(PRAXIS_CRUD_RUNTIME_I18N_CONFIG),
6163
6398
  ], viewQueries: [{ propertyName: "formComp", first: true, predicate: PraxisDynamicForm, descendants: true }], ngImport: i0, template: `
@@ -6229,7 +6464,8 @@ class DynamicFormDialogHostComponent {
6229
6464
  <praxis-resource-identity
6230
6465
  [identity]="resourceIdentity"
6231
6466
  density="compact"
6232
- [showKeyLabel]="true"
6467
+ [showKey]="showResourceIdentityKey"
6468
+ [showKeyLabel]="showResourceIdentityKey"
6233
6469
  [ariaLabel]="resourceIdentityLabel"
6234
6470
  ></praxis-resource-identity>
6235
6471
  </div>
@@ -6256,9 +6492,11 @@ class DynamicFormDialogHostComponent {
6256
6492
  [actions]="formActions"
6257
6493
  (formSubmit)="onSave($event)"
6258
6494
  (formCancel)="onCancel()"
6495
+ (reactiveDeterminationExecuted)="onReactiveDeterminationExecuted($event)"
6496
+ (reactiveDeterminationPendingChange)="onReactiveDeterminationPendingChange($event)"
6259
6497
  ></praxis-dynamic-form>
6260
6498
  </mat-dialog-content>
6261
- `, isInline: true, styles: ["praxis-dynamic-form-dialog-host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}praxis-dynamic-form-dialog-host[data-density=compact]{--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}praxis-dynamic-form-dialog-host .dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);min-height:var(--dlg-header-h);padding:10px var(--dlg-pad);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)}praxis-dynamic-form-dialog-host .dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-heading{display:grid;min-width:0;gap:2px}praxis-dynamic-form-dialog-host .dialog-title-icon{display:inline-grid;flex:0 0 auto;width:36px;height:36px;place-items:center;border-radius:10px;color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}praxis-dynamic-form-dialog-host .dialog-title-icon mat-icon{width:22px;height:22px;font-size:22px}praxis-dynamic-form-dialog-host .dialog-subtitle{display:-webkit-box;overflow:hidden;color:var(--md-sys-color-on-surface-variant);font-size:.82rem;font-weight:400;line-height:1.35;-webkit-box-orient:vertical;-webkit-line-clamp:2}praxis-dynamic-form-dialog-host .spacer{flex:1}praxis-dynamic-form-dialog-host .dialog-content{flex:1 1 auto;overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}praxis-dynamic-form-dialog-host .crud-operation-context{position:sticky;top:0;z-index:1;display:grid;gap:.75rem;margin-block-end:var(--dlg-pad);padding-block:.5rem .75rem;background:var(--pfx-form-surface, var(--md-sys-color-surface));border-bottom:1px solid var(--md-sys-color-outline-variant)}praxis-dynamic-form-dialog-host .crud-resource-identity{display:grid;gap:.25rem;min-width:0}praxis-dynamic-form-dialog-host .crud-resource-identity__label{color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-label-medium, 500 .75rem/1rem inherit)}praxis-dynamic-form-dialog-host.praxis-drawer{--dlg-header-h: 72px;width:100%;min-width:0;max-width:100vw;height:100dvh;background:var(--pfx-form-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-header{background:color-mix(in srgb,var(--md-sys-color-surface-container-high, var(--md-sys-color-surface)),transparent 4%)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content{display:flex;flex-direction:column;max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form{display:block;flex:1 1 auto;min-height:0}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form>.praxis-dynamic-form{min-height:100%}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form>.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections]{margin-top:auto;padding-top:var(--pfx-actions-gap-top, var(--pfx-section-gap, 20px))}praxis-dynamic-form-dialog-host.praxis-surface-frame .dialog-content{max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button{color:var(--md-sys-color-on-surface-variant)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button:hover{color:var(--md-sys-color-primary)}praxis-dynamic-form-dialog-host .dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}.pfx-blur-backdrop{background-color:var(--pfx-backdrop, rgba(15, 23, 42, .42))!important;backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%)}.pfx-transparent-backdrop{background-color:transparent!important}.cdk-overlay-pane.pfx-dialog-pane{overflow:hidden;transition:width .2s ease,height .2s ease,margin .2s ease}.cdk-overlay-pane.pfx-dialog-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane .mdc-dialog__surface{display:flex;flex-direction:column;width:100%;min-width:0;height:100%;max-height:inherit;overflow:hidden;background:var(--pfx-form-surface, var(--md-sys-color-surface))!important;border:1px solid var(--pfx-form-stroke, var(--md-sys-color-outline-variant))!important}.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mdc-dialog__surface{backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cdk-overlay-pane.pfx-drawer-pane{margin:0!important;border-radius:0!important;box-shadow:var(--md-sys-elevation-level3, 0 24px 80px rgba(15, 23, 42, .28)),0 0 0 1px color-mix(in srgb,var(--md-sys-color-outline, #64748b) 24%,transparent)}.cdk-overlay-pane.pfx-drawer-pane.pfx-drawer-maximized{margin:var(--pfx-drawer-edge-gap, 8px)!important}.cdk-overlay-pane.pfx-drawer-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-drawer-pane .mdc-dialog__surface{min-width:0!important;border-radius:0!important}\n"], dependencies: [{ 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: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisResourceIdentityComponent, selector: "praxis-resource-identity", inputs: ["identity", "emptyTitle", "density", "showMetadataLabels", "showKeyLabel", "ariaLabel"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "initialValue", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "readUrl", "submitUrl", "submitMethod", "submitIdempotencyKey", "submitCorrelationId", "submitResourceVersion", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "showAiAssistant", "formId", "componentInstanceId", "configPersistenceStrategy", "layout", "generatedLayoutPreset", "layoutPolicy", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "fieldIconPolicy", "domainRules", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "configPatchChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "ruleDiagnosticsChange"] }], encapsulation: i0.ViewEncapsulation.None });
6499
+ `, isInline: true, styles: ["praxis-dynamic-form-dialog-host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}praxis-dynamic-form-dialog-host[data-density=compact]{--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}praxis-dynamic-form-dialog-host .dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);min-height:var(--dlg-header-h);padding:10px var(--dlg-pad);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)}praxis-dynamic-form-dialog-host .dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-heading{display:grid;min-width:0;gap:2px}praxis-dynamic-form-dialog-host .dialog-title-icon{display:inline-grid;flex:0 0 auto;width:36px;height:36px;place-items:center;border-radius:10px;color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}praxis-dynamic-form-dialog-host .dialog-title-icon mat-icon{width:22px;height:22px;font-size:22px}praxis-dynamic-form-dialog-host .dialog-subtitle{display:-webkit-box;overflow:hidden;color:var(--md-sys-color-on-surface-variant);font-size:.82rem;font-weight:400;line-height:1.35;-webkit-box-orient:vertical;-webkit-line-clamp:2}praxis-dynamic-form-dialog-host .spacer{flex:1}praxis-dynamic-form-dialog-host .dialog-content{flex:1 1 auto;overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}praxis-dynamic-form-dialog-host .crud-operation-context{position:sticky;top:0;z-index:1;display:grid;gap:.75rem;margin-block-end:var(--dlg-pad);padding-block:.5rem .75rem;background:var(--pfx-form-surface, var(--md-sys-color-surface));border-bottom:1px solid var(--md-sys-color-outline-variant)}praxis-dynamic-form-dialog-host .crud-resource-identity{display:grid;gap:.25rem;min-width:0}praxis-dynamic-form-dialog-host .crud-resource-identity__label{color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-label-medium, 500 .75rem/1rem inherit)}praxis-dynamic-form-dialog-host.praxis-drawer{--dlg-header-h: 72px;width:100%;min-width:0;max-width:100vw;height:100dvh;background:var(--pfx-form-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-header{background:color-mix(in srgb,var(--md-sys-color-surface-container-high, var(--md-sys-color-surface)),transparent 4%)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content{display:flex;flex-direction:column;max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form{display:block;flex:1 1 auto;min-height:0}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form>.praxis-dynamic-form{min-height:100%}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form>.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections]{margin-top:auto;padding-top:var(--pfx-actions-gap-top, var(--pfx-section-gap, 20px))}praxis-dynamic-form-dialog-host.praxis-surface-frame .dialog-content{max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button{color:var(--md-sys-color-on-surface-variant)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button:hover{color:var(--md-sys-color-primary)}praxis-dynamic-form-dialog-host .dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}.pfx-blur-backdrop{background-color:var(--pfx-backdrop, rgba(15, 23, 42, .42))!important;backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%)}.pfx-transparent-backdrop{background-color:transparent!important}.cdk-overlay-pane.pfx-dialog-pane{overflow:hidden;transition:width .2s ease,height .2s ease,margin .2s ease}.cdk-overlay-pane.pfx-dialog-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane .mdc-dialog__surface{display:flex;flex-direction:column;width:100%;min-width:0;height:100%;max-height:inherit;overflow:hidden;background:var(--pfx-form-surface, var(--md-sys-color-surface))!important;border:1px solid var(--pfx-form-stroke, var(--md-sys-color-outline-variant))!important}.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mdc-dialog__surface{backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cdk-overlay-pane.pfx-drawer-pane{margin:0!important;border-radius:0!important;box-shadow:var(--md-sys-elevation-level3, 0 24px 80px rgba(15, 23, 42, .28)),0 0 0 1px color-mix(in srgb,var(--md-sys-color-outline, #64748b) 24%,transparent)}.cdk-overlay-pane.pfx-drawer-pane.pfx-drawer-maximized{margin:var(--pfx-drawer-edge-gap, 8px)!important}.cdk-overlay-pane.pfx-drawer-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-drawer-pane .mdc-dialog__surface{min-width:0!important;border-radius:0!important}\n"], dependencies: [{ 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: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisResourceIdentityComponent, selector: "praxis-resource-identity", inputs: ["identity", "emptyTitle", "density", "showMetadataLabels", "showKey", "showKeyLabel", "ariaLabel"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "initialValue", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "readUrl", "submitUrl", "submitMethod", "submitIdempotencyKey", "submitCorrelationId", "submitResourceVersion", "submitResourceVersionTargetResourceKey", "submitResourceVersionTargetIdField", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "showAiAssistant", "formId", "componentInstanceId", "configPersistenceStrategy", "layout", "generatedLayoutPreset", "layoutPolicy", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "fieldIconPolicy", "domainRules", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "configPatchChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "ruleDiagnosticsChange", "reactiveDeterminationExecuted", "reactiveDeterminationPendingChange"] }], encapsulation: i0.ViewEncapsulation.None });
6262
6500
  }
6263
6501
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormDialogHostComponent, decorators: [{
6264
6502
  type: Component,
@@ -6347,7 +6585,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
6347
6585
  <praxis-resource-identity
6348
6586
  [identity]="resourceIdentity"
6349
6587
  density="compact"
6350
- [showKeyLabel]="true"
6588
+ [showKey]="showResourceIdentityKey"
6589
+ [showKeyLabel]="showResourceIdentityKey"
6351
6590
  [ariaLabel]="resourceIdentityLabel"
6352
6591
  ></praxis-resource-identity>
6353
6592
  </div>
@@ -6374,6 +6613,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
6374
6613
  [actions]="formActions"
6375
6614
  (formSubmit)="onSave($event)"
6376
6615
  (formCancel)="onCancel()"
6616
+ (reactiveDeterminationExecuted)="onReactiveDeterminationExecuted($event)"
6617
+ (reactiveDeterminationPendingChange)="onReactiveDeterminationPendingChange($event)"
6377
6618
  ></praxis-dynamic-form>
6378
6619
  </mat-dialog-content>
6379
6620
  `, styles: ["praxis-dynamic-form-dialog-host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}praxis-dynamic-form-dialog-host[data-density=compact]{--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}praxis-dynamic-form-dialog-host .dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);min-height:var(--dlg-header-h);padding:10px var(--dlg-pad);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)}praxis-dynamic-form-dialog-host .dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-heading{display:grid;min-width:0;gap:2px}praxis-dynamic-form-dialog-host .dialog-title-icon{display:inline-grid;flex:0 0 auto;width:36px;height:36px;place-items:center;border-radius:10px;color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}praxis-dynamic-form-dialog-host .dialog-title-icon mat-icon{width:22px;height:22px;font-size:22px}praxis-dynamic-form-dialog-host .dialog-subtitle{display:-webkit-box;overflow:hidden;color:var(--md-sys-color-on-surface-variant);font-size:.82rem;font-weight:400;line-height:1.35;-webkit-box-orient:vertical;-webkit-line-clamp:2}praxis-dynamic-form-dialog-host .spacer{flex:1}praxis-dynamic-form-dialog-host .dialog-content{flex:1 1 auto;overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}praxis-dynamic-form-dialog-host .crud-operation-context{position:sticky;top:0;z-index:1;display:grid;gap:.75rem;margin-block-end:var(--dlg-pad);padding-block:.5rem .75rem;background:var(--pfx-form-surface, var(--md-sys-color-surface));border-bottom:1px solid var(--md-sys-color-outline-variant)}praxis-dynamic-form-dialog-host .crud-resource-identity{display:grid;gap:.25rem;min-width:0}praxis-dynamic-form-dialog-host .crud-resource-identity__label{color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-label-medium, 500 .75rem/1rem inherit)}praxis-dynamic-form-dialog-host.praxis-drawer{--dlg-header-h: 72px;width:100%;min-width:0;max-width:100vw;height:100dvh;background:var(--pfx-form-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-header{background:color-mix(in srgb,var(--md-sys-color-surface-container-high, var(--md-sys-color-surface)),transparent 4%)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content{display:flex;flex-direction:column;max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form{display:block;flex:1 1 auto;min-height:0}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form>.praxis-dynamic-form{min-height:100%}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form>.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections]{margin-top:auto;padding-top:var(--pfx-actions-gap-top, var(--pfx-section-gap, 20px))}praxis-dynamic-form-dialog-host.praxis-surface-frame .dialog-content{max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button{color:var(--md-sys-color-on-surface-variant)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button:hover{color:var(--md-sys-color-primary)}praxis-dynamic-form-dialog-host .dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}.pfx-blur-backdrop{background-color:var(--pfx-backdrop, rgba(15, 23, 42, .42))!important;backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%)}.pfx-transparent-backdrop{background-color:transparent!important}.cdk-overlay-pane.pfx-dialog-pane{overflow:hidden;transition:width .2s ease,height .2s ease,margin .2s ease}.cdk-overlay-pane.pfx-dialog-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane .mdc-dialog__surface{display:flex;flex-direction:column;width:100%;min-width:0;height:100%;max-height:inherit;overflow:hidden;background:var(--pfx-form-surface, var(--md-sys-color-surface))!important;border:1px solid var(--pfx-form-stroke, var(--md-sys-color-outline-variant))!important}.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mdc-dialog__surface{backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cdk-overlay-pane.pfx-drawer-pane{margin:0!important;border-radius:0!important;box-shadow:var(--md-sys-elevation-level3, 0 24px 80px rgba(15, 23, 42, .28)),0 0 0 1px color-mix(in srgb,var(--md-sys-color-outline, #64748b) 24%,transparent)}.cdk-overlay-pane.pfx-drawer-pane.pfx-drawer-maximized{margin:var(--pfx-drawer-edge-gap, 8px)!important}.cdk-overlay-pane.pfx-drawer-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-drawer-pane .mdc-dialog__surface{min-width:0!important;border-radius:0!important}\n"] }]
@@ -6403,6 +6644,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
6403
6644
  }] }], propDecorators: { formComp: [{
6404
6645
  type: ViewChild,
6405
6646
  args: [PraxisDynamicForm]
6647
+ }], reactiveDeterminationExecuted: [{
6648
+ type: Output
6649
+ }], reactiveDeterminationPendingChange: [{
6650
+ type: Output
6651
+ }], reactiveDeterminationStabilityChange: [{
6652
+ type: Output
6406
6653
  }] } });
6407
6654
  function getFormSubmitStage(result) {
6408
6655
  if (!result || typeof result !== 'object' || !('stage' in result)) {
@@ -6681,6 +6928,12 @@ const PRAXIS_CRUD_COMPONENT_METADATA = {
6681
6928
  default: false,
6682
6929
  description: 'Habilita modo de customização do layout.',
6683
6930
  },
6931
+ {
6932
+ name: 'showResourceIdentityKey',
6933
+ type: 'boolean',
6934
+ default: true,
6935
+ description: 'Controla se modal e drawer exibem a chave da identidade canônica do registro. Use false quando a chave pública for opaca ou apenas técnica; o título legível permanece visível.',
6936
+ },
6684
6937
  {
6685
6938
  name: 'authoringCapability',
6686
6939
  type: 'string | null',
@@ -6709,6 +6962,21 @@ const PRAXIS_CRUD_COMPONENT_METADATA = {
6709
6962
  type: '{ id: string | number }',
6710
6963
  description: 'Emitido ao deletar.',
6711
6964
  },
6965
+ {
6966
+ name: 'reactiveDeterminationExecuted',
6967
+ type: '{ action: string; event: ReactiveDeterminationExecutionEvent }',
6968
+ description: 'Evidencia metadata-only encaminhada pelo formulario aberto, com contexto da acao CRUD.',
6969
+ },
6970
+ {
6971
+ name: 'reactiveDeterminationPendingChange',
6972
+ type: '{ action: string; pending: boolean }',
6973
+ description: 'Estado agregado de estabilizacao do formulario aberto, sem valores sensiveis.',
6974
+ },
6975
+ {
6976
+ name: 'reactiveDeterminationStabilityChange',
6977
+ type: 'CrudReactiveDeterminationStabilityChange',
6978
+ description: 'Conclusao agregada do formulario aberto, distinguindo settled-satisfied de settled-unsatisfied e preservando o contexto da acao.',
6979
+ },
6712
6980
  {
6713
6981
  name: 'rowClick',
6714
6982
  type: '{ row: unknown; index: number }',
@@ -6724,6 +6992,11 @@ const PRAXIS_CRUD_COMPONENT_METADATA = {
6724
6992
  type: 'unknown',
6725
6993
  description: 'Encaminha a mudança de seleção da tabela interna quando o CRUD é usado como widget composto.',
6726
6994
  },
6995
+ {
6996
+ name: 'resourceEvent',
6997
+ type: 'PraxisResourceEvent<unknown>',
6998
+ description: 'Encaminha o envelope canônico da tabela interna com kind, resourcePath, resourceId, payload e contexto para composição governada.',
6999
+ },
6727
7000
  {
6728
7001
  name: 'error',
6729
7002
  type: 'unknown',
@@ -7218,10 +7491,12 @@ const PRAXIS_CRUD_AUTHORING_MANIFEST = {
7218
7491
  { name: 'tableConfigPersistenceStrategy', type: "'local-first' | 'input-first' | 'volatile'", description: 'Persistence precedence forwarded to the internal Praxis Table. Governed previews should use input-first or volatile explicitly.' },
7219
7492
  { name: 'context', type: 'Record<string, unknown>', description: 'Opaque host context used for authoring seeds and launcher inputs.' },
7220
7493
  { name: 'enableCustomization', type: 'boolean', description: 'Explicit host opt-in for CRUD authoring surfaces.' },
7494
+ { name: 'showResourceIdentityKey', type: 'boolean', description: 'Presentation-only control for the canonical key in existing-record modal and drawer headers. Defaults to true; false preserves the readable title while hiding an opaque or technical key.' },
7221
7495
  { name: 'authoringCapability', type: 'string | null', description: 'Public runtime capability required before CRUD and delegated table authoring can open.' },
7222
7496
  { name: 'afterOpen', type: '{ mode: FormOpenMode; action: string }', description: 'Emitted after a CRUD action opens.' },
7223
7497
  { name: 'afterSave', type: '{ id: string | number; data: unknown }', description: 'Emitted after save; CRUD refetches the list.' },
7224
7498
  { name: 'afterDelete', type: '{ id: string | number }', description: 'Emitted after delete; CRUD refetches the list.' },
7499
+ { name: 'resourceEvent', type: 'PraxisResourceEvent<unknown>', description: 'Canonical resource interaction envelope forwarded from the internal table for governed host composition.' },
7225
7500
  ],
7226
7501
  editableTargets: [
7227
7502
  { kind: 'resourceBinding', resolver: 'crud-resource-by-path-or-key', description: 'Resource path/key, optional schema path template, id field, endpoint key and query context owned by CRUD orchestration.' },