@praxisui/crud 9.0.4-rc.23 → 9.0.4-rc.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -194,6 +194,12 @@ outside the request schema and are never added to the update payload. Custom
194
194
  drawer adapters receive the same `resourceIdentity` context and retain their own
195
195
  presentation responsibility.
196
196
 
197
+ Related operations also receive `contextIdentity` when the governed surface
198
+ publishes `context.operation.taskScope`. The default host labels it as “Related
199
+ to” and keeps it distinct from the current child record. Both identities stay
200
+ outside dynamic-form inputs and submit payloads; capabilities and HATEOAS remain
201
+ the authority for whether the operation may execute.
202
+
197
203
  Route mode has a different lifecycle because `@praxisui/crud` does not own the
198
204
  destination component. Routed edit hosts must load the entity through the
199
205
  canonical read contract and materialize identity from its response schema. Do
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-08-04T15:15:13.729Z",
3
+ "generatedAt": "2026-08-04T21:23:28.566Z",
4
4
  "packageName": "@praxisui/crud",
5
- "packageVersion": "9.0.4-rc.23",
5
+ "packageVersion": "9.0.4-rc.25",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 2,
@@ -6,7 +6,7 @@ import { Router, ActivatedRoute, RouterLink } from '@angular/router';
6
6
  import { MatSnackBar } from '@angular/material/snack-bar';
7
7
  import { firstValueFrom, BehaviorSubject, Subscription } from 'rxjs';
8
8
  import * as i2$1 from '@praxisui/core';
9
- import { ASYNC_CONFIG_STORAGE, GlobalConfigService, CrudOperationResolutionService, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, EnterpriseRuntimeContextService, GLOBAL_SURFACE_SERVICE, SurfaceOutletRegistryService, ComponentKeyService, ResourceDiscoveryService, ResourceActionOpenAdapterService, ResourceSurfaceOpenAdapterService, translateUnavailableWorkflowMessage, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, PraxisIconButtonComponent, PraxisResourceIdentityComponent, GenericCrudService, ComponentMetadataRegistry } from '@praxisui/core';
9
+ import { ASYNC_CONFIG_STORAGE, GlobalConfigService, CrudOperationResolutionService, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, EnterpriseRuntimeContextService, GLOBAL_SURFACE_SERVICE, SurfaceOutletRegistryService, ComponentKeyService, ResourceDiscoveryService, ResourceActionOpenAdapterService, ResourceSurfaceOpenAdapterService, normalizeSurfaceOperationContext, translateUnavailableWorkflowMessage, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, 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';
@@ -141,15 +141,17 @@ class CrudLauncherService {
141
141
  row[idField] !== undefined) {
142
142
  inputs[idField] = row[idField];
143
143
  }
144
- const resourceIdentity = actionForLaunch.action === 'edit'
144
+ const resourceIdentity = actionForLaunch.action === 'edit' || actionForLaunch.action === 'view'
145
145
  ? runtime?.resourceIdentity ?? null
146
146
  : null;
147
+ const contextIdentity = runtime?.contextIdentity ?? null;
147
148
  if (mode === 'drawer' && this.drawerAdapter) {
148
149
  await Promise.resolve(this.drawerAdapter.open({
149
150
  action: actionForLaunch,
150
151
  metadata: merged.metadata,
151
152
  inputs,
152
153
  resourceIdentity,
154
+ contextIdentity,
153
155
  onClose: drawerCallbacks?.onClose,
154
156
  onResult: drawerCallbacks?.onResult,
155
157
  }));
@@ -212,6 +214,7 @@ class CrudLauncherService {
212
214
  presentation: drawerMode ? 'drawer' : 'modal',
213
215
  dialogPosition,
214
216
  resourceIdentity,
217
+ contextIdentity,
215
218
  },
216
219
  });
217
220
  if (drawerMode) {
@@ -911,6 +914,8 @@ const PRAXIS_CRUD_RUNTIME_I18N_CONFIG = {
911
914
  'crud.actions.opening': 'Abrindo {action}…',
912
915
  'crud.surface.openFailed': 'Não foi possível abrir {title}. Tente novamente ou confirme se esse recurso continua disponível.',
913
916
  'crud.dialog.recordContextLabel': 'Registro em edição',
917
+ 'crud.dialog.viewRecordContextLabel': 'Registro em consulta',
918
+ 'crud.dialog.relatedContextLabel': 'Vinculado a',
914
919
  'crud.delete.confirmMessage': 'Esta ação não pode ser desfeita. Deseja continuar?',
915
920
  'crud.delete.cancel': 'Cancelar',
916
921
  },
@@ -932,6 +937,8 @@ const PRAXIS_CRUD_RUNTIME_I18N_CONFIG = {
932
937
  'crud.actions.opening': 'Opening {action}…',
933
938
  'crud.surface.openFailed': 'Could not open {title}. Try again or confirm that this resource is still available.',
934
939
  'crud.dialog.recordContextLabel': 'Record being edited',
940
+ 'crud.dialog.viewRecordContextLabel': 'Record being viewed',
941
+ 'crud.dialog.relatedContextLabel': 'Related to',
935
942
  'crud.delete.confirmMessage': 'This action cannot be undone. Do you want to continue?',
936
943
  'crud.delete.cancel': 'Cancel',
937
944
  },
@@ -3863,6 +3870,7 @@ class PraxisCrudComponent {
3863
3870
  action: this.normalizeCrudActionName(actionMeta?.action ?? normalizedAction),
3864
3871
  };
3865
3872
  const resourceIdentity = runtimeEvent?.resourceIdentity ?? null;
3873
+ const contextIdentity = this.resolveTaskScopeIdentity();
3866
3874
  const handledByDuplicateDraft = await this.tryHandleCanonicalDuplicateDraftAction(effectiveAction, contextualRow);
3867
3875
  if (handledByDuplicateDraft) {
3868
3876
  return;
@@ -3911,6 +3919,7 @@ class PraxisCrudComponent {
3911
3919
  : this.collectionCapabilities,
3912
3920
  links: this.resolveCrudRuntimeLinks(effectiveAction.action, contextualRow),
3913
3921
  resourceIdentity,
3922
+ contextIdentity,
3914
3923
  });
3915
3924
  this.afterOpen.emit({ mode, action: effectiveAction.action });
3916
3925
  if (mode === 'drawer') {
@@ -3946,6 +3955,10 @@ class PraxisCrudComponent {
3946
3955
  this.openingActionLabel.set(null);
3947
3956
  }
3948
3957
  }
3958
+ resolveTaskScopeIdentity() {
3959
+ const operation = normalizeSurfaceOperationContext(this.context?.['operation']);
3960
+ return operation?.taskScope?.identity ?? null;
3961
+ }
3949
3962
  getOpeningActionStatus(actionLabel) {
3950
3963
  return this.txWithParams('crud.actions.opening', 'Abrindo {action}…', { action: actionLabel });
3951
3964
  }
@@ -5408,6 +5421,7 @@ class DynamicFormDialogHostComponent {
5408
5421
  resourceId;
5409
5422
  initialValue;
5410
5423
  resourceIdentity = null;
5424
+ contextIdentity = null;
5411
5425
  schemaUrl;
5412
5426
  submitUrl;
5413
5427
  submitMethod;
@@ -5427,6 +5441,8 @@ class DynamicFormDialogHostComponent {
5427
5441
  maximizeLabel: 'Maximizar',
5428
5442
  restoreLabel: 'Restaurar',
5429
5443
  recordContextLabel: translateCrudRuntimeText(this.i18n, 'crud.dialog.recordContextLabel', 'Registro em edição'),
5444
+ viewRecordContextLabel: translateCrudRuntimeText(this.i18n, 'crud.dialog.viewRecordContextLabel', 'Registro em consulta'),
5445
+ relatedContextLabel: translateCrudRuntimeText(this.i18n, 'crud.dialog.relatedContextLabel', 'Vinculado a'),
5430
5446
  discardTitle: 'Descartar alterações?',
5431
5447
  discardMessage: 'Você tem alterações não salvas. Deseja fechar assim mesmo?',
5432
5448
  discardConfirm: 'Descartar',
@@ -5477,9 +5493,12 @@ class DynamicFormDialogHostComponent {
5477
5493
  this.apiUrlEntry = this.data.inputs?.['apiUrlEntry'] ?? null;
5478
5494
  const act = this.data.action?.action;
5479
5495
  this.mode = act === 'edit' ? 'edit' : act === 'view' ? 'view' : 'create';
5480
- this.resourceIdentity = this.mode === 'edit' && this.data.resourceIdentity
5496
+ this.resourceIdentity = this.mode !== 'create' && this.data.resourceIdentity
5481
5497
  ? this.data.resourceIdentity
5482
5498
  : null;
5499
+ this.contextIdentity = this.data.contextIdentity
5500
+ ? this.data.contextIdentity
5501
+ : null;
5483
5502
  this.formConfig = this.resolveFormConfig();
5484
5503
  this.layoutPolicy = this.resolveLayoutPolicy();
5485
5504
  this.formActions = this.resolveFormActions();
@@ -5515,6 +5534,32 @@ class DynamicFormDialogHostComponent {
5515
5534
  .pipe(takeUntilDestroyed(this.destroyRef))
5516
5535
  .subscribe(() => this.saveState());
5517
5536
  }
5537
+ get resourceIdentityLabel() {
5538
+ return this.mode === 'view'
5539
+ ? this.texts['viewRecordContextLabel']
5540
+ : this.texts['recordContextLabel'];
5541
+ }
5542
+ get showContextIdentity() {
5543
+ return !!this.contextIdentity && !this.sameIdentity(this.contextIdentity, this.resourceIdentity);
5544
+ }
5545
+ sameIdentity(left, right) {
5546
+ if (!left || !right)
5547
+ return false;
5548
+ const leftKey = left.key;
5549
+ const rightKey = right.key;
5550
+ const leftLabel = this.identityLabel(left);
5551
+ const rightLabel = this.identityLabel(right);
5552
+ if (leftKey && rightKey) {
5553
+ return leftKey.field === rightKey.field
5554
+ && String(leftKey.value) === String(rightKey.value)
5555
+ && !!leftLabel
5556
+ && leftLabel === rightLabel;
5557
+ }
5558
+ return !!leftLabel && leftLabel === rightLabel;
5559
+ }
5560
+ identityLabel(identity) {
5561
+ return String(identity.displayLabel || identity.title?.value || '').trim();
5562
+ }
5518
5563
  extractInitialValue(inputs) {
5519
5564
  if (!inputs || typeof inputs !== 'object') {
5520
5565
  return null;
@@ -5858,17 +5903,34 @@ class DynamicFormDialogHostComponent {
5858
5903
  aria-labelledby="crudDialogTitle"
5859
5904
  [attr.aria-describedby]="dialogSubtitle ? 'crudDialogDescription' : null"
5860
5905
  >
5861
- @if (resourceIdentity) {
5862
- <div class="crud-resource-identity" data-testid="crud-resource-identity">
5863
- <span class="crud-resource-identity__label" aria-hidden="true">
5864
- {{ texts.recordContextLabel }}
5865
- </span>
5866
- <praxis-resource-identity
5867
- [identity]="resourceIdentity"
5868
- density="compact"
5869
- [showKeyLabel]="true"
5870
- [ariaLabel]="texts.recordContextLabel"
5871
- ></praxis-resource-identity>
5906
+ @if (showContextIdentity || resourceIdentity) {
5907
+ <div class="crud-operation-context" data-testid="crud-operation-context">
5908
+ @if (showContextIdentity) {
5909
+ <div class="crud-resource-identity" data-testid="crud-context-identity">
5910
+ <span class="crud-resource-identity__label" aria-hidden="true">
5911
+ {{ texts.relatedContextLabel }}
5912
+ </span>
5913
+ <praxis-resource-identity
5914
+ [identity]="contextIdentity"
5915
+ density="compact"
5916
+ [showKeyLabel]="true"
5917
+ [ariaLabel]="texts.relatedContextLabel"
5918
+ ></praxis-resource-identity>
5919
+ </div>
5920
+ }
5921
+ @if (resourceIdentity) {
5922
+ <div class="crud-resource-identity" data-testid="crud-resource-identity">
5923
+ <span class="crud-resource-identity__label" aria-hidden="true">
5924
+ {{ resourceIdentityLabel }}
5925
+ </span>
5926
+ <praxis-resource-identity
5927
+ [identity]="resourceIdentity"
5928
+ density="compact"
5929
+ [showKeyLabel]="true"
5930
+ [ariaLabel]="resourceIdentityLabel"
5931
+ ></praxis-resource-identity>
5932
+ </div>
5933
+ }
5872
5934
  </div>
5873
5935
  }
5874
5936
  <praxis-dynamic-form
@@ -5892,7 +5954,7 @@ class DynamicFormDialogHostComponent {
5892
5954
  (formCancel)="onCancel()"
5893
5955
  ></praxis-dynamic-form>
5894
5956
  </mat-dialog-content>
5895
- `, 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-resource-identity{position:sticky;top:0;z-index:1;display:grid;gap:.25rem;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__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{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 });
5957
+ `, 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{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 });
5896
5958
  }
5897
5959
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormDialogHostComponent, decorators: [{
5898
5960
  type: Component,
@@ -5953,17 +6015,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5953
6015
  aria-labelledby="crudDialogTitle"
5954
6016
  [attr.aria-describedby]="dialogSubtitle ? 'crudDialogDescription' : null"
5955
6017
  >
5956
- @if (resourceIdentity) {
5957
- <div class="crud-resource-identity" data-testid="crud-resource-identity">
5958
- <span class="crud-resource-identity__label" aria-hidden="true">
5959
- {{ texts.recordContextLabel }}
5960
- </span>
5961
- <praxis-resource-identity
5962
- [identity]="resourceIdentity"
5963
- density="compact"
5964
- [showKeyLabel]="true"
5965
- [ariaLabel]="texts.recordContextLabel"
5966
- ></praxis-resource-identity>
6018
+ @if (showContextIdentity || resourceIdentity) {
6019
+ <div class="crud-operation-context" data-testid="crud-operation-context">
6020
+ @if (showContextIdentity) {
6021
+ <div class="crud-resource-identity" data-testid="crud-context-identity">
6022
+ <span class="crud-resource-identity__label" aria-hidden="true">
6023
+ {{ texts.relatedContextLabel }}
6024
+ </span>
6025
+ <praxis-resource-identity
6026
+ [identity]="contextIdentity"
6027
+ density="compact"
6028
+ [showKeyLabel]="true"
6029
+ [ariaLabel]="texts.relatedContextLabel"
6030
+ ></praxis-resource-identity>
6031
+ </div>
6032
+ }
6033
+ @if (resourceIdentity) {
6034
+ <div class="crud-resource-identity" data-testid="crud-resource-identity">
6035
+ <span class="crud-resource-identity__label" aria-hidden="true">
6036
+ {{ resourceIdentityLabel }}
6037
+ </span>
6038
+ <praxis-resource-identity
6039
+ [identity]="resourceIdentity"
6040
+ density="compact"
6041
+ [showKeyLabel]="true"
6042
+ [ariaLabel]="resourceIdentityLabel"
6043
+ ></praxis-resource-identity>
6044
+ </div>
6045
+ }
5967
6046
  </div>
5968
6047
  }
5969
6048
  <praxis-dynamic-form
@@ -5987,7 +6066,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5987
6066
  (formCancel)="onCancel()"
5988
6067
  ></praxis-dynamic-form>
5989
6068
  </mat-dialog-content>
5990
- `, 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-resource-identity{position:sticky;top:0;z-index:1;display:grid;gap:.25rem;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__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{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"] }]
6069
+ `, 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{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"] }]
5991
6070
  }], ctorParameters: () => [{ type: undefined, decorators: [{
5992
6071
  type: Inject,
5993
6072
  args: [MatDialogRef]
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@praxisui/crud",
3
- "version": "9.0.4-rc.23",
3
+ "version": "9.0.4-rc.25",
4
4
  "description": "CRUD building blocks for Praxis UI: integrates dynamic forms and tables with unified configuration and services.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
7
7
  "@angular/core": "^21.0.0",
8
- "@praxisui/dynamic-form": "^9.0.4-rc.23",
9
- "@praxisui/table": "^9.0.4-rc.23",
10
- "@praxisui/core": "^9.0.4-rc.23",
11
- "@praxisui/dynamic-fields": "^9.0.4-rc.23",
12
- "@praxisui/settings-panel": "^9.0.4-rc.23",
8
+ "@praxisui/dynamic-form": "^9.0.4-rc.25",
9
+ "@praxisui/table": "^9.0.4-rc.25",
10
+ "@praxisui/core": "^9.0.4-rc.25",
11
+ "@praxisui/dynamic-fields": "^9.0.4-rc.25",
12
+ "@praxisui/settings-panel": "^9.0.4-rc.25",
13
13
  "@angular/cdk": "^21.0.0",
14
14
  "@angular/forms": "^21.0.0",
15
15
  "@angular/material": "^21.0.0",
16
16
  "@angular/router": "^21.0.0",
17
- "@praxisui/ai": "^9.0.4-rc.23",
17
+ "@praxisui/ai": "^9.0.4-rc.25",
18
18
  "rxjs": "~7.8.0"
19
19
  },
20
20
  "dependencies": {
@@ -55,8 +55,10 @@ interface CrudDrawerOpenConfig {
55
55
  action: CrudAction;
56
56
  metadata: CrudMetadata;
57
57
  inputs: Record<string, unknown>;
58
- /** Read-only record context for edit drawers. It is never part of form inputs or submit payloads. */
58
+ /** Read-only record context for existing-record drawers. It is never part of form inputs or submit payloads. */
59
59
  resourceIdentity?: MaterializedResourceIdentity | null;
60
+ /** Read-only task scope for related operations. It is never part of form inputs or submit payloads. */
61
+ contextIdentity?: MaterializedResourceIdentity | null;
60
62
  /**
61
63
  * Notifica fechamento do drawer (cancelamento/fechamento externo/resultado finalizado).
62
64
  */
@@ -245,6 +245,7 @@ declare class PraxisCrudComponent implements OnChanges {
245
245
  onToolbarAction(event: CrudActionRuntimeEvent): Promise<void>;
246
246
  ngOnChanges(changes: SimpleChanges): void;
247
247
  onAction(action: string, row?: Record<string, unknown>, runtimeEvent?: CrudActionRuntimeEvent): Promise<void>;
248
+ private resolveTaskScopeIdentity;
248
249
  getOpeningActionStatus(actionLabel: string): string;
249
250
  private resolveOpeningActionLabel;
250
251
  private hasExplicitOpenBinding;
@@ -356,6 +357,7 @@ declare class CrudLauncherService {
356
357
  capabilities?: ResourceCapabilitySnapshot | null;
357
358
  links?: RestApiLinks | null;
358
359
  resourceIdentity?: MaterializedResourceIdentity | null;
360
+ contextIdentity?: MaterializedResourceIdentity | null;
359
361
  }): Promise<{
360
362
  mode: FormOpenMode;
361
363
  ref?: DialogRef<any>;
@@ -411,6 +413,7 @@ declare class DynamicFormDialogHostComponent implements OnInit {
411
413
  resourceId?: string | number;
412
414
  initialValue?: Record<string, unknown> | null;
413
415
  resourceIdentity: MaterializedResourceIdentity | null;
416
+ contextIdentity: MaterializedResourceIdentity | null;
414
417
  schemaUrl?: string | null;
415
418
  submitUrl?: string | null;
416
419
  submitMethod?: string | null;
@@ -425,6 +428,10 @@ declare class DynamicFormDialogHostComponent implements OnInit {
425
428
  private idField;
426
429
  texts: Record<string, string>;
427
430
  constructor(dialogRef: DialogRef<DynamicFormDialogHostComponent>, data: any, dialogService: DialogService, crud: GenericCrudService<any>, configStorage: AsyncConfigStorage);
431
+ get resourceIdentityLabel(): string;
432
+ get showContextIdentity(): boolean;
433
+ private sameIdentity;
434
+ private identityLabel;
428
435
  private extractInitialValue;
429
436
  private resolveFormConfig;
430
437
  get dialogTitle(): string;