@praxisui/crud 10.0.0-rc.11 → 10.0.0-rc.13

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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, DestroyRef, signal, Inject, ViewEncapsulation, Component, EventEmitter, ChangeDetectorRef, Output, Input, ChangeDetectionStrategy, Injectable, InjectionToken, input, computed, effect, output, Injector, booleanAttribute, ViewChild, Optional, ENVIRONMENT_INITIALIZER } from '@angular/core';
2
+ import { inject, DestroyRef, signal, HostListener, Inject, ViewEncapsulation, Component, EventEmitter, ChangeDetectorRef, Output, Input, ChangeDetectionStrategy, Injectable, InjectionToken, input, computed, effect, output, Injector, booleanAttribute, ViewChild, Optional, 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';
@@ -7,7 +7,7 @@ import { MatSnackBar } from '@angular/material/snack-bar';
7
7
  import { firstValueFrom, BehaviorSubject, Subscription } from 'rxjs';
8
8
  import { take, filter } from 'rxjs/operators';
9
9
  import * as i2$2 from '@praxisui/core';
10
- import { PraxisBulkClientService, generateBulkIdempotencyKey, SafeExpressionEvaluator, ASYNC_CONFIG_STORAGE, GlobalConfigService, CrudOperationResolutionService, LoggerService, SurfaceNavigationError, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, PraxisWidgetEventBus, 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
+ import { PraxisBulkClientService, generateBulkIdempotencyKey, API_URL, SafeExpressionEvaluator, buildApiUrl, ASYNC_CONFIG_STORAGE, GlobalConfigService, CrudOperationResolutionService, LoggerService, SurfaceNavigationError, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, PraxisWidgetEventBus, 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';
11
11
  export { createDynamicRecord, extractRecordId, isDynamicDataRecord, resolveDataField } from '@praxisui/core';
12
12
  import * as i1$1 from '@angular/common';
13
13
  import { CommonModule } from '@angular/common';
@@ -52,6 +52,13 @@ class PraxisBulkOperationDialogComponent {
52
52
  execution = signal(null, ...(ngDevMode ? [{ debugName: "execution" }] : /* istanbul ignore next */ []));
53
53
  errorMessage = signal(null, ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
54
54
  parametersValue = signal({}, ...(ngDevMode ? [{ debugName: "parametersValue" }] : /* istanbul ignore next */ []));
55
+ proposalItems = signal([], ...(ngDevMode ? [{ debugName: "proposalItems" }] : /* istanbul ignore next */ []));
56
+ proposalNextCursor = signal(null, ...(ngDevMode ? [{ debugName: "proposalNextCursor" }] : /* istanbul ignore next */ []));
57
+ loadingProposalResults = signal(false, ...(ngDevMode ? [{ debugName: "loadingProposalResults" }] : /* istanbul ignore next */ []));
58
+ executionItems = signal([], ...(ngDevMode ? [{ debugName: "executionItems" }] : /* istanbul ignore next */ []));
59
+ executionNextCursor = signal(null, ...(ngDevMode ? [{ debugName: "executionNextCursor" }] : /* istanbul ignore next */ []));
60
+ loadingExecutionResults = signal(false, ...(ngDevMode ? [{ debugName: "loadingExecutionResults" }] : /* istanbul ignore next */ []));
61
+ cancellingExecution = signal(false, ...(ngDevMode ? [{ debugName: "cancellingExecution" }] : /* istanbul ignore next */ []));
55
62
  action;
56
63
  targets;
57
64
  confirmationIdempotencyKey;
@@ -80,22 +87,33 @@ class PraxisBulkOperationDialogComponent {
80
87
  hasParametersSchema() {
81
88
  const bulkContract = this.action.execution?.bulk;
82
89
  const hasSchemaUrl = Boolean(bulkContract?.evaluationOperation?.requestSchema?.url || this.action.requestSchemaUrl);
83
- if (hasSchemaUrl &&
84
- (bulkContract?.parametersPointer === '/properties/parameters' || bulkContract?.mode === 'DOMAIN_COMMAND')) {
85
- return true;
90
+ if (!hasSchemaUrl) {
91
+ return false;
86
92
  }
87
- return false;
93
+ const descriptors = this.action.commandParameterDescriptors ||
94
+ bulkContract?.commandParameterDescriptors;
95
+ if (Array.isArray(descriptors)) {
96
+ return descriptors.length > 0;
97
+ }
98
+ return Boolean(typeof bulkContract?.parametersPointer === 'string' &&
99
+ bulkContract.parametersPointer.trim().length > 0);
88
100
  }
89
101
  parametersSchemaUrl() {
90
102
  const bulkContract = this.action.execution?.bulk;
91
- return (bulkContract?.evaluationOperation.requestSchema?.url ||
103
+ return (bulkContract?.evaluationOperation?.requestSchema?.url ||
92
104
  this.action.requestSchemaUrl ||
93
105
  null);
94
106
  }
95
107
  onParametersChange(event) {
96
- const val = event?.value ?? event;
97
- if (val && typeof val === 'object') {
98
- this.parametersValue.set(val);
108
+ const raw = event?.formData ?? event?.value ?? event;
109
+ if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
110
+ const unwrapped = raw.parameters &&
111
+ typeof raw.parameters === 'object' &&
112
+ !Array.isArray(raw.parameters) &&
113
+ Object.keys(raw).length === 1
114
+ ? raw.parameters
115
+ : raw;
116
+ this.parametersValue.set(unwrapped);
99
117
  }
100
118
  }
101
119
  isBlockedByAtomicity() {
@@ -103,7 +121,8 @@ class PraxisBulkOperationDialogComponent {
103
121
  const bulkContract = this.action.execution?.bulk;
104
122
  if (!prop || !bulkContract)
105
123
  return false;
106
- return bulkContract.atomicity === 'ATOMIC' && prop.totals.blocked > 0;
124
+ return ((bulkContract.atomicity === 'ATOMIC' || bulkContract.atomicity === 'ALL_OR_NONE') &&
125
+ prop.totals.blocked > 0);
107
126
  }
108
127
  isConfirmDisabled() {
109
128
  const prop = this.proposal();
@@ -133,10 +152,14 @@ class PraxisBulkOperationDialogComponent {
133
152
  this.errorMessage.set('Contrato de operação em lote não encontrado na ação.');
134
153
  return;
135
154
  }
155
+ this.confirmationIdempotencyKey = undefined;
136
156
  this.step.set('EVALUATING');
137
157
  this.errorMessage.set(null);
158
+ const executionMode = bulkContract.executionModes?.includes('SYNC')
159
+ ? 'SYNC'
160
+ : (bulkContract.executionModes?.[0] ?? 'SYNC');
138
161
  const request = {
139
- executionMode: 'SYNC',
162
+ executionMode,
140
163
  selection: {
141
164
  mode: 'EXPLICIT',
142
165
  targets: this.targets,
@@ -153,7 +176,15 @@ class PraxisBulkOperationDialogComponent {
153
176
  .subscribe({
154
177
  next: (proposal) => {
155
178
  this.proposal.set(proposal);
179
+ const inlineItems = Array.isArray(proposal.items)
180
+ ? proposal.items
181
+ : [];
182
+ this.proposalItems.set(inlineItems);
183
+ this.proposalNextCursor.set(null);
156
184
  this.step.set('PREVIEW');
185
+ if (inlineItems.length === 0 && bulkContract.proposalResultsOperation) {
186
+ this.loadProposalResults();
187
+ }
157
188
  },
158
189
  error: (err) => {
159
190
  this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao avaliar proposta em lote.');
@@ -161,6 +192,38 @@ class PraxisBulkOperationDialogComponent {
161
192
  },
162
193
  });
163
194
  }
195
+ loadProposalResults(cursor) {
196
+ const prop = this.proposal();
197
+ const bulkContract = this.action.execution?.bulk;
198
+ if (!prop ||
199
+ !bulkContract?.proposalResultsOperation ||
200
+ typeof this.bulkClient.getProposalResults !== 'function') {
201
+ return;
202
+ }
203
+ this.loadingProposalResults.set(true);
204
+ const requestOptions = {
205
+ endpointKey: this.data.endpointKey,
206
+ apiUrlEntry: this.data.apiUrlEntry,
207
+ };
208
+ this.bulkClient
209
+ .getProposalResults(bulkContract.proposalResultsOperation, prop.proposalId, 50, cursor, requestOptions)
210
+ .pipe(takeUntilDestroyed(this.destroyRef))
211
+ .subscribe({
212
+ next: (page) => {
213
+ const newItems = Array.isArray(page?.items)
214
+ ? page.items
215
+ : Array.isArray(page?.content)
216
+ ? page.content
217
+ : [];
218
+ this.proposalItems.update((prev) => (cursor ? [...prev, ...newItems] : newItems));
219
+ this.proposalNextCursor.set(page?.nextCursor ?? page?.next ?? null);
220
+ this.loadingProposalResults.set(false);
221
+ },
222
+ error: () => {
223
+ this.loadingProposalResults.set(false);
224
+ },
225
+ });
226
+ }
164
227
  onConfirm() {
165
228
  const prop = this.proposal();
166
229
  const bulkContract = this.action.execution?.bulk;
@@ -182,7 +245,25 @@ class PraxisBulkOperationDialogComponent {
182
245
  .subscribe({
183
246
  next: (execution) => {
184
247
  this.execution.set(execution);
185
- this.step.set('RESULT');
248
+ const isTerminal = typeof this.bulkClient.isTerminalStatus === 'function'
249
+ ? this.bulkClient.isTerminalStatus(execution.status)
250
+ : execution.status !== 'QUEUED' && execution.status !== 'RUNNING';
251
+ if (!isTerminal && bulkContract.executionOperation && typeof this.bulkClient.waitForExecution === 'function') {
252
+ this.bulkClient
253
+ .waitForExecution(bulkContract.executionOperation, execution.executionId, 1000, 60000, requestOptions)
254
+ .pipe(takeUntilDestroyed(this.destroyRef))
255
+ .subscribe({
256
+ next: (finalExec) => {
257
+ this.finalizeExecution(finalExec);
258
+ },
259
+ error: (err) => {
260
+ this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao acompanhar execução em lote.');
261
+ this.step.set('RESULT');
262
+ },
263
+ });
264
+ return;
265
+ }
266
+ this.finalizeExecution(execution);
186
267
  },
187
268
  error: (err) => {
188
269
  this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao confirmar execução em lote.');
@@ -190,6 +271,89 @@ class PraxisBulkOperationDialogComponent {
190
271
  },
191
272
  });
192
273
  }
274
+ finalizeExecution(exec) {
275
+ this.execution.set(exec);
276
+ const inlineItems = Array.isArray(exec.items)
277
+ ? exec.items
278
+ : [];
279
+ this.executionItems.set(inlineItems);
280
+ this.executionNextCursor.set(null);
281
+ this.step.set('RESULT');
282
+ if (inlineItems.length === 0 && this.action.execution?.bulk?.resultsOperation) {
283
+ this.loadExecutionResults();
284
+ }
285
+ }
286
+ loadExecutionResults(cursor) {
287
+ const exec = this.execution();
288
+ const bulkContract = this.action.execution?.bulk;
289
+ if (!exec ||
290
+ !bulkContract?.resultsOperation ||
291
+ typeof this.bulkClient.getExecutionResults !== 'function') {
292
+ return;
293
+ }
294
+ this.loadingExecutionResults.set(true);
295
+ const requestOptions = {
296
+ endpointKey: this.data.endpointKey,
297
+ apiUrlEntry: this.data.apiUrlEntry,
298
+ };
299
+ this.bulkClient
300
+ .getExecutionResults(bulkContract.resultsOperation, exec.executionId, 50, cursor, requestOptions)
301
+ .pipe(takeUntilDestroyed(this.destroyRef))
302
+ .subscribe({
303
+ next: (page) => {
304
+ const newItems = Array.isArray(page?.items)
305
+ ? page.items
306
+ : Array.isArray(page?.content)
307
+ ? page.content
308
+ : [];
309
+ this.executionItems.update((prev) => (cursor ? [...prev, ...newItems] : newItems));
310
+ this.executionNextCursor.set(page?.nextCursor ?? page?.next ?? null);
311
+ this.loadingExecutionResults.set(false);
312
+ },
313
+ error: () => {
314
+ this.loadingExecutionResults.set(false);
315
+ },
316
+ });
317
+ }
318
+ canCancelExecution() {
319
+ const exec = this.execution();
320
+ const bulkContract = this.action.execution?.bulk;
321
+ if (this.step() !== 'CONFIRMING' || !exec || !bulkContract?.cancelOperation) {
322
+ return false;
323
+ }
324
+ const isTerminal = typeof this.bulkClient.isTerminalStatus === 'function'
325
+ ? this.bulkClient.isTerminalStatus(exec.status)
326
+ : exec.status !== 'QUEUED' && exec.status !== 'RUNNING';
327
+ return !isTerminal;
328
+ }
329
+ onCancelActiveExecution() {
330
+ const exec = this.execution();
331
+ const bulkContract = this.action.execution?.bulk;
332
+ if (!this.canCancelExecution() ||
333
+ !exec ||
334
+ !bulkContract?.cancelOperation ||
335
+ typeof this.bulkClient.cancel !== 'function') {
336
+ return;
337
+ }
338
+ this.cancellingExecution.set(true);
339
+ const requestOptions = {
340
+ endpointKey: this.data.endpointKey,
341
+ apiUrlEntry: this.data.apiUrlEntry,
342
+ };
343
+ this.bulkClient
344
+ .cancel(bulkContract.cancelOperation, exec.executionId, requestOptions)
345
+ .pipe(takeUntilDestroyed(this.destroyRef))
346
+ .subscribe({
347
+ next: (cancelledExec) => {
348
+ this.cancellingExecution.set(false);
349
+ this.finalizeExecution(cancelledExec);
350
+ },
351
+ error: (err) => {
352
+ this.cancellingExecution.set(false);
353
+ this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao solicitar cancelamento da execução em lote.');
354
+ },
355
+ });
356
+ }
193
357
  onCloseSuccess() {
194
358
  this.dialogRef.close({
195
359
  success: true,
@@ -197,17 +361,27 @@ class PraxisBulkOperationDialogComponent {
197
361
  });
198
362
  }
199
363
  onCancel() {
364
+ if (this.step() === 'RESULT') {
365
+ this.onCloseSuccess();
366
+ return;
367
+ }
200
368
  this.dialogRef.close({ success: false });
201
369
  }
370
+ onEscape(event) {
371
+ if (this.step() !== 'CONFIRMING' && this.step() !== 'EVALUATING') {
372
+ event?.preventDefault();
373
+ this.onCancel();
374
+ }
375
+ }
202
376
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisBulkOperationDialogComponent, deps: [{ token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
203
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisBulkOperationDialogComponent, isStandalone: true, selector: "praxis-bulk-operation-dialog", ngImport: i0, template: `
204
- <div class="praxis-bulk-dialog" role="dialog" aria-labelledby="praxis-bulk-dialog-title">
377
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisBulkOperationDialogComponent, isStandalone: true, selector: "praxis-bulk-operation-dialog", host: { listeners: { "keydown.escape": "onEscape($event)" } }, ngImport: i0, template: `
378
+ <div class="praxis-bulk-dialog" role="dialog" aria-labelledby="praxis-bulk-dialog-title" aria-describedby="praxis-bulk-dialog-subtitle">
205
379
  <div class="praxis-bulk-dialog__header">
206
380
  <div class="praxis-bulk-dialog__title-group">
207
381
  <mat-icon class="praxis-bulk-dialog__header-icon">auto_awesome_motion</mat-icon>
208
382
  <div>
209
383
  <h2 id="praxis-bulk-dialog-title" class="praxis-bulk-dialog__title">{{ getActionTitle() }}</h2>
210
- <p class="praxis-bulk-dialog__subtitle">
384
+ <p id="praxis-bulk-dialog-subtitle" class="praxis-bulk-dialog__subtitle">
211
385
  {{ action.description || 'Execução de comando de domínio com avaliação prévia de regras de negócio e integridade.' }}
212
386
  </p>
213
387
  </div>
@@ -338,6 +512,34 @@ class PraxisBulkOperationDialogComponent {
338
512
  </div>
339
513
  </div>
340
514
  }
515
+
516
+ <!-- Resultados Paginados da Proposta -->
517
+ @if (proposalItems().length > 0) {
518
+ <div class="praxis-bulk-dialog__diagnostics">
519
+ <h4 class="diagnostics-title">Avaliação por Registro ({{ proposalItems().length }})</h4>
520
+ <div class="diagnostics-list">
521
+ @for (item of proposalItems(); track item.id) {
522
+ <div class="diagnostic-item" [class.diagnostic-item--error]="item.decision === 'BLOCKED'">
523
+ <mat-icon>{{ item.decision === 'BLOCKED' ? 'cancel' : 'check_circle' }}</mat-icon>
524
+ <div class="diagnostic-text">
525
+ <span class="diagnostic-target">Item {{ item.id }}:</span>
526
+ <span>{{ item.decision === 'BLOCKED' ? (item.diagnostics?.[0]?.message || 'Bloqueado') : 'Elegível' }}</span>
527
+ </div>
528
+ </div>
529
+ }
530
+ </div>
531
+ @if (proposalNextCursor()) {
532
+ <button
533
+ mat-stroked-button
534
+ type="button"
535
+ (click)="loadProposalResults(proposalNextCursor()!)"
536
+ [disabled]="loadingProposalResults()"
537
+ >
538
+ {{ loadingProposalResults() ? 'Carregando...' : 'Carregar mais resultados' }}
539
+ </button>
540
+ }
541
+ </div>
542
+ }
341
543
  </div>
342
544
  }
343
545
 
@@ -346,6 +548,17 @@ class PraxisBulkOperationDialogComponent {
346
548
  <div class="praxis-bulk-dialog__loading">
347
549
  <mat-spinner diameter="40" />
348
550
  <p>Executando alterações em lote com garantia de idempotência...</p>
551
+ @if (canCancelExecution()) {
552
+ <button
553
+ mat-stroked-button
554
+ color="warn"
555
+ type="button"
556
+ (click)="onCancelActiveExecution()"
557
+ [disabled]="cancellingExecution()"
558
+ >
559
+ {{ cancellingExecution() ? 'Cancelando...' : 'Cancelar Execução' }}
560
+ </button>
561
+ }
349
562
  </div>
350
563
  }
351
564
 
@@ -355,25 +568,52 @@ class PraxisBulkOperationDialogComponent {
355
568
  <div class="praxis-bulk-dialog__alert praxis-bulk-dialog__alert--success">
356
569
  <mat-icon>verified</mat-icon>
357
570
  <div class="alert-content">
358
- <strong>Operação concluída com sucesso!</strong>
359
- <p>Status: Concluído • Transação efetivada e registrada na auditoria.</p>
571
+ <strong>Operação concluída com êxito!</strong>
572
+ <p>Transação homologada e registrada na auditoria corporativa.</p>
360
573
  </div>
361
574
  </div>
362
575
 
363
576
  <div class="praxis-bulk-dialog__totals-grid">
364
577
  <div class="praxis-bulk-dialog__total-card total-card--executable">
365
- <span class="total-label">Confirmados</span>
578
+ <span class="total-label">Efetivados</span>
366
579
  <span class="total-value">{{ execution()?.totals?.confirmed }}</span>
367
580
  </div>
368
581
  <div class="praxis-bulk-dialog__total-card">
369
- <span class="total-label">Inalterados</span>
582
+ <span class="total-label">Preservados</span>
370
583
  <span class="total-value">{{ execution()?.totals?.unchanged }}</span>
371
584
  </div>
372
585
  <div class="praxis-bulk-dialog__total-card" [class.total-card--blocked]="(execution()?.totals?.denied ?? 0) > 0">
373
- <span class="total-label">Recusados</span>
586
+ <span class="total-label">Rejeitados</span>
374
587
  <span class="total-value">{{ execution()?.totals?.denied }}</span>
375
588
  </div>
376
589
  </div>
590
+
591
+ @if (executionItems().length > 0) {
592
+ <div class="praxis-bulk-dialog__diagnostics">
593
+ <h4 class="diagnostics-title">Resultado por Registro ({{ executionItems().length }})</h4>
594
+ <div class="diagnostics-list">
595
+ @for (item of executionItems(); track item.id) {
596
+ <div class="diagnostic-item" [class.diagnostic-item--error]="item.status !== 'CONFIRMED' && item.status !== 'UNCHANGED'">
597
+ <mat-icon>{{ item.status === 'CONFIRMED' || item.status === 'UNCHANGED' ? 'check_circle' : 'cancel' }}</mat-icon>
598
+ <div class="diagnostic-text">
599
+ <span class="diagnostic-target">Item {{ item.id }}:</span>
600
+ <span>{{ item.diagnostics?.[0]?.message || item.status }}</span>
601
+ </div>
602
+ </div>
603
+ }
604
+ </div>
605
+ @if (executionNextCursor()) {
606
+ <button
607
+ mat-stroked-button
608
+ type="button"
609
+ (click)="loadExecutionResults(executionNextCursor()!)"
610
+ [disabled]="loadingExecutionResults()"
611
+ >
612
+ {{ loadingExecutionResults() ? 'Carregando...' : 'Carregar mais resultados' }}
613
+ </button>
614
+ }
615
+ </div>
616
+ }
377
617
  </div>
378
618
  }
379
619
 
@@ -420,7 +660,7 @@ class PraxisBulkOperationDialogComponent {
420
660
  }
421
661
  </div>
422
662
  </div>
423
- `, isInline: true, styles: [".cdk-overlay-pane.praxis-bulk-dialog-panel{--mat-dialog-container-color: var(--praxis-surface-card, var(--card, var(--md-sys-color-surface-container, #1e293b)));--mat-dialog-container-shape: 16px;margin:auto!important;display:flex!important;justify-content:center!important;align-items:center!important}.cdk-overlay-pane.praxis-bulk-dialog-panel .mat-mdc-dialog-surface{border-radius:16px;border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))));box-shadow:var(--praxis-theme-elevation, 0 24px 64px rgba(0, 0, 0, .45));overflow:hidden;width:100%}.praxis-bulk-dialog{display:flex;flex-direction:column;max-height:85vh;width:100%;min-width:600px;max-width:680px;margin:0 auto;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)));background:transparent;box-sizing:border-box}.praxis-bulk-dialog__header{display:flex;justify-content:space-between;align-items:flex-start;padding:20px 24px 16px;border-bottom:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}.praxis-bulk-dialog__title-group{display:flex;align-items:flex-start;gap:12px}.praxis-bulk-dialog__header-icon{color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)));font-size:28px;width:28px;height:28px;margin-top:2px}.praxis-bulk-dialog__title{font-size:1.25rem;font-weight:600;margin:0;line-height:1.3;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)))}.praxis-bulk-dialog__subtitle{font-size:.875rem;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, rgba(148, 163, 184, .8))));margin:4px 0 0}.praxis-bulk-dialog__content{padding:20px 24px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:16px}.praxis-bulk-dialog__targets-badge{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 6%,transparent);border:1px solid color-mix(in srgb,var(--praxis-border-subtle, var(--border, currentColor)) 25%,transparent);border-radius:8px;font-size:.875rem;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__targets-badge mat-icon{font-size:18px;width:18px;height:18px;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.praxis-bulk-dialog__section{display:flex;flex-direction:column;gap:20px}.praxis-bulk-dialog__section-title{font-size:1rem;font-weight:600;margin:0;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)))}.praxis-bulk-dialog__totals-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:0}.praxis-bulk-dialog__total-card{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:96px;padding:18px 20px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 4%,transparent);border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .15))));border-radius:12px;transition:background .2s ease,border-color .2s ease;box-sizing:border-box}.praxis-bulk-dialog__total-card .total-label{font-size:.75rem;text-transform:uppercase;letter-spacing:.06em;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, rgba(148, 163, 184, .8))));font-weight:600;margin-bottom:6px}.praxis-bulk-dialog__total-card .total-value{font-size:1.875rem;font-weight:700;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)));line-height:1;margin:0}.total-card--executable{border-color:color-mix(in srgb,var(--praxis-success, #10b981) 50%,transparent);background:color-mix(in srgb,var(--praxis-success, #10b981) 12%,transparent)}.total-card--executable .total-value{color:var(--praxis-success, #10b981)}.total-card--blocked{border-color:color-mix(in srgb,var(--praxis-error, #ef4444) 50%,transparent);background:color-mix(in srgb,var(--praxis-error, #ef4444) 12%,transparent)}.total-card--blocked .total-value{color:var(--praxis-error, #ef4444)}.praxis-bulk-dialog__alert{display:flex;align-items:flex-start;gap:14px;padding:16px 20px;border-radius:12px;font-size:.875rem;line-height:1.45}.praxis-bulk-dialog__alert mat-icon{font-size:22px;width:22px;height:22px;flex-shrink:0;margin-top:1px}.praxis-bulk-dialog__alert .alert-content{display:flex;flex-direction:column;gap:3px}.praxis-bulk-dialog__alert .alert-content strong{font-size:.9375rem;font-weight:600;color:inherit}.praxis-bulk-dialog__alert .alert-content p{margin:0;font-size:.8125rem;opacity:.9}.praxis-bulk-dialog__alert--error{background:color-mix(in srgb,var(--praxis-error, #ef4444) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-error, #ef4444) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__alert--error mat-icon{color:var(--praxis-error, #ef4444)}.praxis-bulk-dialog__alert--warning{background:color-mix(in srgb,var(--praxis-warning, #d97706) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__alert--warning mat-icon{color:var(--praxis-warning, #d97706)}.praxis-bulk-dialog__alert--success{background:color-mix(in srgb,var(--praxis-success, #10b981) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-success, #10b981) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__alert--success mat-icon{color:var(--praxis-success, #10b981)}.praxis-bulk-dialog__notice-box{display:flex;align-items:center;gap:12px;padding:16px;background:color-mix(in srgb,var(--praxis-info, #0284c7) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-info, #0284c7) 35%,transparent);border-radius:8px;color:var(--praxis-text-primary, var(--foreground, inherit));font-size:.875rem}.praxis-bulk-dialog__notice-box mat-icon{color:var(--praxis-info, #0284c7)}.praxis-bulk-dialog__diagnostics{margin-top:12px;border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))));border-radius:8px;padding:12px 16px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 3%,transparent)}.diagnostics-title{font-size:.875rem;font-weight:600;margin:0 0 8px;color:var(--praxis-text-primary, var(--foreground, inherit))}.diagnostics-list{display:flex;flex-direction:column;gap:6px;max-height:160px;overflow-y:auto}.diagnostic-item{display:flex;align-items:center;gap:8px;font-size:.8125rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.diagnostic-item mat-icon{font-size:16px;width:16px;height:16px;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.diagnostic-item--error{color:var(--praxis-error, #dc2626)}.diagnostic-item--error mat-icon{color:var(--praxis-error, #dc2626)}.diagnostic-target{font-weight:600;margin-right:4px;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;gap:16px;color:var(--praxis-text-muted, var(--muted-foreground, inherit));font-size:.9375rem}.praxis-bulk-dialog__actions{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding:16px 24px 20px;border-top:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatDialogModule }, { 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: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "initialValue", "emptyState", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "readUrl", "submitUrl", "submitMethod", "submitIdempotencyKey", "submitCorrelationId", "submitResourceVersion", "submitResourceVersionTargetResourceKey", "submitResourceVersionTargetIdField", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "showAiAssistant", "formId", "componentInstanceId", "configPersistenceStrategy", "layout", "layoutMode", "formLayout", "generatedLayoutPreset", "layoutPolicy", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "presentation", "presentationPreset", "sectionDefaultAppearance", "sectionDivider", "visibleGlobal", "fieldIconPolicy", "domainRules", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "configPatchChange", "formReady", "valueChange", "payloadPreviewChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "ruleDiagnosticsChange", "reactiveDeterminationExecuted", "reactiveDeterminationPendingChange", "tabChange", "tabSelected"] }], encapsulation: i0.ViewEncapsulation.None });
663
+ `, isInline: true, styles: [".cdk-global-overlay-wrapper:has(.praxis-bulk-dialog-panel){display:flex!important;justify-content:center!important;align-items:center!important;padding:24px!important;box-sizing:border-box!important}.cdk-overlay-pane.praxis-bulk-dialog-panel{--mat-dialog-container-color: var(--card, var(--praxis-color-surface, #1e293b)) !important;--mdc-dialog-container-color: var(--card, var(--praxis-color-surface, #1e293b)) !important;--mat-dialog-container-shape: 16px;position:relative!important;inset:auto!important;margin:auto!important;display:flex!important;justify-content:center!important;align-items:center!important}.cdk-overlay-pane.praxis-bulk-dialog-panel .mat-mdc-dialog-surface{border-radius:16px;border:1px solid var(--border, var(--praxis-border-subtle, rgba(148, 163, 184, .2)));box-shadow:0 24px 64px #0000008c;background-color:var(--card, var(--praxis-color-surface, #1e293b))!important;color:var(--foreground, #f8fafc)!important;overflow:hidden;width:100%}.praxis-bulk-dialog{display:flex;flex-direction:column;max-height:85vh;width:100%;min-width:600px;max-width:680px;margin:0 auto;color:var(--foreground, var(--praxis-text-primary, #f8fafc));background:transparent;box-sizing:border-box}.praxis-bulk-dialog__header{display:flex;justify-content:space-between;align-items:flex-start;padding:22px 26px 18px;border-bottom:1px solid var(--border, rgba(148, 163, 184, .15))}.praxis-bulk-dialog__title-group{display:flex;align-items:flex-start;gap:14px}.praxis-bulk-dialog__header-icon{color:var(--primary, var(--praxis-primary, #0284c7));font-size:28px;width:28px;height:28px;margin-top:2px}.praxis-bulk-dialog__title{font-size:1.25rem;font-weight:600;margin:0;line-height:1.3;color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__subtitle{font-size:.875rem;color:var(--muted-foreground, rgba(148, 163, 184, .85));margin:4px 0 0;line-height:1.45}.praxis-bulk-dialog__content{padding:22px 26px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:20px}.praxis-bulk-dialog__targets-badge{display:inline-flex;align-items:center;gap:10px;padding:10px 16px;background:color-mix(in srgb,var(--foreground, #f8fafc) 6%,var(--card, #1e293b));border:1px solid var(--border, rgba(148, 163, 184, .2));border-radius:10px;font-size:.875rem;color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__targets-badge mat-icon{font-size:18px;width:18px;height:18px;color:var(--primary, var(--praxis-primary, #0284c7))}.praxis-bulk-dialog__section{display:flex;flex-direction:column;gap:20px}.praxis-bulk-dialog__section-title{font-size:1rem;font-weight:600;margin:0;color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__totals-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:4px 0 12px}.praxis-bulk-dialog__total-card{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:104px;padding:20px 22px;background:color-mix(in srgb,var(--foreground, #f8fafc) 6%,var(--card, #1e293b));border:1px solid var(--border, rgba(148, 163, 184, .2));border-radius:14px;transition:background .2s ease,border-color .2s ease;box-sizing:border-box;gap:8px}.praxis-bulk-dialog__total-card .total-label{font-size:.8125rem;text-transform:uppercase;letter-spacing:.05em;color:var(--muted-foreground, rgba(148, 163, 184, .85));font-weight:600;margin-bottom:2px}.praxis-bulk-dialog__total-card .total-value{font-size:2rem;font-weight:700;color:var(--foreground, #f8fafc);line-height:1;margin:0;letter-spacing:-.02em}.total-card--executable{border-color:color-mix(in srgb,var(--praxis-success, #10b981) 50%,transparent)!important;background:color-mix(in srgb,var(--praxis-success, #10b981) 16%,var(--card, #1e293b))!important}.total-card--executable .total-value{color:var(--praxis-success, #10b981)}.total-card--blocked{border-color:color-mix(in srgb,var(--praxis-error, #ef4444) 50%,transparent)!important;background:color-mix(in srgb,var(--praxis-error, #ef4444) 16%,var(--card, #1e293b))!important}.total-card--blocked .total-value{color:var(--praxis-error, #ef4444)}.praxis-bulk-dialog__alert{display:flex;align-items:flex-start;gap:14px;padding:18px 22px;border-radius:12px;font-size:.875rem;line-height:1.5}.praxis-bulk-dialog__alert mat-icon{font-size:22px;width:22px;height:22px;flex-shrink:0;margin-top:1px}.praxis-bulk-dialog__alert .alert-content{display:flex;flex-direction:column;gap:4px}.praxis-bulk-dialog__alert .alert-content strong{font-size:.9375rem;font-weight:600;color:inherit}.praxis-bulk-dialog__alert .alert-content p{margin:0;font-size:.8125rem;opacity:.9}.praxis-bulk-dialog__alert--error{background:color-mix(in srgb,var(--praxis-error, #ef4444) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-error, #ef4444) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__alert--error mat-icon{color:var(--praxis-error, #ef4444)}.praxis-bulk-dialog__alert--warning{background:color-mix(in srgb,var(--praxis-warning, #d97706) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__alert--warning mat-icon{color:var(--praxis-warning, #d97706)}.praxis-bulk-dialog__alert--success{background:color-mix(in srgb,var(--praxis-success, #10b981) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-success, #10b981) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__alert--success mat-icon{color:var(--praxis-success, #10b981)}.praxis-bulk-dialog__notice-box{display:flex;align-items:center;gap:12px;padding:16px 20px;background:color-mix(in srgb,var(--praxis-info, #0284c7) 14%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-info, #0284c7) 40%,transparent);border-radius:10px;color:var(--foreground, #f8fafc);font-size:.875rem}.praxis-bulk-dialog__notice-box mat-icon{color:var(--praxis-info, #0284c7)}.praxis-bulk-dialog__diagnostics{margin-top:12px;border:1px solid var(--border, rgba(148, 163, 184, .2));border-radius:10px;padding:14px 18px;background:color-mix(in srgb,var(--foreground, #f8fafc) 4%,var(--card, #1e293b))}.diagnostics-title{font-size:.875rem;font-weight:600;margin:0 0 8px;color:var(--praxis-text-primary, var(--foreground, inherit))}.diagnostics-list{display:flex;flex-direction:column;gap:6px;max-height:160px;overflow-y:auto}.diagnostic-item{display:flex;align-items:center;gap:8px;font-size:.8125rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.diagnostic-item mat-icon{font-size:16px;width:16px;height:16px;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.diagnostic-item--error{color:var(--praxis-error, #dc2626)}.diagnostic-item--error mat-icon{color:var(--praxis-error, #dc2626)}.diagnostic-target{font-weight:600;margin-right:4px;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;gap:16px;color:var(--praxis-text-muted, var(--muted-foreground, inherit));font-size:.9375rem}.praxis-bulk-dialog__actions{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding:16px 24px 20px;border-top:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatDialogModule }, { 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: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "initialValue", "emptyState", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "readUrl", "submitUrl", "submitMethod", "submitIdempotencyKey", "submitCorrelationId", "submitResourceVersion", "submitResourceVersionTargetResourceKey", "submitResourceVersionTargetIdField", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "showAiAssistant", "formId", "componentInstanceId", "configPersistenceStrategy", "layout", "layoutMode", "formLayout", "generatedLayoutPreset", "layoutPolicy", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "presentation", "presentationPreset", "sectionDefaultAppearance", "sectionDivider", "visibleGlobal", "fieldIconPolicy", "domainRules", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "configPatchChange", "formReady", "valueChange", "payloadPreviewChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "ruleDiagnosticsChange", "reactiveDeterminationExecuted", "reactiveDeterminationPendingChange", "tabChange", "tabSelected"] }], encapsulation: i0.ViewEncapsulation.None });
424
664
  }
425
665
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisBulkOperationDialogComponent, decorators: [{
426
666
  type: Component,
@@ -433,13 +673,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
433
673
  MatProgressSpinnerModule,
434
674
  PraxisDynamicForm,
435
675
  ], template: `
436
- <div class="praxis-bulk-dialog" role="dialog" aria-labelledby="praxis-bulk-dialog-title">
676
+ <div class="praxis-bulk-dialog" role="dialog" aria-labelledby="praxis-bulk-dialog-title" aria-describedby="praxis-bulk-dialog-subtitle">
437
677
  <div class="praxis-bulk-dialog__header">
438
678
  <div class="praxis-bulk-dialog__title-group">
439
679
  <mat-icon class="praxis-bulk-dialog__header-icon">auto_awesome_motion</mat-icon>
440
680
  <div>
441
681
  <h2 id="praxis-bulk-dialog-title" class="praxis-bulk-dialog__title">{{ getActionTitle() }}</h2>
442
- <p class="praxis-bulk-dialog__subtitle">
682
+ <p id="praxis-bulk-dialog-subtitle" class="praxis-bulk-dialog__subtitle">
443
683
  {{ action.description || 'Execução de comando de domínio com avaliação prévia de regras de negócio e integridade.' }}
444
684
  </p>
445
685
  </div>
@@ -570,6 +810,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
570
810
  </div>
571
811
  </div>
572
812
  }
813
+
814
+ <!-- Resultados Paginados da Proposta -->
815
+ @if (proposalItems().length > 0) {
816
+ <div class="praxis-bulk-dialog__diagnostics">
817
+ <h4 class="diagnostics-title">Avaliação por Registro ({{ proposalItems().length }})</h4>
818
+ <div class="diagnostics-list">
819
+ @for (item of proposalItems(); track item.id) {
820
+ <div class="diagnostic-item" [class.diagnostic-item--error]="item.decision === 'BLOCKED'">
821
+ <mat-icon>{{ item.decision === 'BLOCKED' ? 'cancel' : 'check_circle' }}</mat-icon>
822
+ <div class="diagnostic-text">
823
+ <span class="diagnostic-target">Item {{ item.id }}:</span>
824
+ <span>{{ item.decision === 'BLOCKED' ? (item.diagnostics?.[0]?.message || 'Bloqueado') : 'Elegível' }}</span>
825
+ </div>
826
+ </div>
827
+ }
828
+ </div>
829
+ @if (proposalNextCursor()) {
830
+ <button
831
+ mat-stroked-button
832
+ type="button"
833
+ (click)="loadProposalResults(proposalNextCursor()!)"
834
+ [disabled]="loadingProposalResults()"
835
+ >
836
+ {{ loadingProposalResults() ? 'Carregando...' : 'Carregar mais resultados' }}
837
+ </button>
838
+ }
839
+ </div>
840
+ }
573
841
  </div>
574
842
  }
575
843
 
@@ -578,6 +846,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
578
846
  <div class="praxis-bulk-dialog__loading">
579
847
  <mat-spinner diameter="40" />
580
848
  <p>Executando alterações em lote com garantia de idempotência...</p>
849
+ @if (canCancelExecution()) {
850
+ <button
851
+ mat-stroked-button
852
+ color="warn"
853
+ type="button"
854
+ (click)="onCancelActiveExecution()"
855
+ [disabled]="cancellingExecution()"
856
+ >
857
+ {{ cancellingExecution() ? 'Cancelando...' : 'Cancelar Execução' }}
858
+ </button>
859
+ }
581
860
  </div>
582
861
  }
583
862
 
@@ -587,25 +866,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
587
866
  <div class="praxis-bulk-dialog__alert praxis-bulk-dialog__alert--success">
588
867
  <mat-icon>verified</mat-icon>
589
868
  <div class="alert-content">
590
- <strong>Operação concluída com sucesso!</strong>
591
- <p>Status: Concluído • Transação efetivada e registrada na auditoria.</p>
869
+ <strong>Operação concluída com êxito!</strong>
870
+ <p>Transação homologada e registrada na auditoria corporativa.</p>
592
871
  </div>
593
872
  </div>
594
873
 
595
874
  <div class="praxis-bulk-dialog__totals-grid">
596
875
  <div class="praxis-bulk-dialog__total-card total-card--executable">
597
- <span class="total-label">Confirmados</span>
876
+ <span class="total-label">Efetivados</span>
598
877
  <span class="total-value">{{ execution()?.totals?.confirmed }}</span>
599
878
  </div>
600
879
  <div class="praxis-bulk-dialog__total-card">
601
- <span class="total-label">Inalterados</span>
880
+ <span class="total-label">Preservados</span>
602
881
  <span class="total-value">{{ execution()?.totals?.unchanged }}</span>
603
882
  </div>
604
883
  <div class="praxis-bulk-dialog__total-card" [class.total-card--blocked]="(execution()?.totals?.denied ?? 0) > 0">
605
- <span class="total-label">Recusados</span>
884
+ <span class="total-label">Rejeitados</span>
606
885
  <span class="total-value">{{ execution()?.totals?.denied }}</span>
607
886
  </div>
608
887
  </div>
888
+
889
+ @if (executionItems().length > 0) {
890
+ <div class="praxis-bulk-dialog__diagnostics">
891
+ <h4 class="diagnostics-title">Resultado por Registro ({{ executionItems().length }})</h4>
892
+ <div class="diagnostics-list">
893
+ @for (item of executionItems(); track item.id) {
894
+ <div class="diagnostic-item" [class.diagnostic-item--error]="item.status !== 'CONFIRMED' && item.status !== 'UNCHANGED'">
895
+ <mat-icon>{{ item.status === 'CONFIRMED' || item.status === 'UNCHANGED' ? 'check_circle' : 'cancel' }}</mat-icon>
896
+ <div class="diagnostic-text">
897
+ <span class="diagnostic-target">Item {{ item.id }}:</span>
898
+ <span>{{ item.diagnostics?.[0]?.message || item.status }}</span>
899
+ </div>
900
+ </div>
901
+ }
902
+ </div>
903
+ @if (executionNextCursor()) {
904
+ <button
905
+ mat-stroked-button
906
+ type="button"
907
+ (click)="loadExecutionResults(executionNextCursor()!)"
908
+ [disabled]="loadingExecutionResults()"
909
+ >
910
+ {{ loadingExecutionResults() ? 'Carregando...' : 'Carregar mais resultados' }}
911
+ </button>
912
+ }
913
+ </div>
914
+ }
609
915
  </div>
610
916
  }
611
917
 
@@ -652,11 +958,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
652
958
  }
653
959
  </div>
654
960
  </div>
655
- `, styles: [".cdk-overlay-pane.praxis-bulk-dialog-panel{--mat-dialog-container-color: var(--praxis-surface-card, var(--card, var(--md-sys-color-surface-container, #1e293b)));--mat-dialog-container-shape: 16px;margin:auto!important;display:flex!important;justify-content:center!important;align-items:center!important}.cdk-overlay-pane.praxis-bulk-dialog-panel .mat-mdc-dialog-surface{border-radius:16px;border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))));box-shadow:var(--praxis-theme-elevation, 0 24px 64px rgba(0, 0, 0, .45));overflow:hidden;width:100%}.praxis-bulk-dialog{display:flex;flex-direction:column;max-height:85vh;width:100%;min-width:600px;max-width:680px;margin:0 auto;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)));background:transparent;box-sizing:border-box}.praxis-bulk-dialog__header{display:flex;justify-content:space-between;align-items:flex-start;padding:20px 24px 16px;border-bottom:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}.praxis-bulk-dialog__title-group{display:flex;align-items:flex-start;gap:12px}.praxis-bulk-dialog__header-icon{color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)));font-size:28px;width:28px;height:28px;margin-top:2px}.praxis-bulk-dialog__title{font-size:1.25rem;font-weight:600;margin:0;line-height:1.3;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)))}.praxis-bulk-dialog__subtitle{font-size:.875rem;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, rgba(148, 163, 184, .8))));margin:4px 0 0}.praxis-bulk-dialog__content{padding:20px 24px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:16px}.praxis-bulk-dialog__targets-badge{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 6%,transparent);border:1px solid color-mix(in srgb,var(--praxis-border-subtle, var(--border, currentColor)) 25%,transparent);border-radius:8px;font-size:.875rem;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__targets-badge mat-icon{font-size:18px;width:18px;height:18px;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.praxis-bulk-dialog__section{display:flex;flex-direction:column;gap:20px}.praxis-bulk-dialog__section-title{font-size:1rem;font-weight:600;margin:0;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)))}.praxis-bulk-dialog__totals-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:0}.praxis-bulk-dialog__total-card{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:96px;padding:18px 20px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 4%,transparent);border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .15))));border-radius:12px;transition:background .2s ease,border-color .2s ease;box-sizing:border-box}.praxis-bulk-dialog__total-card .total-label{font-size:.75rem;text-transform:uppercase;letter-spacing:.06em;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, rgba(148, 163, 184, .8))));font-weight:600;margin-bottom:6px}.praxis-bulk-dialog__total-card .total-value{font-size:1.875rem;font-weight:700;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)));line-height:1;margin:0}.total-card--executable{border-color:color-mix(in srgb,var(--praxis-success, #10b981) 50%,transparent);background:color-mix(in srgb,var(--praxis-success, #10b981) 12%,transparent)}.total-card--executable .total-value{color:var(--praxis-success, #10b981)}.total-card--blocked{border-color:color-mix(in srgb,var(--praxis-error, #ef4444) 50%,transparent);background:color-mix(in srgb,var(--praxis-error, #ef4444) 12%,transparent)}.total-card--blocked .total-value{color:var(--praxis-error, #ef4444)}.praxis-bulk-dialog__alert{display:flex;align-items:flex-start;gap:14px;padding:16px 20px;border-radius:12px;font-size:.875rem;line-height:1.45}.praxis-bulk-dialog__alert mat-icon{font-size:22px;width:22px;height:22px;flex-shrink:0;margin-top:1px}.praxis-bulk-dialog__alert .alert-content{display:flex;flex-direction:column;gap:3px}.praxis-bulk-dialog__alert .alert-content strong{font-size:.9375rem;font-weight:600;color:inherit}.praxis-bulk-dialog__alert .alert-content p{margin:0;font-size:.8125rem;opacity:.9}.praxis-bulk-dialog__alert--error{background:color-mix(in srgb,var(--praxis-error, #ef4444) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-error, #ef4444) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__alert--error mat-icon{color:var(--praxis-error, #ef4444)}.praxis-bulk-dialog__alert--warning{background:color-mix(in srgb,var(--praxis-warning, #d97706) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__alert--warning mat-icon{color:var(--praxis-warning, #d97706)}.praxis-bulk-dialog__alert--success{background:color-mix(in srgb,var(--praxis-success, #10b981) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-success, #10b981) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__alert--success mat-icon{color:var(--praxis-success, #10b981)}.praxis-bulk-dialog__notice-box{display:flex;align-items:center;gap:12px;padding:16px;background:color-mix(in srgb,var(--praxis-info, #0284c7) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-info, #0284c7) 35%,transparent);border-radius:8px;color:var(--praxis-text-primary, var(--foreground, inherit));font-size:.875rem}.praxis-bulk-dialog__notice-box mat-icon{color:var(--praxis-info, #0284c7)}.praxis-bulk-dialog__diagnostics{margin-top:12px;border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))));border-radius:8px;padding:12px 16px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 3%,transparent)}.diagnostics-title{font-size:.875rem;font-weight:600;margin:0 0 8px;color:var(--praxis-text-primary, var(--foreground, inherit))}.diagnostics-list{display:flex;flex-direction:column;gap:6px;max-height:160px;overflow-y:auto}.diagnostic-item{display:flex;align-items:center;gap:8px;font-size:.8125rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.diagnostic-item mat-icon{font-size:16px;width:16px;height:16px;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.diagnostic-item--error{color:var(--praxis-error, #dc2626)}.diagnostic-item--error mat-icon{color:var(--praxis-error, #dc2626)}.diagnostic-target{font-weight:600;margin-right:4px;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;gap:16px;color:var(--praxis-text-muted, var(--muted-foreground, inherit));font-size:.9375rem}.praxis-bulk-dialog__actions{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding:16px 24px 20px;border-top:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}\n"] }]
961
+ `, styles: [".cdk-global-overlay-wrapper:has(.praxis-bulk-dialog-panel){display:flex!important;justify-content:center!important;align-items:center!important;padding:24px!important;box-sizing:border-box!important}.cdk-overlay-pane.praxis-bulk-dialog-panel{--mat-dialog-container-color: var(--card, var(--praxis-color-surface, #1e293b)) !important;--mdc-dialog-container-color: var(--card, var(--praxis-color-surface, #1e293b)) !important;--mat-dialog-container-shape: 16px;position:relative!important;inset:auto!important;margin:auto!important;display:flex!important;justify-content:center!important;align-items:center!important}.cdk-overlay-pane.praxis-bulk-dialog-panel .mat-mdc-dialog-surface{border-radius:16px;border:1px solid var(--border, var(--praxis-border-subtle, rgba(148, 163, 184, .2)));box-shadow:0 24px 64px #0000008c;background-color:var(--card, var(--praxis-color-surface, #1e293b))!important;color:var(--foreground, #f8fafc)!important;overflow:hidden;width:100%}.praxis-bulk-dialog{display:flex;flex-direction:column;max-height:85vh;width:100%;min-width:600px;max-width:680px;margin:0 auto;color:var(--foreground, var(--praxis-text-primary, #f8fafc));background:transparent;box-sizing:border-box}.praxis-bulk-dialog__header{display:flex;justify-content:space-between;align-items:flex-start;padding:22px 26px 18px;border-bottom:1px solid var(--border, rgba(148, 163, 184, .15))}.praxis-bulk-dialog__title-group{display:flex;align-items:flex-start;gap:14px}.praxis-bulk-dialog__header-icon{color:var(--primary, var(--praxis-primary, #0284c7));font-size:28px;width:28px;height:28px;margin-top:2px}.praxis-bulk-dialog__title{font-size:1.25rem;font-weight:600;margin:0;line-height:1.3;color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__subtitle{font-size:.875rem;color:var(--muted-foreground, rgba(148, 163, 184, .85));margin:4px 0 0;line-height:1.45}.praxis-bulk-dialog__content{padding:22px 26px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:20px}.praxis-bulk-dialog__targets-badge{display:inline-flex;align-items:center;gap:10px;padding:10px 16px;background:color-mix(in srgb,var(--foreground, #f8fafc) 6%,var(--card, #1e293b));border:1px solid var(--border, rgba(148, 163, 184, .2));border-radius:10px;font-size:.875rem;color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__targets-badge mat-icon{font-size:18px;width:18px;height:18px;color:var(--primary, var(--praxis-primary, #0284c7))}.praxis-bulk-dialog__section{display:flex;flex-direction:column;gap:20px}.praxis-bulk-dialog__section-title{font-size:1rem;font-weight:600;margin:0;color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__totals-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:4px 0 12px}.praxis-bulk-dialog__total-card{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:104px;padding:20px 22px;background:color-mix(in srgb,var(--foreground, #f8fafc) 6%,var(--card, #1e293b));border:1px solid var(--border, rgba(148, 163, 184, .2));border-radius:14px;transition:background .2s ease,border-color .2s ease;box-sizing:border-box;gap:8px}.praxis-bulk-dialog__total-card .total-label{font-size:.8125rem;text-transform:uppercase;letter-spacing:.05em;color:var(--muted-foreground, rgba(148, 163, 184, .85));font-weight:600;margin-bottom:2px}.praxis-bulk-dialog__total-card .total-value{font-size:2rem;font-weight:700;color:var(--foreground, #f8fafc);line-height:1;margin:0;letter-spacing:-.02em}.total-card--executable{border-color:color-mix(in srgb,var(--praxis-success, #10b981) 50%,transparent)!important;background:color-mix(in srgb,var(--praxis-success, #10b981) 16%,var(--card, #1e293b))!important}.total-card--executable .total-value{color:var(--praxis-success, #10b981)}.total-card--blocked{border-color:color-mix(in srgb,var(--praxis-error, #ef4444) 50%,transparent)!important;background:color-mix(in srgb,var(--praxis-error, #ef4444) 16%,var(--card, #1e293b))!important}.total-card--blocked .total-value{color:var(--praxis-error, #ef4444)}.praxis-bulk-dialog__alert{display:flex;align-items:flex-start;gap:14px;padding:18px 22px;border-radius:12px;font-size:.875rem;line-height:1.5}.praxis-bulk-dialog__alert mat-icon{font-size:22px;width:22px;height:22px;flex-shrink:0;margin-top:1px}.praxis-bulk-dialog__alert .alert-content{display:flex;flex-direction:column;gap:4px}.praxis-bulk-dialog__alert .alert-content strong{font-size:.9375rem;font-weight:600;color:inherit}.praxis-bulk-dialog__alert .alert-content p{margin:0;font-size:.8125rem;opacity:.9}.praxis-bulk-dialog__alert--error{background:color-mix(in srgb,var(--praxis-error, #ef4444) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-error, #ef4444) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__alert--error mat-icon{color:var(--praxis-error, #ef4444)}.praxis-bulk-dialog__alert--warning{background:color-mix(in srgb,var(--praxis-warning, #d97706) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__alert--warning mat-icon{color:var(--praxis-warning, #d97706)}.praxis-bulk-dialog__alert--success{background:color-mix(in srgb,var(--praxis-success, #10b981) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-success, #10b981) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-dialog__alert--success mat-icon{color:var(--praxis-success, #10b981)}.praxis-bulk-dialog__notice-box{display:flex;align-items:center;gap:12px;padding:16px 20px;background:color-mix(in srgb,var(--praxis-info, #0284c7) 14%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-info, #0284c7) 40%,transparent);border-radius:10px;color:var(--foreground, #f8fafc);font-size:.875rem}.praxis-bulk-dialog__notice-box mat-icon{color:var(--praxis-info, #0284c7)}.praxis-bulk-dialog__diagnostics{margin-top:12px;border:1px solid var(--border, rgba(148, 163, 184, .2));border-radius:10px;padding:14px 18px;background:color-mix(in srgb,var(--foreground, #f8fafc) 4%,var(--card, #1e293b))}.diagnostics-title{font-size:.875rem;font-weight:600;margin:0 0 8px;color:var(--praxis-text-primary, var(--foreground, inherit))}.diagnostics-list{display:flex;flex-direction:column;gap:6px;max-height:160px;overflow-y:auto}.diagnostic-item{display:flex;align-items:center;gap:8px;font-size:.8125rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.diagnostic-item mat-icon{font-size:16px;width:16px;height:16px;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.diagnostic-item--error{color:var(--praxis-error, #dc2626)}.diagnostic-item--error mat-icon{color:var(--praxis-error, #dc2626)}.diagnostic-target{font-weight:600;margin-right:4px;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-dialog__loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;gap:16px;color:var(--praxis-text-muted, var(--muted-foreground, inherit));font-size:.9375rem}.praxis-bulk-dialog__actions{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding:16px 24px 20px;border-top:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}\n"] }]
656
962
  }], ctorParameters: () => [{ type: undefined, decorators: [{
657
963
  type: Inject,
658
964
  args: [MAT_DIALOG_DATA]
659
- }] }] });
965
+ }] }], propDecorators: { onEscape: [{
966
+ type: HostListener,
967
+ args: ['keydown.escape', ['$event']]
968
+ }] } });
660
969
 
661
970
  class PraxisBulkEditDialogComponent {
662
971
  data;
@@ -668,17 +977,25 @@ class PraxisBulkEditDialogComponent {
668
977
  proposal = signal(null, ...(ngDevMode ? [{ debugName: "proposal" }] : /* istanbul ignore next */ []));
669
978
  execution = signal(null, ...(ngDevMode ? [{ debugName: "execution" }] : /* istanbul ignore next */ []));
670
979
  errorMessage = signal(null, ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
980
+ fetchedSchema = signal(null, ...(ngDevMode ? [{ debugName: "fetchedSchema" }] : /* istanbul ignore next */ []));
981
+ proposalItems = signal([], ...(ngDevMode ? [{ debugName: "proposalItems" }] : /* istanbul ignore next */ []));
982
+ proposalNextCursor = signal(null, ...(ngDevMode ? [{ debugName: "proposalNextCursor" }] : /* istanbul ignore next */ []));
983
+ loadingProposalResults = signal(false, ...(ngDevMode ? [{ debugName: "loadingProposalResults" }] : /* istanbul ignore next */ []));
984
+ executionItems = signal([], ...(ngDevMode ? [{ debugName: "executionItems" }] : /* istanbul ignore next */ []));
985
+ executionNextCursor = signal(null, ...(ngDevMode ? [{ debugName: "executionNextCursor" }] : /* istanbul ignore next */ []));
986
+ loadingExecutionResults = signal(false, ...(ngDevMode ? [{ debugName: "loadingExecutionResults" }] : /* istanbul ignore next */ []));
987
+ cancellingExecution = signal(false, ...(ngDevMode ? [{ debugName: "cancellingExecution" }] : /* istanbul ignore next */ []));
671
988
  contract;
672
989
  targets;
673
990
  writableFields;
674
991
  clearableFields;
675
992
  confirmationIdempotencyKey = null;
676
993
  get dialogTitle() {
677
- return this.data.title || 'Atualização de Dados em Lote';
994
+ return this.data.title || 'Atualização de Registros em Massa';
678
995
  }
679
996
  get dialogSubtitle() {
680
997
  return (this.data.subtitle ||
681
- 'Aplique alterações padronizadas nos registros selecionados com validação prévia de conformidade e integridade.');
998
+ 'Atualize os campos homologados de forma simultânea e consistente para todos os registros selecionados.');
682
999
  }
683
1000
  constructor(data) {
684
1001
  this.data = data;
@@ -686,17 +1003,72 @@ class PraxisBulkEditDialogComponent {
686
1003
  this.targets = data.targets || [];
687
1004
  this.writableFields = data.bulkContract.editableFields?.writableFields || [];
688
1005
  this.clearableFields = data.bulkContract.editableFields?.clearableFields || [];
1006
+ this.hydrateRemoteSchemaIfNeeded();
689
1007
  if (this.writableFields.length > 0) {
690
1008
  this.addFieldRow();
691
1009
  }
692
1010
  }
1011
+ hydrateRemoteSchemaIfNeeded() {
1012
+ if (this.data.schema?.properties) {
1013
+ return;
1014
+ }
1015
+ const schemaUrl = this.contract.editableFields?.requestSchema?.url ||
1016
+ this.contract.evaluationOperation?.requestSchema?.url;
1017
+ if (!schemaUrl || typeof this.bulkClient.fetchSchema !== 'function') {
1018
+ return;
1019
+ }
1020
+ this.bulkClient
1021
+ .fetchSchema(schemaUrl, {
1022
+ endpointKey: this.data.endpointKey,
1023
+ apiUrlEntry: this.data.apiUrlEntry,
1024
+ })
1025
+ .pipe(takeUntilDestroyed(this.destroyRef))
1026
+ .subscribe({
1027
+ next: (schema) => {
1028
+ if (schema && typeof schema === 'object') {
1029
+ this.fetchedSchema.set(schema);
1030
+ }
1031
+ },
1032
+ error: () => {
1033
+ // Non-blocking schema enrichment fallback
1034
+ },
1035
+ });
1036
+ }
1037
+ getEffectiveSchemaProperty(fieldName) {
1038
+ const schema = this.data.schema || this.fetchedSchema();
1039
+ const rootProps = schema?.properties;
1040
+ if (rootProps?.[fieldName]) {
1041
+ return rootProps[fieldName];
1042
+ }
1043
+ const changesItemProps = rootProps?.changes?.items?.properties ||
1044
+ schema?.items?.properties;
1045
+ if (changesItemProps?.[fieldName]) {
1046
+ return changesItemProps[fieldName];
1047
+ }
1048
+ return undefined;
1049
+ }
1050
+ getFieldDescriptor(fieldName) {
1051
+ const descriptors = this.contract.editableFields?.fieldDescriptors ||
1052
+ this.contract.editableFields?.fields;
1053
+ if (!Array.isArray(descriptors)) {
1054
+ return undefined;
1055
+ }
1056
+ return descriptors.find((d) => (d?.name || d?.field || d?.property) === fieldName);
1057
+ }
693
1058
  isFieldClearable(fieldName) {
1059
+ const descriptor = this.getFieldDescriptor(fieldName);
1060
+ if (descriptor && typeof descriptor['clearable'] === 'boolean') {
1061
+ return descriptor['clearable'];
1062
+ }
694
1063
  return this.clearableFields.includes(fieldName);
695
1064
  }
696
1065
  hasAvailableFieldsToAdd() {
697
1066
  const selectedFields = this.fieldRows().map((r) => r.field);
698
1067
  return this.writableFields.some((f) => !selectedFields.includes(f));
699
1068
  }
1069
+ isFieldSelectedInOtherRow(field, currentRow) {
1070
+ return this.fieldRows().some((r) => r !== currentRow && r.field === field);
1071
+ }
700
1072
  addFieldRow() {
701
1073
  const selectedFields = this.fieldRows().map((r) => r.field);
702
1074
  const nextField = this.writableFields.find((f) => !selectedFields.includes(f)) || this.writableFields[0] || '';
@@ -715,12 +1087,18 @@ class PraxisBulkEditDialogComponent {
715
1087
  getFieldLabel(fieldName) {
716
1088
  if (!fieldName)
717
1089
  return '';
1090
+ const descriptor = this.getFieldDescriptor(fieldName);
1091
+ if (descriptor?.['label'] || descriptor?.['title']) {
1092
+ return String(descriptor['label'] || descriptor['title']);
1093
+ }
718
1094
  const col = this.data.columns?.find((c) => c.field === fieldName);
719
1095
  if (col?.header)
720
1096
  return col.header;
721
- const prop = this.data.schema?.properties?.[fieldName];
722
- if (prop?.title)
723
- return prop.title;
1097
+ const prop = this.getEffectiveSchemaProperty(fieldName);
1098
+ if (prop?.['x-ui']?.label)
1099
+ return String(prop['x-ui'].label);
1100
+ if (prop?.['title'])
1101
+ return String(prop['title']);
724
1102
  return fieldName
725
1103
  .replace(/([A-Z])/g, ' $1')
726
1104
  .replace(/^./, (str) => str.toUpperCase())
@@ -729,38 +1107,75 @@ class PraxisBulkEditDialogComponent {
729
1107
  getFieldType(fieldName) {
730
1108
  if (!fieldName)
731
1109
  return 'string';
1110
+ const descriptor = this.getFieldDescriptor(fieldName);
1111
+ const descControl = String(descriptor?.['controlType'] || '').toLowerCase();
1112
+ const descType = String(descriptor?.['type'] || '').toLowerCase();
1113
+ const descFormat = String(descriptor?.['format'] || '').toLowerCase();
1114
+ if (Array.isArray(descriptor?.['options']) && descriptor['options'].length > 0)
1115
+ return 'enum';
1116
+ if (descControl === 'currency' || descType === 'currency' || descFormat.startsWith('currency'))
1117
+ return 'currency';
1118
+ if (descType === 'boolean' || descControl === 'checkbox' || descControl === 'toggle')
1119
+ return 'boolean';
1120
+ if (descType === 'date' || descFormat === 'date' || descFormat === 'date-time' || descControl === 'date')
1121
+ return 'date';
1122
+ if (descType === 'number' || descType === 'integer' || descType === 'decimal')
1123
+ return 'number';
732
1124
  const col = this.data.columns?.find((c) => c.field === fieldName);
733
- if (col?.type === 'currency')
1125
+ const colFormat = String(col?.format || '').toLowerCase();
1126
+ if (col?.type === 'currency' || colFormat.startsWith('currency'))
734
1127
  return 'currency';
735
1128
  if (col?.type === 'number')
736
1129
  return 'number';
737
- if (col?.type === 'date')
1130
+ if (col?.type === 'date' || colFormat === 'date' || colFormat === 'date-time')
738
1131
  return 'date';
739
1132
  if (col?.type === 'boolean')
740
1133
  return 'boolean';
741
- const prop = this.data.schema?.properties?.[fieldName];
742
- if (prop?.enum && Array.isArray(prop.enum))
1134
+ if (Array.isArray(col?.options) && col.options.length > 0)
1135
+ return 'enum';
1136
+ const prop = this.getEffectiveSchemaProperty(fieldName);
1137
+ const ui = prop?.['x-ui'] || {};
1138
+ const uiControl = String(ui.controlType || '').toLowerCase();
1139
+ const uiFormat = String(ui.format || prop?.['format'] || '').toLowerCase();
1140
+ if ((prop?.['enum'] && Array.isArray(prop['enum'])) ||
1141
+ (Array.isArray(ui.options) && ui.options.length > 0)) {
743
1142
  return 'enum';
744
- if (prop?.type === 'boolean')
1143
+ }
1144
+ if (prop?.['type'] === 'boolean' || uiControl === 'checkbox' || uiControl === 'toggle') {
745
1145
  return 'boolean';
746
- if (prop?.type === 'number' || prop?.type === 'integer') {
747
- if (prop?.format === 'currency' ||
748
- fieldName.toLowerCase().includes('salario') ||
749
- fieldName.toLowerCase().includes('valor') ||
750
- fieldName.toLowerCase().includes('desconto') ||
751
- fieldName.toLowerCase().includes('preco')) {
1146
+ }
1147
+ if (prop?.['type'] === 'number' ||
1148
+ prop?.['type'] === 'integer' ||
1149
+ uiControl === 'currency' ||
1150
+ uiFormat.startsWith('currency')) {
1151
+ if (uiControl === 'currency' || uiFormat.startsWith('currency') || ui.type === 'currency') {
752
1152
  return 'currency';
753
1153
  }
754
1154
  return 'number';
755
1155
  }
756
- if (prop?.format === 'date' || prop?.format === 'date-time')
1156
+ if (uiFormat === 'date' || uiFormat === 'date-time' || uiControl === 'date' || uiControl === 'datepicker') {
757
1157
  return 'date';
1158
+ }
758
1159
  return 'string';
759
1160
  }
760
1161
  getFieldEnumOptions(fieldName) {
761
- const prop = this.data.schema?.properties?.[fieldName];
762
- if (prop?.enum && Array.isArray(prop.enum)) {
763
- return prop.enum.map((val) => ({
1162
+ const descriptor = this.getFieldDescriptor(fieldName);
1163
+ if (Array.isArray(descriptor?.['options']) && descriptor['options'].length > 0) {
1164
+ return descriptor['options'].map((opt) => ({
1165
+ value: String(opt?.value ?? opt?.key ?? opt?.id ?? opt),
1166
+ label: String(opt?.label ?? opt?.title ?? opt?.value ?? opt?.key ?? opt),
1167
+ }));
1168
+ }
1169
+ const prop = this.getEffectiveSchemaProperty(fieldName);
1170
+ const uiOptions = prop?.['x-ui']?.options;
1171
+ if (Array.isArray(uiOptions) && uiOptions.length > 0) {
1172
+ return uiOptions.map((opt) => ({
1173
+ value: String(opt?.value ?? opt?.key ?? opt?.id ?? opt),
1174
+ label: String(opt?.label ?? opt?.title ?? opt?.value ?? opt?.key ?? opt),
1175
+ }));
1176
+ }
1177
+ if (prop?.['enum'] && Array.isArray(prop['enum'])) {
1178
+ return prop['enum'].map((val) => ({
764
1179
  value: String(val),
765
1180
  label: String(val),
766
1181
  }));
@@ -779,21 +1194,23 @@ class PraxisBulkEditDialogComponent {
779
1194
  row.operator = 'SET';
780
1195
  }
781
1196
  const type = this.getFieldType(row.field);
782
- if (type === 'boolean' && row.value !== 'true' && row.value !== 'false') {
1197
+ if (type === 'boolean') {
783
1198
  row.value = 'true';
784
1199
  }
785
1200
  else if (type === 'enum') {
786
1201
  const opts = this.getFieldEnumOptions(row.field);
787
- if (opts.length > 0 && !opts.some((o) => o.value === row.value)) {
788
- row.value = opts[0].value;
789
- }
1202
+ row.value = opts.length > 0 ? opts[0].value : '';
1203
+ }
1204
+ else {
1205
+ row.value = '';
790
1206
  }
791
1207
  }
792
1208
  isBlockedByAtomicity() {
793
1209
  const prop = this.proposal();
794
1210
  if (!prop)
795
1211
  return false;
796
- return this.contract.atomicity === 'ATOMIC' && prop.totals.blocked > 0;
1212
+ return ((this.contract.atomicity === 'ATOMIC' || this.contract.atomicity === 'ALL_OR_NONE') &&
1213
+ prop.totals.blocked > 0);
797
1214
  }
798
1215
  isConfirmDisabled() {
799
1216
  const prop = this.proposal();
@@ -818,21 +1235,49 @@ class PraxisBulkEditDialogComponent {
818
1235
  diag?.severity === 'ERROR');
819
1236
  }
820
1237
  onEvaluate() {
821
- const changes = this.fieldRows()
822
- .filter((r) => Boolean(r.field))
823
- .map((r) => ({
824
- field: r.field,
825
- operator: r.operator,
826
- value: r.operator === 'CLEAR' ? null : this.parseFieldValue(r.value, r.field),
827
- }));
1238
+ for (const r of this.fieldRows()) {
1239
+ if (!r.field)
1240
+ continue;
1241
+ if (r.operator === 'SET') {
1242
+ const valStr = String(r.value ?? '').trim();
1243
+ if (valStr === '') {
1244
+ this.errorMessage.set(`Por favor, informe um valor válido para o campo "${this.getFieldLabel(r.field)}".`);
1245
+ return;
1246
+ }
1247
+ }
1248
+ }
1249
+ const seen = new Set();
1250
+ const changes = [];
1251
+ for (const r of this.fieldRows()) {
1252
+ if (r.field && !seen.has(r.field)) {
1253
+ seen.add(r.field);
1254
+ if (r.operator === 'CLEAR') {
1255
+ changes.push({
1256
+ field: r.field,
1257
+ operator: 'CLEAR',
1258
+ });
1259
+ }
1260
+ else {
1261
+ changes.push({
1262
+ field: r.field,
1263
+ operator: r.operator,
1264
+ value: this.parseFieldValue(r.value, r.field),
1265
+ });
1266
+ }
1267
+ }
1268
+ }
828
1269
  if (changes.length === 0) {
829
1270
  this.errorMessage.set('Selecione ao menos um campo para alterar.');
830
1271
  return;
831
1272
  }
1273
+ this.confirmationIdempotencyKey = null;
832
1274
  this.step.set('EVALUATING');
833
1275
  this.errorMessage.set(null);
1276
+ const executionMode = this.contract.executionModes?.includes('SYNC')
1277
+ ? 'SYNC'
1278
+ : (this.contract.executionModes?.[0] ?? 'SYNC');
834
1279
  const request = {
835
- executionMode: 'SYNC',
1280
+ executionMode,
836
1281
  selection: {
837
1282
  mode: 'EXPLICIT',
838
1283
  targets: this.targets,
@@ -849,7 +1294,15 @@ class PraxisBulkEditDialogComponent {
849
1294
  .subscribe({
850
1295
  next: (prop) => {
851
1296
  this.proposal.set(prop);
1297
+ const inlineItems = Array.isArray(prop.items)
1298
+ ? prop.items
1299
+ : [];
1300
+ this.proposalItems.set(inlineItems);
1301
+ this.proposalNextCursor.set(null);
852
1302
  this.step.set('PREVIEW');
1303
+ if (inlineItems.length === 0 && this.contract.proposalResultsOperation) {
1304
+ this.loadProposalResults();
1305
+ }
853
1306
  },
854
1307
  error: (err) => {
855
1308
  this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao avaliar alterações em lote.');
@@ -857,6 +1310,37 @@ class PraxisBulkEditDialogComponent {
857
1310
  },
858
1311
  });
859
1312
  }
1313
+ loadProposalResults(cursor) {
1314
+ const prop = this.proposal();
1315
+ if (!prop ||
1316
+ !this.contract?.proposalResultsOperation ||
1317
+ typeof this.bulkClient.getProposalResults !== 'function') {
1318
+ return;
1319
+ }
1320
+ this.loadingProposalResults.set(true);
1321
+ const requestOptions = {
1322
+ endpointKey: this.data.endpointKey,
1323
+ apiUrlEntry: this.data.apiUrlEntry,
1324
+ };
1325
+ this.bulkClient
1326
+ .getProposalResults(this.contract.proposalResultsOperation, prop.proposalId, 50, cursor, requestOptions)
1327
+ .pipe(takeUntilDestroyed(this.destroyRef))
1328
+ .subscribe({
1329
+ next: (page) => {
1330
+ const newItems = Array.isArray(page?.items)
1331
+ ? page.items
1332
+ : Array.isArray(page?.content)
1333
+ ? page.content
1334
+ : [];
1335
+ this.proposalItems.update((prev) => (cursor ? [...prev, ...newItems] : newItems));
1336
+ this.proposalNextCursor.set(page?.nextCursor ?? page?.next ?? null);
1337
+ this.loadingProposalResults.set(false);
1338
+ },
1339
+ error: () => {
1340
+ this.loadingProposalResults.set(false);
1341
+ },
1342
+ });
1343
+ }
860
1344
  onConfirm() {
861
1345
  const prop = this.proposal();
862
1346
  if (!prop)
@@ -864,7 +1348,7 @@ class PraxisBulkEditDialogComponent {
864
1348
  this.step.set('CONFIRMING');
865
1349
  this.errorMessage.set(null);
866
1350
  if (!this.confirmationIdempotencyKey) {
867
- this.confirmationIdempotencyKey = `bulk-conf-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
1351
+ this.confirmationIdempotencyKey = generateBulkIdempotencyKey();
868
1352
  }
869
1353
  const requestOptions = {
870
1354
  endpointKey: this.data.endpointKey,
@@ -877,7 +1361,25 @@ class PraxisBulkEditDialogComponent {
877
1361
  .subscribe({
878
1362
  next: (exec) => {
879
1363
  this.execution.set(exec);
880
- this.step.set('RESULT');
1364
+ const isTerminal = typeof this.bulkClient.isTerminalStatus === 'function'
1365
+ ? this.bulkClient.isTerminalStatus(exec.status)
1366
+ : exec.status !== 'QUEUED' && exec.status !== 'RUNNING';
1367
+ if (!isTerminal && this.contract.executionOperation && typeof this.bulkClient.waitForExecution === 'function') {
1368
+ this.bulkClient
1369
+ .waitForExecution(this.contract.executionOperation, exec.executionId, 1000, 60000, requestOptions)
1370
+ .pipe(takeUntilDestroyed(this.destroyRef))
1371
+ .subscribe({
1372
+ next: (finalExec) => {
1373
+ this.finalizeExecution(finalExec);
1374
+ },
1375
+ error: (err) => {
1376
+ this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao acompanhar execução em lote.');
1377
+ this.step.set('RESULT');
1378
+ },
1379
+ });
1380
+ return;
1381
+ }
1382
+ this.finalizeExecution(exec);
881
1383
  },
882
1384
  error: (err) => {
883
1385
  this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao confirmar alterações em lote.');
@@ -885,6 +1387,86 @@ class PraxisBulkEditDialogComponent {
885
1387
  },
886
1388
  });
887
1389
  }
1390
+ finalizeExecution(exec) {
1391
+ this.execution.set(exec);
1392
+ const inlineItems = Array.isArray(exec.items)
1393
+ ? exec.items
1394
+ : [];
1395
+ this.executionItems.set(inlineItems);
1396
+ this.executionNextCursor.set(null);
1397
+ this.step.set('RESULT');
1398
+ if (inlineItems.length === 0 && this.contract?.resultsOperation) {
1399
+ this.loadExecutionResults();
1400
+ }
1401
+ }
1402
+ loadExecutionResults(cursor) {
1403
+ const exec = this.execution();
1404
+ if (!exec ||
1405
+ !this.contract?.resultsOperation ||
1406
+ typeof this.bulkClient.getExecutionResults !== 'function') {
1407
+ return;
1408
+ }
1409
+ this.loadingExecutionResults.set(true);
1410
+ const requestOptions = {
1411
+ endpointKey: this.data.endpointKey,
1412
+ apiUrlEntry: this.data.apiUrlEntry,
1413
+ };
1414
+ this.bulkClient
1415
+ .getExecutionResults(this.contract.resultsOperation, exec.executionId, 50, cursor, requestOptions)
1416
+ .pipe(takeUntilDestroyed(this.destroyRef))
1417
+ .subscribe({
1418
+ next: (page) => {
1419
+ const newItems = Array.isArray(page?.items)
1420
+ ? page.items
1421
+ : Array.isArray(page?.content)
1422
+ ? page.content
1423
+ : [];
1424
+ this.executionItems.update((prev) => (cursor ? [...prev, ...newItems] : newItems));
1425
+ this.executionNextCursor.set(page?.nextCursor ?? page?.next ?? null);
1426
+ this.loadingExecutionResults.set(false);
1427
+ },
1428
+ error: () => {
1429
+ this.loadingExecutionResults.set(false);
1430
+ },
1431
+ });
1432
+ }
1433
+ canCancelExecution() {
1434
+ const exec = this.execution();
1435
+ if (this.step() !== 'CONFIRMING' || !exec || !this.contract?.cancelOperation) {
1436
+ return false;
1437
+ }
1438
+ const isTerminal = typeof this.bulkClient.isTerminalStatus === 'function'
1439
+ ? this.bulkClient.isTerminalStatus(exec.status)
1440
+ : exec.status !== 'QUEUED' && exec.status !== 'RUNNING';
1441
+ return !isTerminal;
1442
+ }
1443
+ onCancelActiveExecution() {
1444
+ const exec = this.execution();
1445
+ if (!this.canCancelExecution() ||
1446
+ !exec ||
1447
+ !this.contract?.cancelOperation ||
1448
+ typeof this.bulkClient.cancel !== 'function') {
1449
+ return;
1450
+ }
1451
+ this.cancellingExecution.set(true);
1452
+ const requestOptions = {
1453
+ endpointKey: this.data.endpointKey,
1454
+ apiUrlEntry: this.data.apiUrlEntry,
1455
+ };
1456
+ this.bulkClient
1457
+ .cancel(this.contract.cancelOperation, exec.executionId, requestOptions)
1458
+ .pipe(takeUntilDestroyed(this.destroyRef))
1459
+ .subscribe({
1460
+ next: (cancelledExec) => {
1461
+ this.cancellingExecution.set(false);
1462
+ this.finalizeExecution(cancelledExec);
1463
+ },
1464
+ error: (err) => {
1465
+ this.cancellingExecution.set(false);
1466
+ this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao solicitar cancelamento da execução em lote.');
1467
+ },
1468
+ });
1469
+ }
888
1470
  onCloseSuccess() {
889
1471
  this.dialogRef.close({
890
1472
  success: true,
@@ -892,8 +1474,18 @@ class PraxisBulkEditDialogComponent {
892
1474
  });
893
1475
  }
894
1476
  onCancel() {
1477
+ if (this.step() === 'RESULT') {
1478
+ this.onCloseSuccess();
1479
+ return;
1480
+ }
895
1481
  this.dialogRef.close({ success: false });
896
1482
  }
1483
+ onEscape(event) {
1484
+ if (this.step() !== 'CONFIRMING' && this.step() !== 'EVALUATING') {
1485
+ event?.preventDefault();
1486
+ this.onCancel();
1487
+ }
1488
+ }
897
1489
  parseFieldValue(raw, fieldName) {
898
1490
  if (fieldName) {
899
1491
  const type = this.getFieldType(fieldName);
@@ -917,14 +1509,14 @@ class PraxisBulkEditDialogComponent {
917
1509
  return trimmed;
918
1510
  }
919
1511
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisBulkEditDialogComponent, deps: [{ token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
920
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisBulkEditDialogComponent, isStandalone: true, selector: "praxis-bulk-edit-dialog", ngImport: i0, template: `
921
- <div class="praxis-bulk-edit-dialog" role="dialog" aria-labelledby="praxis-bulk-edit-dialog-title">
1512
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisBulkEditDialogComponent, isStandalone: true, selector: "praxis-bulk-edit-dialog", host: { listeners: { "keydown.escape": "onEscape($event)" } }, ngImport: i0, template: `
1513
+ <div class="praxis-bulk-edit-dialog" role="dialog" aria-labelledby="praxis-bulk-edit-dialog-title" aria-describedby="praxis-bulk-edit-dialog-subtitle">
922
1514
  <div class="praxis-bulk-edit-dialog__header">
923
1515
  <div class="praxis-bulk-edit-dialog__title-group">
924
1516
  <mat-icon class="praxis-bulk-edit-dialog__header-icon">edit_note</mat-icon>
925
1517
  <div>
926
1518
  <h2 class="praxis-bulk-edit-dialog__title" id="praxis-bulk-edit-dialog-title">{{ dialogTitle }}</h2>
927
- <p class="praxis-bulk-edit-dialog__subtitle">{{ dialogSubtitle }}</p>
1519
+ <p id="praxis-bulk-edit-dialog-subtitle" class="praxis-bulk-edit-dialog__subtitle">{{ dialogSubtitle }}</p>
928
1520
  </div>
929
1521
  </div>
930
1522
  <button mat-icon-button (click)="onCancel()" [disabled]="step() === 'CONFIRMING'" aria-label="Fechar diálogo">
@@ -972,7 +1564,7 @@ class PraxisBulkEditDialogComponent {
972
1564
  <mat-label>Campo</mat-label>
973
1565
  <mat-select [(ngModel)]="row.field" (ngModelChange)="onFieldSelectionChange(row)">
974
1566
  @for (field of writableFields; track field) {
975
- <mat-option [value]="field">
1567
+ <mat-option [value]="field" [disabled]="isFieldSelectedInOtherRow(field, row)">
976
1568
  {{ getFieldLabel(field) }}
977
1569
  <span class="field-tech-name">({{ field }})</span>
978
1570
  </mat-option>
@@ -983,7 +1575,7 @@ class PraxisBulkEditDialogComponent {
983
1575
  <!-- Seleção do Operador: SET ou CLEAR -->
984
1576
  <mat-form-field appearance="outline" class="operator-select">
985
1577
  <mat-label>Operação</mat-label>
986
- <mat-select [(ngModel)]="row.operator">
1578
+ <mat-select [(ngModel)]="row.operator" (ngModelChange)="errorMessage.set(null)">
987
1579
  <mat-option value="SET">Definir Valor</mat-option>
988
1580
  @if (isFieldClearable(row.field)) {
989
1581
  <mat-option value="CLEAR">Limpar (CLEAR)</mat-option>
@@ -997,19 +1589,25 @@ class PraxisBulkEditDialogComponent {
997
1589
  @case ('currency') {
998
1590
  <mat-form-field appearance="outline" class="value-input">
999
1591
  <mat-label>Novo Valor (R$)</mat-label>
1000
- <input matInput type="number" step="0.01" [(ngModel)]="row.value" placeholder="0.00" />
1592
+ <input matInput type="number" step="0.01" [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)" placeholder="0.00" />
1001
1593
  </mat-form-field>
1002
1594
  }
1003
1595
  @case ('number') {
1004
1596
  <mat-form-field appearance="outline" class="value-input">
1005
1597
  <mat-label>Novo Valor Numérico</mat-label>
1006
- <input matInput type="number" [(ngModel)]="row.value" placeholder="0" />
1598
+ <input matInput type="number" [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)" placeholder="0" />
1599
+ </mat-form-field>
1600
+ }
1601
+ @case ('date') {
1602
+ <mat-form-field appearance="outline" class="value-input">
1603
+ <mat-label>Nova Data</mat-label>
1604
+ <input matInput type="date" [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)" />
1007
1605
  </mat-form-field>
1008
1606
  }
1009
1607
  @case ('boolean') {
1010
1608
  <mat-form-field appearance="outline" class="value-input">
1011
1609
  <mat-label>Novo Valor</mat-label>
1012
- <mat-select [(ngModel)]="row.value">
1610
+ <mat-select [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)">
1013
1611
  <mat-option value="true">Sim (Verdadeiro)</mat-option>
1014
1612
  <mat-option value="false">Não (Falso)</mat-option>
1015
1613
  </mat-select>
@@ -1018,7 +1616,7 @@ class PraxisBulkEditDialogComponent {
1018
1616
  @case ('enum') {
1019
1617
  <mat-form-field appearance="outline" class="value-input">
1020
1618
  <mat-label>Novo Valor</mat-label>
1021
- <mat-select [(ngModel)]="row.value">
1619
+ <mat-select [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)">
1022
1620
  @for (opt of getFieldEnumOptions(row.field); track opt.value) {
1023
1621
  <mat-option [value]="opt.value">{{ opt.label }}</mat-option>
1024
1622
  }
@@ -1028,7 +1626,7 @@ class PraxisBulkEditDialogComponent {
1028
1626
  @default {
1029
1627
  <mat-form-field appearance="outline" class="value-input">
1030
1628
  <mat-label>Novo Valor</mat-label>
1031
- <input matInput [(ngModel)]="row.value" placeholder="Digite o novo valor..." />
1629
+ <input matInput [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)" placeholder="Digite o novo valor..." />
1032
1630
  </mat-form-field>
1033
1631
  }
1034
1632
  }
@@ -1125,6 +1723,33 @@ class PraxisBulkEditDialogComponent {
1125
1723
  }
1126
1724
  </div>
1127
1725
  }
1726
+
1727
+ @if (proposalItems().length > 0) {
1728
+ <div class="praxis-bulk-edit-dialog__diagnostics">
1729
+ <h4 class="diagnostics-title">Avaliação por Registro ({{ proposalItems().length }})</h4>
1730
+ @for (item of proposalItems(); track item.id) {
1731
+ <div class="diagnostic-item" [class.diagnostic-item--error]="item.decision === 'BLOCKED'">
1732
+ <mat-icon>{{ item.decision === 'BLOCKED' ? 'cancel' : 'check_circle' }}</mat-icon>
1733
+ <div class="diagnostic-content">
1734
+ <span class="diagnostic-target">Registro #{{ item.id }}:</span>
1735
+ <span class="diagnostic-message">
1736
+ {{ item.decision === 'BLOCKED' ? (item.diagnostics?.[0]?.message || 'Bloqueado') : 'Elegível' }}
1737
+ </span>
1738
+ </div>
1739
+ </div>
1740
+ }
1741
+ @if (proposalNextCursor()) {
1742
+ <button
1743
+ mat-stroked-button
1744
+ type="button"
1745
+ (click)="loadProposalResults(proposalNextCursor()!)"
1746
+ [disabled]="loadingProposalResults()"
1747
+ >
1748
+ {{ loadingProposalResults() ? 'Carregando...' : 'Carregar mais resultados' }}
1749
+ </button>
1750
+ }
1751
+ </div>
1752
+ }
1128
1753
  </div>
1129
1754
  }
1130
1755
 
@@ -1133,6 +1758,17 @@ class PraxisBulkEditDialogComponent {
1133
1758
  <div class="praxis-bulk-edit-dialog__loading">
1134
1759
  <mat-spinner diameter="40" />
1135
1760
  <p>Gravando atualizações em lote com garantia de idempotência...</p>
1761
+ @if (canCancelExecution()) {
1762
+ <button
1763
+ mat-stroked-button
1764
+ color="warn"
1765
+ type="button"
1766
+ (click)="onCancelActiveExecution()"
1767
+ [disabled]="cancellingExecution()"
1768
+ >
1769
+ {{ cancellingExecution() ? 'Cancelando...' : 'Cancelar Execução' }}
1770
+ </button>
1771
+ }
1136
1772
  </div>
1137
1773
  }
1138
1774
 
@@ -1142,25 +1778,50 @@ class PraxisBulkEditDialogComponent {
1142
1778
  <div class="praxis-bulk-edit-dialog__alert praxis-bulk-edit-dialog__alert--success">
1143
1779
  <mat-icon>verified</mat-icon>
1144
1780
  <div class="alert-content">
1145
- <strong>Atualização concluída com sucesso!</strong>
1146
- <p>Status: Concluído • Transação efetivada e registrada na auditoria.</p>
1781
+ <strong>Atualização concluída com êxito!</strong>
1782
+ <p>Transação homologada e registrada na auditoria corporativa.</p>
1147
1783
  </div>
1148
1784
  </div>
1149
1785
 
1150
1786
  <div class="praxis-bulk-edit-dialog__totals-grid">
1151
1787
  <div class="total-card total-card--executable">
1152
- <span class="total-label">Confirmados</span>
1788
+ <span class="total-label">Efetivados</span>
1153
1789
  <span class="total-value">{{ execution()?.totals?.confirmed }}</span>
1154
1790
  </div>
1155
1791
  <div class="total-card">
1156
- <span class="total-label">Inalterados</span>
1792
+ <span class="total-label">Preservados</span>
1157
1793
  <span class="total-value">{{ execution()?.totals?.unchanged }}</span>
1158
1794
  </div>
1159
1795
  <div class="total-card" [class.total-card--blocked]="(execution()?.totals?.denied ?? 0) > 0">
1160
- <span class="total-label">Recusados</span>
1796
+ <span class="total-label">Rejeitados</span>
1161
1797
  <span class="total-value">{{ execution()?.totals?.denied }}</span>
1162
1798
  </div>
1163
1799
  </div>
1800
+
1801
+ @if (executionItems().length > 0) {
1802
+ <div class="praxis-bulk-edit-dialog__diagnostics">
1803
+ <h4 class="diagnostics-title">Resultado por Registro ({{ executionItems().length }})</h4>
1804
+ @for (item of executionItems(); track item.id) {
1805
+ <div class="diagnostic-item" [class.diagnostic-item--error]="item.status !== 'CONFIRMED' && item.status !== 'UNCHANGED'">
1806
+ <mat-icon>{{ item.status === 'CONFIRMED' || item.status === 'UNCHANGED' ? 'check_circle' : 'cancel' }}</mat-icon>
1807
+ <div class="diagnostic-content">
1808
+ <span class="diagnostic-target">Registro #{{ item.id }}:</span>
1809
+ <span class="diagnostic-message">{{ item.diagnostics?.[0]?.message || item.status }}</span>
1810
+ </div>
1811
+ </div>
1812
+ }
1813
+ @if (executionNextCursor()) {
1814
+ <button
1815
+ mat-stroked-button
1816
+ type="button"
1817
+ (click)="loadExecutionResults(executionNextCursor()!)"
1818
+ [disabled]="loadingExecutionResults()"
1819
+ >
1820
+ {{ loadingExecutionResults() ? 'Carregando...' : 'Carregar mais resultados' }}
1821
+ </button>
1822
+ }
1823
+ </div>
1824
+ }
1164
1825
  </div>
1165
1826
  }
1166
1827
 
@@ -1207,7 +1868,7 @@ class PraxisBulkEditDialogComponent {
1207
1868
  }
1208
1869
  </div>
1209
1870
  </div>
1210
- `, isInline: true, styles: [".cdk-overlay-pane.praxis-bulk-dialog-panel{--mat-dialog-container-color: var(--praxis-surface-card, var(--card, var(--md-sys-color-surface-container, #1e293b)));--mat-dialog-container-shape: 16px;margin:auto!important;display:flex!important;justify-content:center!important;align-items:center!important}.cdk-overlay-pane.praxis-bulk-dialog-panel .mat-mdc-dialog-surface{border-radius:16px;border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))));box-shadow:var(--praxis-theme-elevation, 0 24px 64px rgba(0, 0, 0, .45));overflow:hidden;width:100%}.praxis-bulk-edit-dialog{display:flex;flex-direction:column;max-height:85vh;width:100%;min-width:600px;max-width:680px;margin:0 auto;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)));background:transparent;box-sizing:border-box}.praxis-bulk-edit-dialog__header{display:flex;justify-content:space-between;align-items:flex-start;padding:20px 24px 16px;border-bottom:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}.praxis-bulk-edit-dialog__title-group{display:flex;align-items:flex-start;gap:12px}.praxis-bulk-edit-dialog__header-icon{color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)));font-size:28px;width:28px;height:28px;margin-top:2px}.praxis-bulk-edit-dialog__title{font-size:1.25rem;font-weight:600;margin:0;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)))}.praxis-bulk-edit-dialog__subtitle{font-size:.875rem;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, rgba(148, 163, 184, .8))));margin:4px 0 0}.praxis-bulk-edit-dialog__content{padding:20px 24px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:16px}.praxis-bulk-edit-dialog__targets-badge{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 6%,transparent);border:1px solid color-mix(in srgb,var(--praxis-border-subtle, var(--border, currentColor)) 25%,transparent);border-radius:8px;font-size:.875rem;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__targets-badge mat-icon{font-size:18px;width:18px;height:18px;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.section-title{font-size:1rem;font-weight:600;margin:0;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)))}.praxis-bulk-edit-dialog__empty-box{display:flex;flex-direction:column;align-items:center;padding:32px 16px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 2%,transparent);border:1px dashed var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .25))));border-radius:8px;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, inherit)));gap:8px}.praxis-bulk-edit-dialog__empty-box mat-icon{font-size:32px;width:32px;height:32px;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.field-rows-container{display:flex;flex-direction:column;gap:12px}.field-row{display:flex;align-items:center;gap:12px}.field-select{flex:1.3}.field-tech-name{font-size:.75rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit));margin-left:6px;opacity:.75}.operator-select{flex:.9}.value-input{flex:1.4}.clear-notice{flex:1.4;display:flex;align-items:center;gap:8px;font-size:.8125rem;color:var(--praxis-warning, #d97706);background:color-mix(in srgb,var(--praxis-warning, #d97706) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 30%,transparent);padding:8px 12px;border-radius:6px}.clear-notice mat-icon{font-size:16px;width:16px;height:16px}.praxis-bulk-edit-dialog__section{display:flex;flex-direction:column;gap:20px}.praxis-bulk-edit-dialog__totals-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:0}.total-card{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:96px;padding:18px 20px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 4%,transparent);border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .15))));border-radius:12px;transition:background .2s ease,border-color .2s ease;box-sizing:border-box}.total-card .total-label{font-size:.75rem;text-transform:uppercase;letter-spacing:.06em;font-weight:600;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, rgba(148, 163, 184, .8))));margin-bottom:6px}.total-card .total-value{font-size:1.875rem;font-weight:700;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)));line-height:1;margin:0}.total-card--executable{border-color:color-mix(in srgb,var(--praxis-success, #10b981) 50%,transparent);background:color-mix(in srgb,var(--praxis-success, #10b981) 12%,transparent)}.total-card--executable .total-value{color:var(--praxis-success, #10b981)}.total-card--blocked{border-color:color-mix(in srgb,var(--praxis-error, #ef4444) 50%,transparent);background:color-mix(in srgb,var(--praxis-error, #ef4444) 12%,transparent)}.total-card--blocked .total-value{color:var(--praxis-error, #ef4444)}.praxis-bulk-edit-dialog__alert{display:flex;align-items:flex-start;gap:14px;padding:16px 20px;border-radius:12px;font-size:.875rem;line-height:1.45}.praxis-bulk-edit-dialog__alert mat-icon{font-size:22px;width:22px;height:22px;flex-shrink:0;margin-top:1px}.praxis-bulk-edit-dialog__alert .alert-content{display:flex;flex-direction:column;gap:3px}.praxis-bulk-edit-dialog__alert .alert-content strong{font-size:.9375rem;font-weight:600;color:inherit}.praxis-bulk-edit-dialog__alert .alert-content p{margin:0;font-size:.8125rem;opacity:.9}.praxis-bulk-edit-dialog__alert--error{background:color-mix(in srgb,var(--praxis-error, #ef4444) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-error, #ef4444) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__alert--error mat-icon{color:var(--praxis-error, #ef4444)}.praxis-bulk-edit-dialog__alert--warning{background:color-mix(in srgb,var(--praxis-warning, #d97706) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__alert--warning mat-icon{color:var(--praxis-warning, #d97706)}.praxis-bulk-edit-dialog__alert--success{background:color-mix(in srgb,var(--praxis-success, #10b981) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-success, #10b981) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__alert--success mat-icon{color:var(--praxis-success, #10b981)}.praxis-bulk-edit-dialog__diagnostics{margin-top:12px;padding:12px 16px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 3%,transparent);border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))));border-radius:8px;display:flex;flex-direction:column;gap:8px}.diagnostics-title{font-size:.875rem;font-weight:600;margin:0 0 4px;color:var(--praxis-text-primary, var(--foreground, inherit))}.diagnostic-item{display:flex;align-items:flex-start;gap:8px;font-size:.8125rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.diagnostic-item mat-icon{font-size:16px;width:16px;height:16px;margin-top:2px;flex-shrink:0;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.diagnostic-item--error{color:var(--praxis-error, #ef4444)}.diagnostic-item--error mat-icon{color:var(--praxis-error, #ef4444)}.diagnostic-content{display:flex;flex-wrap:wrap;gap:4px}.diagnostic-target{font-weight:600;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__loading{display:flex;flex-direction:column;align-items:center;padding:40px;gap:16px;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.praxis-bulk-edit-dialog__actions{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding:18px 24px 22px;border-top:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDialogModule }, { 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: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatSelectModule }, { 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: "component", type: i5.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: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.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: MatFormFieldModule }], encapsulation: i0.ViewEncapsulation.None });
1871
+ `, isInline: true, styles: [".cdk-global-overlay-wrapper:has(.praxis-bulk-dialog-panel){display:flex!important;justify-content:center!important;align-items:center!important;padding:24px!important;box-sizing:border-box!important}.cdk-overlay-pane.praxis-bulk-dialog-panel{--mat-dialog-container-color: var(--card, var(--praxis-color-surface, #1e293b)) !important;--mdc-dialog-container-color: var(--card, var(--praxis-color-surface, #1e293b)) !important;--mat-dialog-container-shape: 16px;position:relative!important;inset:auto!important;margin:auto!important;display:flex!important;justify-content:center!important;align-items:center!important}.cdk-overlay-pane.praxis-bulk-dialog-panel .mat-mdc-dialog-surface{border-radius:16px;border:1px solid var(--border, var(--praxis-border-subtle, rgba(148, 163, 184, .2)));box-shadow:0 24px 64px #0000008c;background-color:var(--card, var(--praxis-color-surface, #1e293b))!important;color:var(--foreground, #f8fafc)!important;overflow:hidden;width:100%}.praxis-bulk-edit-dialog{display:flex;flex-direction:column;max-height:85vh;width:100%;min-width:600px;max-width:680px;margin:0 auto;color:var(--foreground, var(--praxis-text-primary, #f8fafc));background:transparent;box-sizing:border-box}.praxis-bulk-edit-dialog__header{display:flex;justify-content:space-between;align-items:flex-start;padding:22px 26px 18px;border-bottom:1px solid var(--border, rgba(148, 163, 184, .15))}.praxis-bulk-edit-dialog__title-group{display:flex;align-items:flex-start;gap:14px}.praxis-bulk-edit-dialog__header-icon{color:var(--primary, var(--praxis-primary, #0284c7));font-size:28px;width:28px;height:28px;margin-top:2px}.praxis-bulk-edit-dialog__title{font-size:1.25rem;font-weight:600;margin:0;line-height:1.3;color:var(--foreground, #f8fafc)}.praxis-bulk-edit-dialog__subtitle{font-size:.875rem;color:var(--muted-foreground, rgba(148, 163, 184, .85));margin:4px 0 0;line-height:1.45}.praxis-bulk-edit-dialog__content{padding:22px 26px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:20px}.praxis-bulk-edit-dialog__targets-badge{display:inline-flex;align-items:center;gap:10px;padding:10px 16px;background:color-mix(in srgb,var(--foreground, #f8fafc) 6%,var(--card, #1e293b));border:1px solid var(--border, rgba(148, 163, 184, .2));border-radius:10px;font-size:.875rem;color:var(--foreground, #f8fafc)}.praxis-bulk-edit-dialog__targets-badge mat-icon{font-size:18px;width:18px;height:18px;color:var(--primary, var(--praxis-primary, #0284c7))}.section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.section-title{font-size:1rem;font-weight:600;margin:0;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)))}.praxis-bulk-edit-dialog__empty-box{display:flex;flex-direction:column;align-items:center;padding:32px 16px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 2%,transparent);border:1px dashed var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .25))));border-radius:8px;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, inherit)));gap:8px}.praxis-bulk-edit-dialog__empty-box mat-icon{font-size:32px;width:32px;height:32px;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.field-rows-container{display:flex;flex-direction:column;gap:12px}.field-row{display:flex;align-items:center;gap:12px}.field-select{flex:1.3}.field-tech-name{font-size:.75rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit));margin-left:6px;opacity:.75}.operator-select{flex:.9}.value-input{flex:1.4}.clear-notice{flex:1.4;display:flex;align-items:center;gap:8px;font-size:.8125rem;color:var(--praxis-warning, #d97706);background:color-mix(in srgb,var(--praxis-warning, #d97706) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 30%,transparent);padding:8px 12px;border-radius:6px}.clear-notice mat-icon{font-size:16px;width:16px;height:16px}.praxis-bulk-edit-dialog__section{display:flex;flex-direction:column;gap:20px}.praxis-bulk-edit-dialog__totals-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:4px 0 12px}.total-card{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:104px;padding:20px 22px;background:color-mix(in srgb,var(--foreground, #f8fafc) 6%,var(--card, #1e293b));border:1px solid var(--border, rgba(148, 163, 184, .2));border-radius:14px;transition:background .2s ease,border-color .2s ease;box-sizing:border-box;gap:8px}.total-card .total-label{font-size:.8125rem;text-transform:uppercase;letter-spacing:.05em;font-weight:600;color:var(--muted-foreground, rgba(148, 163, 184, .85));margin-bottom:2px}.total-card .total-value{font-size:2rem;font-weight:700;color:var(--foreground, #f8fafc);line-height:1;margin:0;letter-spacing:-.02em}.total-card--executable{border-color:color-mix(in srgb,var(--praxis-success, #10b981) 50%,transparent)!important;background:color-mix(in srgb,var(--praxis-success, #10b981) 16%,var(--card, #1e293b))!important}.total-card--executable .total-value{color:var(--praxis-success, #10b981)}.total-card--blocked{border-color:color-mix(in srgb,var(--praxis-error, #ef4444) 50%,transparent)!important;background:color-mix(in srgb,var(--praxis-error, #ef4444) 16%,var(--card, #1e293b))!important}.total-card--blocked .total-value{color:var(--praxis-error, #ef4444)}.praxis-bulk-edit-dialog__alert{display:flex;align-items:flex-start;gap:14px;padding:18px 22px;border-radius:12px;font-size:.875rem;line-height:1.5}.praxis-bulk-edit-dialog__alert mat-icon{font-size:22px;width:22px;height:22px;flex-shrink:0;margin-top:1px}.praxis-bulk-edit-dialog__alert .alert-content{display:flex;flex-direction:column;gap:4px}.praxis-bulk-edit-dialog__alert .alert-content strong{font-size:.9375rem;font-weight:600;color:inherit}.praxis-bulk-edit-dialog__alert .alert-content p{margin:0;font-size:.8125rem;opacity:.9}.praxis-bulk-edit-dialog__alert--error{background:color-mix(in srgb,var(--praxis-error, #ef4444) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-error, #ef4444) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-edit-dialog__alert--error mat-icon{color:var(--praxis-error, #ef4444)}.praxis-bulk-edit-dialog__alert--warning{background:color-mix(in srgb,var(--praxis-warning, #d97706) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-edit-dialog__alert--warning mat-icon{color:var(--praxis-warning, #d97706)}.praxis-bulk-edit-dialog__alert--success{background:color-mix(in srgb,var(--praxis-success, #10b981) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-success, #10b981) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-edit-dialog__alert--success mat-icon{color:var(--praxis-success, #10b981)}.praxis-bulk-edit-dialog__diagnostics{margin-top:12px;padding:12px 16px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 3%,transparent);border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))));border-radius:8px;display:flex;flex-direction:column;gap:8px}.diagnostics-title{font-size:.875rem;font-weight:600;margin:0 0 4px;color:var(--praxis-text-primary, var(--foreground, inherit))}.diagnostic-item{display:flex;align-items:flex-start;gap:8px;font-size:.8125rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.diagnostic-item mat-icon{font-size:16px;width:16px;height:16px;margin-top:2px;flex-shrink:0;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.diagnostic-item--error{color:var(--praxis-error, #ef4444)}.diagnostic-item--error mat-icon{color:var(--praxis-error, #ef4444)}.diagnostic-content{display:flex;flex-wrap:wrap;gap:4px}.diagnostic-target{font-weight:600;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__loading{display:flex;flex-direction:column;align-items:center;padding:40px;gap:16px;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.praxis-bulk-edit-dialog__actions{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding:18px 24px 22px;border-top:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDialogModule }, { 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: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatSelectModule }, { 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: "component", type: i5.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: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.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: MatFormFieldModule }], encapsulation: i0.ViewEncapsulation.None });
1211
1872
  }
1212
1873
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisBulkEditDialogComponent, decorators: [{
1213
1874
  type: Component,
@@ -1222,13 +1883,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1222
1883
  MatInputModule,
1223
1884
  MatFormFieldModule,
1224
1885
  ], template: `
1225
- <div class="praxis-bulk-edit-dialog" role="dialog" aria-labelledby="praxis-bulk-edit-dialog-title">
1886
+ <div class="praxis-bulk-edit-dialog" role="dialog" aria-labelledby="praxis-bulk-edit-dialog-title" aria-describedby="praxis-bulk-edit-dialog-subtitle">
1226
1887
  <div class="praxis-bulk-edit-dialog__header">
1227
1888
  <div class="praxis-bulk-edit-dialog__title-group">
1228
1889
  <mat-icon class="praxis-bulk-edit-dialog__header-icon">edit_note</mat-icon>
1229
1890
  <div>
1230
1891
  <h2 class="praxis-bulk-edit-dialog__title" id="praxis-bulk-edit-dialog-title">{{ dialogTitle }}</h2>
1231
- <p class="praxis-bulk-edit-dialog__subtitle">{{ dialogSubtitle }}</p>
1892
+ <p id="praxis-bulk-edit-dialog-subtitle" class="praxis-bulk-edit-dialog__subtitle">{{ dialogSubtitle }}</p>
1232
1893
  </div>
1233
1894
  </div>
1234
1895
  <button mat-icon-button (click)="onCancel()" [disabled]="step() === 'CONFIRMING'" aria-label="Fechar diálogo">
@@ -1276,7 +1937,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1276
1937
  <mat-label>Campo</mat-label>
1277
1938
  <mat-select [(ngModel)]="row.field" (ngModelChange)="onFieldSelectionChange(row)">
1278
1939
  @for (field of writableFields; track field) {
1279
- <mat-option [value]="field">
1940
+ <mat-option [value]="field" [disabled]="isFieldSelectedInOtherRow(field, row)">
1280
1941
  {{ getFieldLabel(field) }}
1281
1942
  <span class="field-tech-name">({{ field }})</span>
1282
1943
  </mat-option>
@@ -1287,7 +1948,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1287
1948
  <!-- Seleção do Operador: SET ou CLEAR -->
1288
1949
  <mat-form-field appearance="outline" class="operator-select">
1289
1950
  <mat-label>Operação</mat-label>
1290
- <mat-select [(ngModel)]="row.operator">
1951
+ <mat-select [(ngModel)]="row.operator" (ngModelChange)="errorMessage.set(null)">
1291
1952
  <mat-option value="SET">Definir Valor</mat-option>
1292
1953
  @if (isFieldClearable(row.field)) {
1293
1954
  <mat-option value="CLEAR">Limpar (CLEAR)</mat-option>
@@ -1301,19 +1962,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1301
1962
  @case ('currency') {
1302
1963
  <mat-form-field appearance="outline" class="value-input">
1303
1964
  <mat-label>Novo Valor (R$)</mat-label>
1304
- <input matInput type="number" step="0.01" [(ngModel)]="row.value" placeholder="0.00" />
1965
+ <input matInput type="number" step="0.01" [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)" placeholder="0.00" />
1305
1966
  </mat-form-field>
1306
1967
  }
1307
1968
  @case ('number') {
1308
1969
  <mat-form-field appearance="outline" class="value-input">
1309
1970
  <mat-label>Novo Valor Numérico</mat-label>
1310
- <input matInput type="number" [(ngModel)]="row.value" placeholder="0" />
1971
+ <input matInput type="number" [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)" placeholder="0" />
1972
+ </mat-form-field>
1973
+ }
1974
+ @case ('date') {
1975
+ <mat-form-field appearance="outline" class="value-input">
1976
+ <mat-label>Nova Data</mat-label>
1977
+ <input matInput type="date" [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)" />
1311
1978
  </mat-form-field>
1312
1979
  }
1313
1980
  @case ('boolean') {
1314
1981
  <mat-form-field appearance="outline" class="value-input">
1315
1982
  <mat-label>Novo Valor</mat-label>
1316
- <mat-select [(ngModel)]="row.value">
1983
+ <mat-select [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)">
1317
1984
  <mat-option value="true">Sim (Verdadeiro)</mat-option>
1318
1985
  <mat-option value="false">Não (Falso)</mat-option>
1319
1986
  </mat-select>
@@ -1322,7 +1989,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1322
1989
  @case ('enum') {
1323
1990
  <mat-form-field appearance="outline" class="value-input">
1324
1991
  <mat-label>Novo Valor</mat-label>
1325
- <mat-select [(ngModel)]="row.value">
1992
+ <mat-select [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)">
1326
1993
  @for (opt of getFieldEnumOptions(row.field); track opt.value) {
1327
1994
  <mat-option [value]="opt.value">{{ opt.label }}</mat-option>
1328
1995
  }
@@ -1332,7 +1999,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1332
1999
  @default {
1333
2000
  <mat-form-field appearance="outline" class="value-input">
1334
2001
  <mat-label>Novo Valor</mat-label>
1335
- <input matInput [(ngModel)]="row.value" placeholder="Digite o novo valor..." />
2002
+ <input matInput [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)" placeholder="Digite o novo valor..." />
1336
2003
  </mat-form-field>
1337
2004
  }
1338
2005
  }
@@ -1429,6 +2096,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1429
2096
  }
1430
2097
  </div>
1431
2098
  }
2099
+
2100
+ @if (proposalItems().length > 0) {
2101
+ <div class="praxis-bulk-edit-dialog__diagnostics">
2102
+ <h4 class="diagnostics-title">Avaliação por Registro ({{ proposalItems().length }})</h4>
2103
+ @for (item of proposalItems(); track item.id) {
2104
+ <div class="diagnostic-item" [class.diagnostic-item--error]="item.decision === 'BLOCKED'">
2105
+ <mat-icon>{{ item.decision === 'BLOCKED' ? 'cancel' : 'check_circle' }}</mat-icon>
2106
+ <div class="diagnostic-content">
2107
+ <span class="diagnostic-target">Registro #{{ item.id }}:</span>
2108
+ <span class="diagnostic-message">
2109
+ {{ item.decision === 'BLOCKED' ? (item.diagnostics?.[0]?.message || 'Bloqueado') : 'Elegível' }}
2110
+ </span>
2111
+ </div>
2112
+ </div>
2113
+ }
2114
+ @if (proposalNextCursor()) {
2115
+ <button
2116
+ mat-stroked-button
2117
+ type="button"
2118
+ (click)="loadProposalResults(proposalNextCursor()!)"
2119
+ [disabled]="loadingProposalResults()"
2120
+ >
2121
+ {{ loadingProposalResults() ? 'Carregando...' : 'Carregar mais resultados' }}
2122
+ </button>
2123
+ }
2124
+ </div>
2125
+ }
1432
2126
  </div>
1433
2127
  }
1434
2128
 
@@ -1437,6 +2131,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1437
2131
  <div class="praxis-bulk-edit-dialog__loading">
1438
2132
  <mat-spinner diameter="40" />
1439
2133
  <p>Gravando atualizações em lote com garantia de idempotência...</p>
2134
+ @if (canCancelExecution()) {
2135
+ <button
2136
+ mat-stroked-button
2137
+ color="warn"
2138
+ type="button"
2139
+ (click)="onCancelActiveExecution()"
2140
+ [disabled]="cancellingExecution()"
2141
+ >
2142
+ {{ cancellingExecution() ? 'Cancelando...' : 'Cancelar Execução' }}
2143
+ </button>
2144
+ }
1440
2145
  </div>
1441
2146
  }
1442
2147
 
@@ -1446,25 +2151,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1446
2151
  <div class="praxis-bulk-edit-dialog__alert praxis-bulk-edit-dialog__alert--success">
1447
2152
  <mat-icon>verified</mat-icon>
1448
2153
  <div class="alert-content">
1449
- <strong>Atualização concluída com sucesso!</strong>
1450
- <p>Status: Concluído • Transação efetivada e registrada na auditoria.</p>
2154
+ <strong>Atualização concluída com êxito!</strong>
2155
+ <p>Transação homologada e registrada na auditoria corporativa.</p>
1451
2156
  </div>
1452
2157
  </div>
1453
2158
 
1454
2159
  <div class="praxis-bulk-edit-dialog__totals-grid">
1455
2160
  <div class="total-card total-card--executable">
1456
- <span class="total-label">Confirmados</span>
2161
+ <span class="total-label">Efetivados</span>
1457
2162
  <span class="total-value">{{ execution()?.totals?.confirmed }}</span>
1458
2163
  </div>
1459
2164
  <div class="total-card">
1460
- <span class="total-label">Inalterados</span>
2165
+ <span class="total-label">Preservados</span>
1461
2166
  <span class="total-value">{{ execution()?.totals?.unchanged }}</span>
1462
2167
  </div>
1463
2168
  <div class="total-card" [class.total-card--blocked]="(execution()?.totals?.denied ?? 0) > 0">
1464
- <span class="total-label">Recusados</span>
2169
+ <span class="total-label">Rejeitados</span>
1465
2170
  <span class="total-value">{{ execution()?.totals?.denied }}</span>
1466
2171
  </div>
1467
2172
  </div>
2173
+
2174
+ @if (executionItems().length > 0) {
2175
+ <div class="praxis-bulk-edit-dialog__diagnostics">
2176
+ <h4 class="diagnostics-title">Resultado por Registro ({{ executionItems().length }})</h4>
2177
+ @for (item of executionItems(); track item.id) {
2178
+ <div class="diagnostic-item" [class.diagnostic-item--error]="item.status !== 'CONFIRMED' && item.status !== 'UNCHANGED'">
2179
+ <mat-icon>{{ item.status === 'CONFIRMED' || item.status === 'UNCHANGED' ? 'check_circle' : 'cancel' }}</mat-icon>
2180
+ <div class="diagnostic-content">
2181
+ <span class="diagnostic-target">Registro #{{ item.id }}:</span>
2182
+ <span class="diagnostic-message">{{ item.diagnostics?.[0]?.message || item.status }}</span>
2183
+ </div>
2184
+ </div>
2185
+ }
2186
+ @if (executionNextCursor()) {
2187
+ <button
2188
+ mat-stroked-button
2189
+ type="button"
2190
+ (click)="loadExecutionResults(executionNextCursor()!)"
2191
+ [disabled]="loadingExecutionResults()"
2192
+ >
2193
+ {{ loadingExecutionResults() ? 'Carregando...' : 'Carregar mais resultados' }}
2194
+ </button>
2195
+ }
2196
+ </div>
2197
+ }
1468
2198
  </div>
1469
2199
  }
1470
2200
 
@@ -1511,11 +2241,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1511
2241
  }
1512
2242
  </div>
1513
2243
  </div>
1514
- `, styles: [".cdk-overlay-pane.praxis-bulk-dialog-panel{--mat-dialog-container-color: var(--praxis-surface-card, var(--card, var(--md-sys-color-surface-container, #1e293b)));--mat-dialog-container-shape: 16px;margin:auto!important;display:flex!important;justify-content:center!important;align-items:center!important}.cdk-overlay-pane.praxis-bulk-dialog-panel .mat-mdc-dialog-surface{border-radius:16px;border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))));box-shadow:var(--praxis-theme-elevation, 0 24px 64px rgba(0, 0, 0, .45));overflow:hidden;width:100%}.praxis-bulk-edit-dialog{display:flex;flex-direction:column;max-height:85vh;width:100%;min-width:600px;max-width:680px;margin:0 auto;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)));background:transparent;box-sizing:border-box}.praxis-bulk-edit-dialog__header{display:flex;justify-content:space-between;align-items:flex-start;padding:20px 24px 16px;border-bottom:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}.praxis-bulk-edit-dialog__title-group{display:flex;align-items:flex-start;gap:12px}.praxis-bulk-edit-dialog__header-icon{color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)));font-size:28px;width:28px;height:28px;margin-top:2px}.praxis-bulk-edit-dialog__title{font-size:1.25rem;font-weight:600;margin:0;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)))}.praxis-bulk-edit-dialog__subtitle{font-size:.875rem;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, rgba(148, 163, 184, .8))));margin:4px 0 0}.praxis-bulk-edit-dialog__content{padding:20px 24px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:16px}.praxis-bulk-edit-dialog__targets-badge{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 6%,transparent);border:1px solid color-mix(in srgb,var(--praxis-border-subtle, var(--border, currentColor)) 25%,transparent);border-radius:8px;font-size:.875rem;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__targets-badge mat-icon{font-size:18px;width:18px;height:18px;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.section-title{font-size:1rem;font-weight:600;margin:0;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)))}.praxis-bulk-edit-dialog__empty-box{display:flex;flex-direction:column;align-items:center;padding:32px 16px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 2%,transparent);border:1px dashed var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .25))));border-radius:8px;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, inherit)));gap:8px}.praxis-bulk-edit-dialog__empty-box mat-icon{font-size:32px;width:32px;height:32px;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.field-rows-container{display:flex;flex-direction:column;gap:12px}.field-row{display:flex;align-items:center;gap:12px}.field-select{flex:1.3}.field-tech-name{font-size:.75rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit));margin-left:6px;opacity:.75}.operator-select{flex:.9}.value-input{flex:1.4}.clear-notice{flex:1.4;display:flex;align-items:center;gap:8px;font-size:.8125rem;color:var(--praxis-warning, #d97706);background:color-mix(in srgb,var(--praxis-warning, #d97706) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 30%,transparent);padding:8px 12px;border-radius:6px}.clear-notice mat-icon{font-size:16px;width:16px;height:16px}.praxis-bulk-edit-dialog__section{display:flex;flex-direction:column;gap:20px}.praxis-bulk-edit-dialog__totals-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:0}.total-card{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:96px;padding:18px 20px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 4%,transparent);border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .15))));border-radius:12px;transition:background .2s ease,border-color .2s ease;box-sizing:border-box}.total-card .total-label{font-size:.75rem;text-transform:uppercase;letter-spacing:.06em;font-weight:600;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, rgba(148, 163, 184, .8))));margin-bottom:6px}.total-card .total-value{font-size:1.875rem;font-weight:700;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)));line-height:1;margin:0}.total-card--executable{border-color:color-mix(in srgb,var(--praxis-success, #10b981) 50%,transparent);background:color-mix(in srgb,var(--praxis-success, #10b981) 12%,transparent)}.total-card--executable .total-value{color:var(--praxis-success, #10b981)}.total-card--blocked{border-color:color-mix(in srgb,var(--praxis-error, #ef4444) 50%,transparent);background:color-mix(in srgb,var(--praxis-error, #ef4444) 12%,transparent)}.total-card--blocked .total-value{color:var(--praxis-error, #ef4444)}.praxis-bulk-edit-dialog__alert{display:flex;align-items:flex-start;gap:14px;padding:16px 20px;border-radius:12px;font-size:.875rem;line-height:1.45}.praxis-bulk-edit-dialog__alert mat-icon{font-size:22px;width:22px;height:22px;flex-shrink:0;margin-top:1px}.praxis-bulk-edit-dialog__alert .alert-content{display:flex;flex-direction:column;gap:3px}.praxis-bulk-edit-dialog__alert .alert-content strong{font-size:.9375rem;font-weight:600;color:inherit}.praxis-bulk-edit-dialog__alert .alert-content p{margin:0;font-size:.8125rem;opacity:.9}.praxis-bulk-edit-dialog__alert--error{background:color-mix(in srgb,var(--praxis-error, #ef4444) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-error, #ef4444) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__alert--error mat-icon{color:var(--praxis-error, #ef4444)}.praxis-bulk-edit-dialog__alert--warning{background:color-mix(in srgb,var(--praxis-warning, #d97706) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__alert--warning mat-icon{color:var(--praxis-warning, #d97706)}.praxis-bulk-edit-dialog__alert--success{background:color-mix(in srgb,var(--praxis-success, #10b981) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-success, #10b981) 40%,transparent);color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__alert--success mat-icon{color:var(--praxis-success, #10b981)}.praxis-bulk-edit-dialog__diagnostics{margin-top:12px;padding:12px 16px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 3%,transparent);border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))));border-radius:8px;display:flex;flex-direction:column;gap:8px}.diagnostics-title{font-size:.875rem;font-weight:600;margin:0 0 4px;color:var(--praxis-text-primary, var(--foreground, inherit))}.diagnostic-item{display:flex;align-items:flex-start;gap:8px;font-size:.8125rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.diagnostic-item mat-icon{font-size:16px;width:16px;height:16px;margin-top:2px;flex-shrink:0;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.diagnostic-item--error{color:var(--praxis-error, #ef4444)}.diagnostic-item--error mat-icon{color:var(--praxis-error, #ef4444)}.diagnostic-content{display:flex;flex-wrap:wrap;gap:4px}.diagnostic-target{font-weight:600;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__loading{display:flex;flex-direction:column;align-items:center;padding:40px;gap:16px;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.praxis-bulk-edit-dialog__actions{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding:18px 24px 22px;border-top:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}\n"] }]
2244
+ `, styles: [".cdk-global-overlay-wrapper:has(.praxis-bulk-dialog-panel){display:flex!important;justify-content:center!important;align-items:center!important;padding:24px!important;box-sizing:border-box!important}.cdk-overlay-pane.praxis-bulk-dialog-panel{--mat-dialog-container-color: var(--card, var(--praxis-color-surface, #1e293b)) !important;--mdc-dialog-container-color: var(--card, var(--praxis-color-surface, #1e293b)) !important;--mat-dialog-container-shape: 16px;position:relative!important;inset:auto!important;margin:auto!important;display:flex!important;justify-content:center!important;align-items:center!important}.cdk-overlay-pane.praxis-bulk-dialog-panel .mat-mdc-dialog-surface{border-radius:16px;border:1px solid var(--border, var(--praxis-border-subtle, rgba(148, 163, 184, .2)));box-shadow:0 24px 64px #0000008c;background-color:var(--card, var(--praxis-color-surface, #1e293b))!important;color:var(--foreground, #f8fafc)!important;overflow:hidden;width:100%}.praxis-bulk-edit-dialog{display:flex;flex-direction:column;max-height:85vh;width:100%;min-width:600px;max-width:680px;margin:0 auto;color:var(--foreground, var(--praxis-text-primary, #f8fafc));background:transparent;box-sizing:border-box}.praxis-bulk-edit-dialog__header{display:flex;justify-content:space-between;align-items:flex-start;padding:22px 26px 18px;border-bottom:1px solid var(--border, rgba(148, 163, 184, .15))}.praxis-bulk-edit-dialog__title-group{display:flex;align-items:flex-start;gap:14px}.praxis-bulk-edit-dialog__header-icon{color:var(--primary, var(--praxis-primary, #0284c7));font-size:28px;width:28px;height:28px;margin-top:2px}.praxis-bulk-edit-dialog__title{font-size:1.25rem;font-weight:600;margin:0;line-height:1.3;color:var(--foreground, #f8fafc)}.praxis-bulk-edit-dialog__subtitle{font-size:.875rem;color:var(--muted-foreground, rgba(148, 163, 184, .85));margin:4px 0 0;line-height:1.45}.praxis-bulk-edit-dialog__content{padding:22px 26px;overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:20px}.praxis-bulk-edit-dialog__targets-badge{display:inline-flex;align-items:center;gap:10px;padding:10px 16px;background:color-mix(in srgb,var(--foreground, #f8fafc) 6%,var(--card, #1e293b));border:1px solid var(--border, rgba(148, 163, 184, .2));border-radius:10px;font-size:.875rem;color:var(--foreground, #f8fafc)}.praxis-bulk-edit-dialog__targets-badge mat-icon{font-size:18px;width:18px;height:18px;color:var(--primary, var(--praxis-primary, #0284c7))}.section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.section-title{font-size:1rem;font-weight:600;margin:0;color:var(--praxis-text-primary, var(--foreground, var(--md-sys-color-on-surface, inherit)))}.praxis-bulk-edit-dialog__empty-box{display:flex;flex-direction:column;align-items:center;padding:32px 16px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 2%,transparent);border:1px dashed var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .25))));border-radius:8px;color:var(--praxis-text-muted, var(--muted-foreground, var(--md-sys-color-on-surface-variant, inherit)));gap:8px}.praxis-bulk-edit-dialog__empty-box mat-icon{font-size:32px;width:32px;height:32px;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.field-rows-container{display:flex;flex-direction:column;gap:12px}.field-row{display:flex;align-items:center;gap:12px}.field-select{flex:1.3}.field-tech-name{font-size:.75rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit));margin-left:6px;opacity:.75}.operator-select{flex:.9}.value-input{flex:1.4}.clear-notice{flex:1.4;display:flex;align-items:center;gap:8px;font-size:.8125rem;color:var(--praxis-warning, #d97706);background:color-mix(in srgb,var(--praxis-warning, #d97706) 12%,transparent);border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 30%,transparent);padding:8px 12px;border-radius:6px}.clear-notice mat-icon{font-size:16px;width:16px;height:16px}.praxis-bulk-edit-dialog__section{display:flex;flex-direction:column;gap:20px}.praxis-bulk-edit-dialog__totals-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:4px 0 12px}.total-card{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:104px;padding:20px 22px;background:color-mix(in srgb,var(--foreground, #f8fafc) 6%,var(--card, #1e293b));border:1px solid var(--border, rgba(148, 163, 184, .2));border-radius:14px;transition:background .2s ease,border-color .2s ease;box-sizing:border-box;gap:8px}.total-card .total-label{font-size:.8125rem;text-transform:uppercase;letter-spacing:.05em;font-weight:600;color:var(--muted-foreground, rgba(148, 163, 184, .85));margin-bottom:2px}.total-card .total-value{font-size:2rem;font-weight:700;color:var(--foreground, #f8fafc);line-height:1;margin:0;letter-spacing:-.02em}.total-card--executable{border-color:color-mix(in srgb,var(--praxis-success, #10b981) 50%,transparent)!important;background:color-mix(in srgb,var(--praxis-success, #10b981) 16%,var(--card, #1e293b))!important}.total-card--executable .total-value{color:var(--praxis-success, #10b981)}.total-card--blocked{border-color:color-mix(in srgb,var(--praxis-error, #ef4444) 50%,transparent)!important;background:color-mix(in srgb,var(--praxis-error, #ef4444) 16%,var(--card, #1e293b))!important}.total-card--blocked .total-value{color:var(--praxis-error, #ef4444)}.praxis-bulk-edit-dialog__alert{display:flex;align-items:flex-start;gap:14px;padding:18px 22px;border-radius:12px;font-size:.875rem;line-height:1.5}.praxis-bulk-edit-dialog__alert mat-icon{font-size:22px;width:22px;height:22px;flex-shrink:0;margin-top:1px}.praxis-bulk-edit-dialog__alert .alert-content{display:flex;flex-direction:column;gap:4px}.praxis-bulk-edit-dialog__alert .alert-content strong{font-size:.9375rem;font-weight:600;color:inherit}.praxis-bulk-edit-dialog__alert .alert-content p{margin:0;font-size:.8125rem;opacity:.9}.praxis-bulk-edit-dialog__alert--error{background:color-mix(in srgb,var(--praxis-error, #ef4444) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-error, #ef4444) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-edit-dialog__alert--error mat-icon{color:var(--praxis-error, #ef4444)}.praxis-bulk-edit-dialog__alert--warning{background:color-mix(in srgb,var(--praxis-warning, #d97706) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-warning, #d97706) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-edit-dialog__alert--warning mat-icon{color:var(--praxis-warning, #d97706)}.praxis-bulk-edit-dialog__alert--success{background:color-mix(in srgb,var(--praxis-success, #10b981) 16%,var(--card, #1e293b));border:1px solid color-mix(in srgb,var(--praxis-success, #10b981) 45%,transparent);color:var(--foreground, #f8fafc)}.praxis-bulk-edit-dialog__alert--success mat-icon{color:var(--praxis-success, #10b981)}.praxis-bulk-edit-dialog__diagnostics{margin-top:12px;padding:12px 16px;background:color-mix(in srgb,var(--praxis-text-primary, var(--foreground, currentColor)) 3%,transparent);border:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))));border-radius:8px;display:flex;flex-direction:column;gap:8px}.diagnostics-title{font-size:.875rem;font-weight:600;margin:0 0 4px;color:var(--praxis-text-primary, var(--foreground, inherit))}.diagnostic-item{display:flex;align-items:flex-start;gap:8px;font-size:.8125rem;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.diagnostic-item mat-icon{font-size:16px;width:16px;height:16px;margin-top:2px;flex-shrink:0;color:var(--praxis-primary, var(--primary, var(--md-sys-color-primary, #0284c7)))}.diagnostic-item--error{color:var(--praxis-error, #ef4444)}.diagnostic-item--error mat-icon{color:var(--praxis-error, #ef4444)}.diagnostic-content{display:flex;flex-wrap:wrap;gap:4px}.diagnostic-target{font-weight:600;color:var(--praxis-text-primary, var(--foreground, inherit))}.praxis-bulk-edit-dialog__loading{display:flex;flex-direction:column;align-items:center;padding:40px;gap:16px;color:var(--praxis-text-muted, var(--muted-foreground, inherit))}.praxis-bulk-edit-dialog__actions{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding:18px 24px 22px;border-top:1px solid var(--praxis-border-subtle, var(--border, var(--md-sys-color-outline-variant, rgba(148, 163, 184, .2))))}\n"] }]
1515
2245
  }], ctorParameters: () => [{ type: undefined, decorators: [{
1516
2246
  type: Inject,
1517
2247
  args: [MAT_DIALOG_DATA]
1518
- }] }] });
2248
+ }] }], propDecorators: { onEscape: [{
2249
+ type: HostListener,
2250
+ args: ['keydown.escape', ['$event']]
2251
+ }] } });
1519
2252
 
1520
2253
  function formatKpiValue(value, format) {
1521
2254
  if (value === null || value === undefined || value === '') {
@@ -1585,6 +2318,7 @@ class PraxisKpiBandComponent {
1585
2318
  statsError = new EventEmitter();
1586
2319
  refreshClick = new EventEmitter();
1587
2320
  http = inject(HttpClient);
2321
+ apiUrl = inject(API_URL, { optional: true });
1588
2322
  cdr = inject(ChangeDetectorRef);
1589
2323
  destroyRef = inject(DestroyRef);
1590
2324
  evaluator = new SafeExpressionEvaluator({
@@ -1595,6 +2329,8 @@ class PraxisKpiBandComponent {
1595
2329
  evaluatedCards = [];
1596
2330
  fetchedData = {};
1597
2331
  skeletonPlaceholders = [1, 2, 3, 4];
2332
+ initialized = false;
2333
+ fetchSub;
1598
2334
  get isVisible() {
1599
2335
  if (!this.config || this.config.enabled === false)
1600
2336
  return false;
@@ -1617,12 +2353,16 @@ class PraxisKpiBandComponent {
1617
2353
  return gap || '1rem';
1618
2354
  }
1619
2355
  ngOnInit() {
2356
+ this.initialized = true;
1620
2357
  if (this.config?.cards?.length) {
1621
2358
  this.skeletonPlaceholders = Array.from({ length: this.config.cards.length }, (_, i) => i + 1);
1622
2359
  }
1623
2360
  this.refresh();
1624
2361
  }
1625
2362
  ngOnChanges(changes) {
2363
+ if (!this.initialized) {
2364
+ return;
2365
+ }
1626
2366
  if (changes['config'] || changes['dataContext']) {
1627
2367
  if (changes['config'] && this.config?.source !== changes['config'].previousValue?.source) {
1628
2368
  this.refresh();
@@ -1644,17 +2384,35 @@ class PraxisKpiBandComponent {
1644
2384
  this.recomputeCards();
1645
2385
  }
1646
2386
  }
2387
+ resolveSourceUrl(rawSource) {
2388
+ const trimmed = rawSource.trim();
2389
+ if (!trimmed || trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
2390
+ return trimmed;
2391
+ }
2392
+ const defaultEntry = this.apiUrl?.['default'] || (this.apiUrl ? Object.values(this.apiUrl)[0] : undefined);
2393
+ const base = defaultEntry ? buildApiUrl(defaultEntry).replace(/\/+$/, '') : '';
2394
+ if (!base) {
2395
+ return trimmed;
2396
+ }
2397
+ if (trimmed.startsWith('/api/') && base.endsWith('/api')) {
2398
+ return `${base.slice(0, -4)}${trimmed}`;
2399
+ }
2400
+ const cleanPath = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
2401
+ return `${base}${cleanPath}`;
2402
+ }
1647
2403
  fetchFromSource() {
1648
2404
  const source = this.config?.source;
1649
2405
  if (!source)
1650
2406
  return;
2407
+ this.fetchSub?.unsubscribe();
1651
2408
  this.isLoading = true;
1652
2409
  this.cdr.markForCheck();
2410
+ const effectiveUrl = this.resolveSourceUrl(source);
1653
2411
  const method = this.config?.method || 'GET';
1654
2412
  const request$ = method.toUpperCase() === 'POST'
1655
- ? this.http.post(source, this.config?.payload || {})
1656
- : this.http.get(source);
1657
- request$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
2413
+ ? this.http.post(effectiveUrl, this.config?.payload || {})
2414
+ : this.http.get(effectiveUrl);
2415
+ this.fetchSub = request$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
1658
2416
  next: (res) => {
1659
2417
  const payload = res?.data ?? res ?? {};
1660
2418
  this.fetchedData = typeof payload === 'object' ? payload : { value: payload };
@@ -1801,7 +2559,7 @@ class PraxisKpiBandComponent {
1801
2559
  </ng-template>
1802
2560
  </div>
1803
2561
  </div>
1804
- `, isInline: true, styles: [".praxis-kpi-band{display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;width:100%;box-sizing:border-box}.praxis-kpi-band-header{display:flex;align-items:center;justify-content:space-between;gap:1rem}.praxis-kpi-band-title{margin:0;font-size:1rem;font-weight:600;color:var(--praxis-text-primary, #0f172a)}.praxis-kpi-band-subtitle{margin:.125rem 0 0;font-size:.8125rem;color:var(--praxis-text-muted, #64748b)}.praxis-kpi-band-grid{display:grid;grid-template-columns:repeat(var(--praxis-kpi-columns, auto-fit),minmax(200px,1fr));gap:var(--praxis-kpi-gap, 1rem);width:100%}.praxis-kpi-card{display:flex;flex-direction:column;justify-content:space-between;padding:1rem 1.25rem;border-radius:.75rem;background:var(--praxis-surface-card, #ffffff);border:1px solid var(--praxis-border-subtle, #e2e8f0);box-shadow:0 1px 3px #0000000a;transition:transform .15s ease,box-shadow .15s ease,border-color .15s ease;position:relative;overflow:hidden;min-height:5.5rem;box-sizing:border-box}.praxis-kpi-card.interactive{cursor:pointer}.praxis-kpi-card.interactive:hover{transform:translateY(-2px);box-shadow:0 4px 12px #00000014}.praxis-kpi-card.interactive:focus-visible{outline:2px solid var(--praxis-primary, #0284c7);outline-offset:2px}.praxis-kpi-card-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:.5rem}.praxis-kpi-card-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--praxis-text-muted, #64748b)}.praxis-kpi-card-icon{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:.5rem}.praxis-kpi-card-icon mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem}.praxis-kpi-card-value{font-size:1.625rem;font-weight:700;line-height:1.2;letter-spacing:-.02em;font-feature-settings:\"tnum\" on,\"lnum\" on;color:var(--praxis-text-primary, #0f172a)}.praxis-kpi-card-caption{font-size:.75rem;color:var(--praxis-text-muted, #64748b);margin-top:.375rem}.praxis-kpi-card-footnote{font-size:.7rem;color:var(--praxis-text-secondary, #94a3b8);margin-top:.25rem}.tone-info{border-left:4px solid var(--praxis-info, #0284c7)}.tone-info .praxis-kpi-card-icon{background:#f0f9ff;color:#0284c7}.tone-success{border-left:4px solid var(--praxis-success, #059669)}.tone-success .praxis-kpi-card-icon{background:#ecfdf5;color:#059669}.tone-warning{border-left:4px solid var(--praxis-warning, #d97706)}.tone-warning .praxis-kpi-card-icon{background:#fffbeb;color:#d97706}.tone-error,.tone-danger{border-left:4px solid var(--praxis-error, #dc2626)}.tone-error .praxis-kpi-card-icon,.tone-danger .praxis-kpi-card-icon{background:#fef2f2;color:#dc2626}.tone-neutral{border-left:4px solid var(--praxis-neutral, #94a3b8)}.tone-neutral .praxis-kpi-card-icon{background:#f8fafc;color:#64748b}.praxis-kpi-card-skeleton{pointer-events:none}.praxis-kpi-skeleton-line{background:linear-gradient(90deg,#f1f5f9 25%,#e2e8f0,#f1f5f9 75%);background-size:200% 100%;animation:praxis-shimmer 1.5s infinite;border-radius:.25rem}.praxis-kpi-skeleton-line.label{width:40%;height:.75rem;margin-bottom:.75rem}.praxis-kpi-skeleton-line.value{width:65%;height:1.5rem;margin-bottom:.5rem}.praxis-kpi-skeleton-line.caption{width:50%;height:.65rem}@keyframes praxis-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2562
+ `, isInline: true, styles: [".praxis-kpi-band{display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;width:100%;box-sizing:border-box}.praxis-kpi-band-header{display:flex;align-items:center;justify-content:space-between;gap:1rem}.praxis-kpi-band-title{margin:0;font-size:1rem;font-weight:600;color:var(--praxis-text-primary, #0f172a)}.praxis-kpi-band-subtitle{margin:.125rem 0 0;font-size:.8125rem;color:var(--praxis-text-muted, #64748b)}.praxis-kpi-band-grid{display:grid;grid-template-columns:repeat(var(--praxis-kpi-columns, auto-fit),minmax(200px,1fr));gap:var(--praxis-kpi-gap, 1rem);width:100%}.praxis-kpi-card{display:flex;flex-direction:column;justify-content:space-between;padding:1rem 1.25rem;border-radius:.75rem;background:var(--praxis-surface-card, #ffffff);border:1px solid var(--praxis-border-subtle, #e2e8f0);box-shadow:0 1px 3px #0000000a;transition:transform .15s ease,box-shadow .15s ease,border-color .15s ease;position:relative;overflow:hidden;min-height:5.5rem;box-sizing:border-box}.praxis-kpi-card.interactive{cursor:pointer}.praxis-kpi-card.interactive:hover{transform:translateY(-2px);box-shadow:0 4px 12px #00000014}.praxis-kpi-card.interactive:focus-visible{outline:2px solid var(--praxis-primary, #0284c7);outline-offset:2px}.praxis-kpi-card-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:.5rem}.praxis-kpi-card-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--praxis-text-muted, #64748b)}.praxis-kpi-card-icon{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:.5rem}.praxis-kpi-card-icon mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem}.praxis-kpi-card-value{font-size:1.625rem;font-weight:700;line-height:1.2;letter-spacing:-.02em;font-feature-settings:\"tnum\" on,\"lnum\" on;color:var(--praxis-text-primary, #0f172a)}.praxis-kpi-card-caption{font-size:.75rem;color:var(--praxis-text-muted, #64748b);margin-top:.375rem}.praxis-kpi-card-footnote{font-size:.7rem;color:var(--praxis-text-secondary, #94a3b8);margin-top:.25rem}.tone-info{border-left:4px solid var(--praxis-info, #0284c7)}.tone-info .praxis-kpi-card-icon{background:color-mix(in srgb,var(--praxis-info, #0284c7) 14%,transparent);color:var(--praxis-info, #0284c7)}.tone-success{border-left:4px solid var(--praxis-success, #059669)}.tone-success .praxis-kpi-card-icon{background:color-mix(in srgb,var(--praxis-success, #059669) 14%,transparent);color:var(--praxis-success, #059669)}.tone-warning{border-left:4px solid var(--praxis-warning, #d97706)}.tone-warning .praxis-kpi-card-icon{background:color-mix(in srgb,var(--praxis-warning, #d97706) 14%,transparent);color:var(--praxis-warning, #d97706)}.tone-error,.tone-danger{border-left:4px solid var(--praxis-error, #dc2626)}.tone-error .praxis-kpi-card-icon,.tone-danger .praxis-kpi-card-icon{background:color-mix(in srgb,var(--praxis-error, #dc2626) 14%,transparent);color:var(--praxis-error, #dc2626)}.tone-neutral{border-left:4px solid var(--praxis-neutral, #94a3b8)}.tone-neutral .praxis-kpi-card-icon{background:color-mix(in srgb,var(--praxis-neutral, #94a3b8) 14%,transparent);color:var(--praxis-text-secondary, #64748b)}.praxis-kpi-card-skeleton{pointer-events:none}.praxis-kpi-skeleton-line{background:linear-gradient(90deg,color-mix(in srgb,var(--praxis-text-secondary, #94a3b8) 12%,transparent) 25%,color-mix(in srgb,var(--praxis-text-secondary, #94a3b8) 22%,transparent),color-mix(in srgb,var(--praxis-text-secondary, #94a3b8) 12%,transparent) 75%);background-size:200% 100%;animation:praxis-shimmer 1.5s infinite;border-radius:.25rem}.praxis-kpi-skeleton-line.label{width:40%;height:.75rem;margin-bottom:.75rem}.praxis-kpi-skeleton-line.value{width:65%;height:1.5rem;margin-bottom:.5rem}.praxis-kpi-skeleton-line.caption{width:50%;height:.65rem}@keyframes praxis-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1805
2563
  }
1806
2564
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisKpiBandComponent, decorators: [{
1807
2565
  type: Component,
@@ -1876,7 +2634,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1876
2634
  </ng-template>
1877
2635
  </div>
1878
2636
  </div>
1879
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".praxis-kpi-band{display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;width:100%;box-sizing:border-box}.praxis-kpi-band-header{display:flex;align-items:center;justify-content:space-between;gap:1rem}.praxis-kpi-band-title{margin:0;font-size:1rem;font-weight:600;color:var(--praxis-text-primary, #0f172a)}.praxis-kpi-band-subtitle{margin:.125rem 0 0;font-size:.8125rem;color:var(--praxis-text-muted, #64748b)}.praxis-kpi-band-grid{display:grid;grid-template-columns:repeat(var(--praxis-kpi-columns, auto-fit),minmax(200px,1fr));gap:var(--praxis-kpi-gap, 1rem);width:100%}.praxis-kpi-card{display:flex;flex-direction:column;justify-content:space-between;padding:1rem 1.25rem;border-radius:.75rem;background:var(--praxis-surface-card, #ffffff);border:1px solid var(--praxis-border-subtle, #e2e8f0);box-shadow:0 1px 3px #0000000a;transition:transform .15s ease,box-shadow .15s ease,border-color .15s ease;position:relative;overflow:hidden;min-height:5.5rem;box-sizing:border-box}.praxis-kpi-card.interactive{cursor:pointer}.praxis-kpi-card.interactive:hover{transform:translateY(-2px);box-shadow:0 4px 12px #00000014}.praxis-kpi-card.interactive:focus-visible{outline:2px solid var(--praxis-primary, #0284c7);outline-offset:2px}.praxis-kpi-card-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:.5rem}.praxis-kpi-card-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--praxis-text-muted, #64748b)}.praxis-kpi-card-icon{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:.5rem}.praxis-kpi-card-icon mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem}.praxis-kpi-card-value{font-size:1.625rem;font-weight:700;line-height:1.2;letter-spacing:-.02em;font-feature-settings:\"tnum\" on,\"lnum\" on;color:var(--praxis-text-primary, #0f172a)}.praxis-kpi-card-caption{font-size:.75rem;color:var(--praxis-text-muted, #64748b);margin-top:.375rem}.praxis-kpi-card-footnote{font-size:.7rem;color:var(--praxis-text-secondary, #94a3b8);margin-top:.25rem}.tone-info{border-left:4px solid var(--praxis-info, #0284c7)}.tone-info .praxis-kpi-card-icon{background:#f0f9ff;color:#0284c7}.tone-success{border-left:4px solid var(--praxis-success, #059669)}.tone-success .praxis-kpi-card-icon{background:#ecfdf5;color:#059669}.tone-warning{border-left:4px solid var(--praxis-warning, #d97706)}.tone-warning .praxis-kpi-card-icon{background:#fffbeb;color:#d97706}.tone-error,.tone-danger{border-left:4px solid var(--praxis-error, #dc2626)}.tone-error .praxis-kpi-card-icon,.tone-danger .praxis-kpi-card-icon{background:#fef2f2;color:#dc2626}.tone-neutral{border-left:4px solid var(--praxis-neutral, #94a3b8)}.tone-neutral .praxis-kpi-card-icon{background:#f8fafc;color:#64748b}.praxis-kpi-card-skeleton{pointer-events:none}.praxis-kpi-skeleton-line{background:linear-gradient(90deg,#f1f5f9 25%,#e2e8f0,#f1f5f9 75%);background-size:200% 100%;animation:praxis-shimmer 1.5s infinite;border-radius:.25rem}.praxis-kpi-skeleton-line.label{width:40%;height:.75rem;margin-bottom:.75rem}.praxis-kpi-skeleton-line.value{width:65%;height:1.5rem;margin-bottom:.5rem}.praxis-kpi-skeleton-line.caption{width:50%;height:.65rem}@keyframes praxis-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}\n"] }]
2637
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".praxis-kpi-band{display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;width:100%;box-sizing:border-box}.praxis-kpi-band-header{display:flex;align-items:center;justify-content:space-between;gap:1rem}.praxis-kpi-band-title{margin:0;font-size:1rem;font-weight:600;color:var(--praxis-text-primary, #0f172a)}.praxis-kpi-band-subtitle{margin:.125rem 0 0;font-size:.8125rem;color:var(--praxis-text-muted, #64748b)}.praxis-kpi-band-grid{display:grid;grid-template-columns:repeat(var(--praxis-kpi-columns, auto-fit),minmax(200px,1fr));gap:var(--praxis-kpi-gap, 1rem);width:100%}.praxis-kpi-card{display:flex;flex-direction:column;justify-content:space-between;padding:1rem 1.25rem;border-radius:.75rem;background:var(--praxis-surface-card, #ffffff);border:1px solid var(--praxis-border-subtle, #e2e8f0);box-shadow:0 1px 3px #0000000a;transition:transform .15s ease,box-shadow .15s ease,border-color .15s ease;position:relative;overflow:hidden;min-height:5.5rem;box-sizing:border-box}.praxis-kpi-card.interactive{cursor:pointer}.praxis-kpi-card.interactive:hover{transform:translateY(-2px);box-shadow:0 4px 12px #00000014}.praxis-kpi-card.interactive:focus-visible{outline:2px solid var(--praxis-primary, #0284c7);outline-offset:2px}.praxis-kpi-card-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:.5rem}.praxis-kpi-card-label{font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--praxis-text-muted, #64748b)}.praxis-kpi-card-icon{display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:.5rem}.praxis-kpi-card-icon mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem}.praxis-kpi-card-value{font-size:1.625rem;font-weight:700;line-height:1.2;letter-spacing:-.02em;font-feature-settings:\"tnum\" on,\"lnum\" on;color:var(--praxis-text-primary, #0f172a)}.praxis-kpi-card-caption{font-size:.75rem;color:var(--praxis-text-muted, #64748b);margin-top:.375rem}.praxis-kpi-card-footnote{font-size:.7rem;color:var(--praxis-text-secondary, #94a3b8);margin-top:.25rem}.tone-info{border-left:4px solid var(--praxis-info, #0284c7)}.tone-info .praxis-kpi-card-icon{background:color-mix(in srgb,var(--praxis-info, #0284c7) 14%,transparent);color:var(--praxis-info, #0284c7)}.tone-success{border-left:4px solid var(--praxis-success, #059669)}.tone-success .praxis-kpi-card-icon{background:color-mix(in srgb,var(--praxis-success, #059669) 14%,transparent);color:var(--praxis-success, #059669)}.tone-warning{border-left:4px solid var(--praxis-warning, #d97706)}.tone-warning .praxis-kpi-card-icon{background:color-mix(in srgb,var(--praxis-warning, #d97706) 14%,transparent);color:var(--praxis-warning, #d97706)}.tone-error,.tone-danger{border-left:4px solid var(--praxis-error, #dc2626)}.tone-error .praxis-kpi-card-icon,.tone-danger .praxis-kpi-card-icon{background:color-mix(in srgb,var(--praxis-error, #dc2626) 14%,transparent);color:var(--praxis-error, #dc2626)}.tone-neutral{border-left:4px solid var(--praxis-neutral, #94a3b8)}.tone-neutral .praxis-kpi-card-icon{background:color-mix(in srgb,var(--praxis-neutral, #94a3b8) 14%,transparent);color:var(--praxis-text-secondary, #64748b)}.praxis-kpi-card-skeleton{pointer-events:none}.praxis-kpi-skeleton-line{background:linear-gradient(90deg,color-mix(in srgb,var(--praxis-text-secondary, #94a3b8) 12%,transparent) 25%,color-mix(in srgb,var(--praxis-text-secondary, #94a3b8) 22%,transparent),color-mix(in srgb,var(--praxis-text-secondary, #94a3b8) 12%,transparent) 75%);background-size:200% 100%;animation:praxis-shimmer 1.5s infinite;border-radius:.25rem}.praxis-kpi-skeleton-line.label{width:40%;height:.75rem;margin-bottom:.75rem}.praxis-kpi-skeleton-line.value{width:65%;height:1.5rem;margin-bottom:.5rem}.praxis-kpi-skeleton-line.caption{width:50%;height:.65rem}@keyframes praxis-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}\n"] }]
1880
2638
  }], propDecorators: { config: [{
1881
2639
  type: Input
1882
2640
  }], dataContext: [{
@@ -3495,7 +4253,13 @@ class CrudMetadataEditorComponent {
3495
4253
  errorCount = computed(() => this.diagnostics().filter((issue) => issue.level === 'error').length, ...(ngDevMode ? [{ debugName: "errorCount" }] : /* istanbul ignore next */ []));
3496
4254
  warningCount = computed(() => this.diagnostics().filter((issue) => issue.level !== 'error').length, ...(ngDevMode ? [{ debugName: "warningCount" }] : /* istanbul ignore next */ []));
3497
4255
  serializedDocument = computed(() => JSON.stringify(serializeCrudAuthoringDocument(this.currentDocument()), null, 2), ...(ngDevMode ? [{ debugName: "serializedDocument" }] : /* istanbul ignore next */ []));
3498
- tableConfig = computed(() => this.currentDocument().metadata.table || { columns: [] }, ...(ngDevMode ? [{ debugName: "tableConfig" }] : /* istanbul ignore next */ []));
4256
+ tableConfig = computed(() => {
4257
+ const raw = this.currentDocument().metadata.table;
4258
+ return {
4259
+ ...(raw || {}),
4260
+ columns: Array.isArray(raw?.columns) ? raw.columns : [],
4261
+ };
4262
+ }, ...(ngDevMode ? [{ debugName: "tableConfig" }] : /* istanbul ignore next */ []));
3499
4263
  resourcePath = computed(() => this.currentDocument().metadata.resource?.path || '', ...(ngDevMode ? [{ debugName: "resourcePath" }] : /* istanbul ignore next */ []));
3500
4264
  resourceIdField = computed(() => String(this.currentDocument().metadata.resource?.idField ?? ''), ...(ngDevMode ? [{ debugName: "resourceIdField" }] : /* istanbul ignore next */ []));
3501
4265
  resourceEndpointKey = computed(() => String(this.currentDocument().metadata.resource?.endpointKey ?? ''), ...(ngDevMode ? [{ debugName: "resourceEndpointKey" }] : /* istanbul ignore next */ []));
@@ -6070,18 +6834,11 @@ class PraxisCrudComponent {
6070
6834
  rows = [this.selectedRow];
6071
6835
  }
6072
6836
  const rawTargets = event?.targets;
6073
- const targets = Array.isArray(rawTargets) && rawTargets.length > 0
6074
- ? rawTargets
6075
- : rows.map((r) => {
6076
- const id = this.resolveRowResourceId(r);
6077
- const expectedVersion = (r?.resourceVersion != null ? String(r.resourceVersion).trim() : null) ||
6078
- (r?.version != null ? String(r.version).trim() : null) ||
6079
- (r?._etag != null ? String(r._etag).trim() : null) ||
6080
- (r?.etag != null ? String(r.etag).trim() : null) ||
6081
- (r?.eTag != null ? String(r.eTag).trim() : null) ||
6082
- undefined;
6083
- return { id: id, ...(expectedVersion ? { expectedVersion } : {}) };
6084
- });
6837
+ const hasSelection = (Array.isArray(rawTargets) && rawTargets.length > 0) || rows.length > 0;
6838
+ if (!hasSelection) {
6839
+ this.snack.open('Nenhum registro selecionado para a operação em lote.', undefined, { duration: 3000 });
6840
+ return;
6841
+ }
6085
6842
  const resourcePath = String(this.resolveResourcePath(this.resolvedMetadata) || '').trim();
6086
6843
  const resourceKey = String(this.resolvedMetadata?.resource?.resourceKey ||
6087
6844
  this.tableCrudContext?.tableId ||
@@ -6092,14 +6849,28 @@ class PraxisCrudComponent {
6092
6849
  (a.id || a.action) === action);
6093
6850
  const tableBulkAction = (this.effectiveTableConfig?.actions?.bulk?.actions || []).find((a) => this.normalizeCrudActionName(a.id || a.action) === normalizedAction ||
6094
6851
  (a.id || a.action) === action);
6095
- const bulkContract = configAction?.execution?.bulk ||
6096
- crudAction?.execution?.bulk ||
6097
- tableBulkAction?.execution?.bulk;
6852
+ let resolvedBulk = this.resolveSyncBulkExecutionContractForAction(action, normalizedAction, configAction, crudAction, tableBulkAction);
6853
+ if (!resolvedBulk.contract && this.tableCollectionLinks && (!crudAction || !this.hasExplicitOpenBinding(crudAction))) {
6854
+ resolvedBulk = await this.resolveAsyncDiscoveredBulkContractForAction(action, normalizedAction);
6855
+ }
6856
+ const bulkContract = resolvedBulk.contract;
6857
+ const discoveredCatalogAction = resolvedBulk.catalogAction;
6098
6858
  if (bulkContract) {
6099
- const columns = this.effectiveTableConfig?.columns;
6859
+ const targets = Array.isArray(rawTargets) && rawTargets.length > 0
6860
+ ? rawTargets
6861
+ : await this.resolveBulkTargetsFromRows(rows);
6862
+ if (!targets.length) {
6863
+ this.snack.open('Nenhum registro selecionado para a operação em lote.', undefined, { duration: 3000 });
6864
+ return;
6865
+ }
6866
+ const columns = this.table?.config?.columns?.length
6867
+ ? this.table.config.columns
6868
+ : this.effectiveTableConfig?.columns;
6100
6869
  const schema = this.resolvedMetadata?.schema || this.resolvedMetadata?.resource?.schema;
6101
6870
  if (bulkContract.mode === 'UNIFORM_UPDATE') {
6102
- const actionTitle = configAction?.title || tableBulkAction?.title || configAction?.label || tableBulkAction?.label;
6871
+ const actionTitle = configAction?.title ||
6872
+ tableBulkAction?.title ||
6873
+ discoveredCatalogAction?.title;
6103
6874
  const dialogRef = this.dialog.open(PraxisBulkEditDialogComponent, {
6104
6875
  width: '680px',
6105
6876
  maxWidth: '92vw',
@@ -6112,12 +6883,16 @@ class PraxisCrudComponent {
6112
6883
  apiUrlEntry: this.resolveDiscoveryApiEntry(),
6113
6884
  columns,
6114
6885
  schema,
6115
- title: actionTitle || 'Atualização de Dados em Lote',
6886
+ title: actionTitle || 'Atualização de Registros em Massa',
6116
6887
  },
6117
6888
  panelClass: ['praxis-bulk-dialog-panel'],
6118
- disableCloseOnBackdrop: true,
6889
+ backdropClass: ['praxis-bulk-dialog-backdrop'],
6890
+ disableClose: true,
6119
6891
  });
6120
- dialogRef.afterClosed().subscribe((res) => {
6892
+ dialogRef
6893
+ .afterClosed()
6894
+ .pipe(takeUntilDestroyed(this.destroyRef))
6895
+ .subscribe((res) => {
6121
6896
  if (res?.success) {
6122
6897
  this.refreshTable();
6123
6898
  this.snack.open('Atualização em lote concluída com sucesso.', undefined, { duration: 3000 });
@@ -6126,13 +6901,16 @@ class PraxisCrudComponent {
6126
6901
  return;
6127
6902
  }
6128
6903
  if (bulkContract.mode === 'DOMAIN_COMMAND') {
6129
- const actionRef = crudAction || configAction || {
6904
+ const actionRef = discoveredCatalogAction || crudAction || configAction || {
6130
6905
  id: action,
6131
6906
  action,
6132
6907
  label: configAction?.label || tableBulkAction?.label || action,
6133
6908
  title: configAction?.title || configAction?.label || tableBulkAction?.label || action,
6134
6909
  execution: { bulk: bulkContract },
6135
6910
  };
6911
+ if (!actionRef.execution?.bulk) {
6912
+ actionRef.execution = { ...(actionRef.execution || {}), bulk: bulkContract };
6913
+ }
6136
6914
  actionRef.title = actionRef.title || actionRef.label || action;
6137
6915
  const dialogRef = this.dialog.open(PraxisBulkOperationDialogComponent, {
6138
6916
  width: '680px',
@@ -6148,9 +6926,13 @@ class PraxisCrudComponent {
6148
6926
  schema,
6149
6927
  },
6150
6928
  panelClass: ['praxis-bulk-dialog-panel'],
6151
- disableCloseOnBackdrop: true,
6929
+ backdropClass: ['praxis-bulk-dialog-backdrop'],
6930
+ disableClose: true,
6152
6931
  });
6153
- dialogRef.afterClosed().subscribe((res) => {
6932
+ dialogRef
6933
+ .afterClosed()
6934
+ .pipe(takeUntilDestroyed(this.destroyRef))
6935
+ .subscribe((res) => {
6154
6936
  if (res?.success) {
6155
6937
  this.refreshTable();
6156
6938
  this.snack.open('Operação em lote executada com sucesso.', undefined, { duration: 3000 });
@@ -6165,6 +6947,79 @@ class PraxisCrudComponent {
6165
6947
  }
6166
6948
  await this.onAction(action, row ?? undefined, event);
6167
6949
  }
6950
+ async resolveBulkTargetsFromRows(rows) {
6951
+ const options = this.buildDiscoveryOptions();
6952
+ return Promise.all(rows.map(async (r) => {
6953
+ const id = this.resolveRowResourceId(r);
6954
+ let expectedVersion = (r?.resourceVersion != null ? String(r.resourceVersion).trim() : null) ||
6955
+ (r?.version != null ? String(r.version).trim() : null) ||
6956
+ (r?._etag != null ? String(r._etag).trim() : null) ||
6957
+ (r?.etag != null ? String(r.etag).trim() : null) ||
6958
+ (r?.eTag != null ? String(r.eTag).trim() : null) ||
6959
+ undefined;
6960
+ if (!expectedVersion && r?._links?.self) {
6961
+ try {
6962
+ const selfHref = this.getResourceDiscovery().resolveLinkHref(r._links, 'self', options);
6963
+ if (selfHref) {
6964
+ const resp = await firstValueFrom(this.http.get(selfHref, { observe: 'response' }));
6965
+ const etagHeader = resp.headers?.get('ETag');
6966
+ const bodyVersion = resp.body?.data?.resourceVersion ??
6967
+ resp.body?.resourceVersion ??
6968
+ resp.body?.data?.version;
6969
+ if (etagHeader && etagHeader.trim()) {
6970
+ expectedVersion = etagHeader.trim();
6971
+ }
6972
+ else if (bodyVersion != null && String(bodyVersion).trim()) {
6973
+ expectedVersion = String(bodyVersion).trim();
6974
+ }
6975
+ }
6976
+ }
6977
+ catch {
6978
+ // Non-blocking fallback when self endpoint is unreachable or unmocked
6979
+ }
6980
+ }
6981
+ return { id: id, ...(expectedVersion ? { expectedVersion } : {}) };
6982
+ }));
6983
+ }
6984
+ resolveSyncBulkExecutionContractForAction(action, normalizedAction, configAction, crudAction, tableBulkAction) {
6985
+ const direct = configAction?.execution?.bulk ||
6986
+ crudAction?.execution?.bulk ||
6987
+ tableBulkAction?.execution?.bulk;
6988
+ if (direct) {
6989
+ return { contract: direct };
6990
+ }
6991
+ const matchKey = (candidate) => {
6992
+ const id = String(candidate?.id || candidate?.action || candidate?.operationId || '').trim();
6993
+ const norm = this.normalizeCrudActionName(id).toLowerCase();
6994
+ return (id === action ||
6995
+ norm === normalizedAction.toLowerCase() ||
6996
+ String(candidate?.operationId || '').trim() === action);
6997
+ };
6998
+ const capAction = (this.collectionCapabilities?.actions || []).find((a) => matchKey(a) && a.execution?.bulk);
6999
+ if (capAction?.execution?.bulk) {
7000
+ return { contract: capAction.execution.bulk, catalogAction: capAction };
7001
+ }
7002
+ const capOp = Object.values(this.collectionCapabilities?.operations || {}).find((op) => matchKey(op) && op?.bulk);
7003
+ if (capOp?.bulk) {
7004
+ return { contract: capOp.bulk };
7005
+ }
7006
+ return {};
7007
+ }
7008
+ async resolveAsyncDiscoveredBulkContractForAction(action, normalizedAction) {
7009
+ const matchKey = (candidate) => {
7010
+ const id = String(candidate?.id || candidate?.action || candidate?.operationId || '').trim();
7011
+ const norm = this.normalizeCrudActionName(id).toLowerCase();
7012
+ return (id === action ||
7013
+ norm === normalizedAction.toLowerCase() ||
7014
+ String(candidate?.operationId || '').trim() === action);
7015
+ };
7016
+ const discoveredCatalog = await this.resolveDiscoveredActionCatalog();
7017
+ const discoveredAction = (discoveredCatalog?.actions || []).find((a) => matchKey(a) && a.execution?.bulk);
7018
+ if (discoveredAction?.execution?.bulk) {
7019
+ return { contract: discoveredAction.execution.bulk, catalogAction: discoveredAction };
7020
+ }
7021
+ return {};
7022
+ }
6168
7023
  async onToolbarAction(event) {
6169
7024
  if (this.isInspectOnly()) {
6170
7025
  return;
@@ -6173,12 +7028,15 @@ class PraxisCrudComponent {
6173
7028
  if (!action)
6174
7029
  return;
6175
7030
  const normalizedAction = this.normalizeCrudActionName(action);
6176
- const bulkContract = event?.actionConfig?.execution?.bulk ||
6177
- (this.resolvedMetadata?.actions || []).find((a) => this.normalizeCrudActionName(a.id || a.action) === normalizedAction ||
6178
- (a.id || a.action) === action)?.execution?.bulk ||
6179
- (this.effectiveTableConfig?.actions?.bulk?.actions || []).find((a) => this.normalizeCrudActionName(a.id || a.action) === normalizedAction ||
6180
- (a.id || a.action) === action)?.execution?.bulk;
6181
- if (bulkContract) {
7031
+ const crudAction = (this.resolvedMetadata?.actions || []).find((a) => this.normalizeCrudActionName(a.id || a.action) === normalizedAction ||
7032
+ (a.id || a.action) === action);
7033
+ const tableBulkAction = (this.effectiveTableConfig?.actions?.bulk?.actions || []).find((a) => this.normalizeCrudActionName(a.id || a.action) === normalizedAction ||
7034
+ (a.id || a.action) === action);
7035
+ let resolvedBulk = this.resolveSyncBulkExecutionContractForAction(action, normalizedAction, event?.actionConfig, crudAction, tableBulkAction);
7036
+ if (!resolvedBulk.contract && this.tableCollectionLinks && (!crudAction || !this.hasExplicitOpenBinding(crudAction))) {
7037
+ resolvedBulk = await this.resolveAsyncDiscoveredBulkContractForAction(action, normalizedAction);
7038
+ }
7039
+ if (resolvedBulk.contract) {
6182
7040
  await this.onBulkAction(event);
6183
7041
  return;
6184
7042
  }
@@ -7605,6 +8463,51 @@ class PraxisCrudComponent {
7605
8463
  changed = true;
7606
8464
  }
7607
8465
  }
8466
+ const discoveredBulkActions = this.resolveDiscoveredBulkTableActions(capabilities);
8467
+ if (discoveredBulkActions.length > 0) {
8468
+ cfg.actions = cfg.actions || {};
8469
+ const existingBulk = cfg.actions.bulk || {
8470
+ enabled: true,
8471
+ position: 'both',
8472
+ actions: [],
8473
+ };
8474
+ const mergedActions = Array.isArray(existingBulk.actions) ? [...existingBulk.actions] : [];
8475
+ for (const discovered of discoveredBulkActions) {
8476
+ const idx = mergedActions.findIndex((candidate) => String(candidate?.action || candidate?.id || '').trim().toLowerCase() ===
8477
+ String(discovered.action || discovered.id || '').trim().toLowerCase());
8478
+ if (idx >= 0) {
8479
+ mergedActions[idx] = {
8480
+ ...discovered,
8481
+ ...mergedActions[idx],
8482
+ execution: mergedActions[idx]?.execution || discovered.execution,
8483
+ };
8484
+ }
8485
+ else {
8486
+ mergedActions.push(discovered);
8487
+ }
8488
+ }
8489
+ cfg.actions.bulk = {
8490
+ ...existingBulk,
8491
+ position: existingBulk.position || 'both',
8492
+ enabled: existingBulk.enabled !== false,
8493
+ actions: mergedActions,
8494
+ };
8495
+ cfg.behavior = cfg.behavior || {};
8496
+ if (!cfg.behavior.selection || cfg.behavior.selection.enabled !== false) {
8497
+ const prevSelection = cfg.behavior.selection;
8498
+ cfg.behavior.selection = {
8499
+ ...prevSelection,
8500
+ enabled: true,
8501
+ type: prevSelection?.type || 'multiple',
8502
+ mode: prevSelection?.mode || 'checkbox',
8503
+ allowSelectAll: prevSelection?.allowSelectAll ?? true,
8504
+ checkboxPosition: prevSelection?.checkboxPosition || 'start',
8505
+ persistSelection: prevSelection?.persistSelection ?? true,
8506
+ persistOnDataUpdate: prevSelection?.persistOnDataUpdate ?? false,
8507
+ };
8508
+ }
8509
+ changed = true;
8510
+ }
7608
8511
  const effectiveOpenMode = meta.defaults?.openMode;
7609
8512
  const hasDrawerAction = (meta.actions || []).some((action) => action.openMode === 'drawer');
7610
8513
  const drawerRequested = effectiveOpenMode === 'drawer' ||
@@ -7889,7 +8792,7 @@ class PraxisCrudComponent {
7889
8792
  return [];
7890
8793
  }
7891
8794
  return (capabilities.actions || [])
7892
- .filter((action) => action.scope === 'COLLECTION')
8795
+ .filter((action) => action.scope === 'COLLECTION' && !action.execution?.bulk)
7893
8796
  .sort((left, right) => (left.order ?? 0) - (right.order ?? 0))
7894
8797
  .map((action) => ({
7895
8798
  id: action.id,
@@ -7905,6 +8808,43 @@ class PraxisCrudComponent {
7905
8808
  : undefined),
7906
8809
  }));
7907
8810
  }
8811
+ resolveDiscoveredBulkTableActions(capabilities) {
8812
+ if (!capabilities) {
8813
+ return [];
8814
+ }
8815
+ const items = [];
8816
+ for (const action of (capabilities.actions || [])
8817
+ .filter((a) => a.scope === 'COLLECTION' && a.execution?.bulk)
8818
+ .sort((left, right) => (left.order ?? 0) - (right.order ?? 0))) {
8819
+ items.push({
8820
+ id: action.id,
8821
+ label: action.title || action.id,
8822
+ icon: action.icon || (action.execution?.bulk?.mode === 'UNIFORM_UPDATE' ? 'edit_note' : 'playlist_add_check'),
8823
+ action: action.id,
8824
+ requiresSelection: true,
8825
+ disabled: !this.isCatalogActionAvailable(capabilities, action.id),
8826
+ execution: { bulk: action.execution.bulk },
8827
+ });
8828
+ }
8829
+ for (const [opKey, op] of Object.entries(capabilities.operations || {})) {
8830
+ if (op?.supported &&
8831
+ op?.scope === 'COLLECTION' &&
8832
+ op?.bulk?.mode === 'UNIFORM_UPDATE' &&
8833
+ op.availability?.allowed !== false &&
8834
+ !items.some((item) => String(item['action'] || item['id']).toLowerCase() === opKey.toLowerCase())) {
8835
+ const isAtomic = op.bulk.atomicity === 'ATOMIC' || op.bulk.atomicity === 'ALL_OR_NONE';
8836
+ items.push({
8837
+ id: op.id || opKey,
8838
+ label: isAtomic ? 'Editar Selecionados (Atômico)' : 'Editar Selecionados',
8839
+ icon: 'edit_note',
8840
+ action: op.id || opKey,
8841
+ requiresSelection: true,
8842
+ execution: { bulk: op.bulk },
8843
+ });
8844
+ }
8845
+ }
8846
+ return items;
8847
+ }
7908
8848
  supportsCreateCapability(snapshot) {
7909
8849
  const operationAvailability = this.resolveOperationAvailability(snapshot, 'create');
7910
8850
  if (operationAvailability !== null) {
@@ -10535,13 +11475,13 @@ const PRAXIS_CRUD_AUTHORING_MANIFEST = {
10535
11475
  * Pode ser utilizado diretamente como alvo de rotas do Angular:
10536
11476
  * ```typescript
10537
11477
  * {
10538
- * path: 'missoes',
11478
+ * path: 'contracts',
10539
11479
  * component: PraxisResourcePage,
10540
11480
  * data: {
10541
- * resource: 'operations/missoes',
10542
- * title: 'Centro de Missões',
10543
- * subtitle: 'Planejamento e acompanhamento em campo.',
10544
- * domain: { label: 'Operações Táticas', icon: 'military_tech', tone: 'operations' },
11481
+ * resource: 'procurement/contracts',
11482
+ * title: 'Gestão de Contratos',
11483
+ * subtitle: 'Acompanhamento de vigência e conformidade.',
11484
+ * domain: { label: 'Suprimentos', icon: 'inventory_2', tone: 'info' },
10545
11485
  * scopeBar: { ... }
10546
11486
  * }
10547
11487
  * }
@@ -10582,6 +11522,7 @@ class PraxisResourcePage {
10582
11522
  afterSave = new EventEmitter();
10583
11523
  afterDelete = new EventEmitter();
10584
11524
  error = new EventEmitter();
11525
+ inputsVersion = signal(0, ...(ngDevMode ? [{ debugName: "inputsVersion" }] : /* istanbul ignore next */ []));
10585
11526
  routeResourceSignal = signal(null, ...(ngDevMode ? [{ debugName: "routeResourceSignal" }] : /* istanbul ignore next */ []));
10586
11527
  routeMetadataSignal = signal(null, ...(ngDevMode ? [{ debugName: "routeMetadataSignal" }] : /* istanbul ignore next */ []));
10587
11528
  routeTitleSignal = signal(null, ...(ngDevMode ? [{ debugName: "routeTitleSignal" }] : /* istanbul ignore next */ []));
@@ -10594,21 +11535,27 @@ class PraxisResourcePage {
10594
11535
  activeScopeId = signal('all', ...(ngDevMode ? [{ debugName: "activeScopeId" }] : /* istanbul ignore next */ []));
10595
11536
  activeScopeFilter = signal({}, ...(ngDevMode ? [{ debugName: "activeScopeFilter" }] : /* istanbul ignore next */ []));
10596
11537
  effectiveResource = computed(() => {
11538
+ this.inputsVersion();
10597
11539
  return this.resource || this.resourcePath || this.routeResourceSignal();
10598
11540
  }, ...(ngDevMode ? [{ debugName: "effectiveResource" }] : /* istanbul ignore next */ []));
10599
11541
  effectiveTitle = computed(() => {
11542
+ this.inputsVersion();
10600
11543
  return this.title || this.routeTitleSignal();
10601
11544
  }, ...(ngDevMode ? [{ debugName: "effectiveTitle" }] : /* istanbul ignore next */ []));
10602
11545
  effectiveSubtitle = computed(() => {
11546
+ this.inputsVersion();
10603
11547
  return this.subtitle || this.routeSubtitleSignal();
10604
11548
  }, ...(ngDevMode ? [{ debugName: "effectiveSubtitle" }] : /* istanbul ignore next */ []));
10605
11549
  effectiveDomain = computed(() => {
11550
+ this.inputsVersion();
10606
11551
  return this.domain || this.routeDomainSignal();
10607
11552
  }, ...(ngDevMode ? [{ debugName: "effectiveDomain" }] : /* istanbul ignore next */ []));
10608
11553
  effectiveEnableCustomization = computed(() => {
11554
+ this.inputsVersion();
10609
11555
  return this.enableCustomization || this.routeCustomizationSignal() || false;
10610
11556
  }, ...(ngDevMode ? [{ debugName: "effectiveEnableCustomization" }] : /* istanbul ignore next */ []));
10611
11557
  effectiveScopeBar = computed(() => {
11558
+ this.inputsVersion();
10612
11559
  if (this.scopeBar)
10613
11560
  return this.scopeBar;
10614
11561
  if (this.scopeItems && this.scopeItems.length > 0) {
@@ -10652,23 +11599,37 @@ class PraxisResourcePage {
10652
11599
  return null;
10653
11600
  }, ...(ngDevMode ? [{ debugName: "effectiveScopeBar" }] : /* istanbul ignore next */ []));
10654
11601
  resolvedCrudId = computed(() => {
11602
+ this.inputsVersion();
10655
11603
  if (this.crudId)
10656
11604
  return this.crudId;
10657
11605
  const res = this.effectiveResource();
10658
11606
  return res ? `crud-${res.replace(/\//g, '-')}` : 'praxis-crud-auto';
10659
11607
  }, ...(ngDevMode ? [{ debugName: "resolvedCrudId" }] : /* istanbul ignore next */ []));
10660
11608
  effectiveMetadata = computed(() => {
11609
+ this.inputsVersion();
10661
11610
  const metaSource = this.metadata || this.routeMetadataSignal();
10662
11611
  let base;
10663
11612
  if (typeof metaSource === 'string') {
10664
11613
  const trimmed = metaSource.trim();
10665
- base = trimmed.startsWith('{')
10666
- ? JSON.parse(trimmed)
10667
- : {
11614
+ if (trimmed.startsWith('{')) {
11615
+ try {
11616
+ base = JSON.parse(trimmed);
11617
+ }
11618
+ catch {
11619
+ base = {
11620
+ component: 'praxis-crud',
11621
+ resource: { path: this.effectiveResource() || 'recurso-auto', idField: 'id' },
11622
+ table: { columns: [] },
11623
+ };
11624
+ }
11625
+ }
11626
+ else {
11627
+ base = {
10668
11628
  component: 'praxis-crud',
10669
11629
  resource: { path: trimmed, idField: 'id' },
10670
11630
  table: { columns: [] },
10671
11631
  };
11632
+ }
10672
11633
  }
10673
11634
  else if (metaSource) {
10674
11635
  base = { ...metaSource };
@@ -10788,6 +11749,15 @@ class PraxisResourcePage {
10788
11749
  this.contextSub = this.runtimeContext?.contextChanges$
10789
11750
  .subscribe((context) => this.onRuntimeContext(context));
10790
11751
  }
11752
+ ngOnChanges(changes) {
11753
+ this.inputsVersion.update((v) => v + 1);
11754
+ const scopeInputChanged = (changes['scopeBar'] && !changes['scopeBar'].isFirstChange()) ||
11755
+ (changes['scopeItems'] && !changes['scopeItems'].isFirstChange()) ||
11756
+ (changes['defaultScopeId'] && !changes['defaultScopeId'].isFirstChange());
11757
+ if (scopeInputChanged) {
11758
+ this.restoreDefaultScope();
11759
+ }
11760
+ }
10791
11761
  ngOnDestroy() {
10792
11762
  this.routeSub?.unsubscribe();
10793
11763
  this.contextSub?.unsubscribe();
@@ -10863,7 +11833,7 @@ class PraxisResourcePage {
10863
11833
  }
10864
11834
  }
10865
11835
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisResourcePage, deps: [], target: i0.ɵɵFactoryTarget.Component });
10866
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisResourcePage, isStandalone: true, selector: "praxis-resource-page", inputs: { resource: "resource", resourcePath: "resourcePath", crudId: "crudId", metadata: "metadata", title: "title", subtitle: "subtitle", domain: "domain", scopeBar: "scopeBar", scopeItems: "scopeItems", defaultScopeId: "defaultScopeId", enableCustomization: "enableCustomization", tableConfigPersistenceStrategy: "tableConfigPersistenceStrategy", context: "context", actions: "actions", filterCriteria: "filterCriteria" }, outputs: { rowClick: "rowClick", rowAction: "rowAction", rowDoubleClick: "rowDoubleClick", selectionChange: "selectionChange", drawerOpen: "drawerOpen", drawerClose: "drawerClose", afterOpen: "afterOpen", afterClose: "afterClose", kpiCardClick: "kpiCardClick", afterSave: "afterSave", afterDelete: "afterDelete", error: "error" }, ngImport: i0, template: `
11836
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisResourcePage, isStandalone: true, selector: "praxis-resource-page", inputs: { resource: "resource", resourcePath: "resourcePath", crudId: "crudId", metadata: "metadata", title: "title", subtitle: "subtitle", domain: "domain", scopeBar: "scopeBar", scopeItems: "scopeItems", defaultScopeId: "defaultScopeId", enableCustomization: "enableCustomization", tableConfigPersistenceStrategy: "tableConfigPersistenceStrategy", context: "context", actions: "actions", filterCriteria: "filterCriteria" }, outputs: { rowClick: "rowClick", rowAction: "rowAction", rowDoubleClick: "rowDoubleClick", selectionChange: "selectionChange", drawerOpen: "drawerOpen", drawerClose: "drawerClose", afterOpen: "afterOpen", afterClose: "afterClose", kpiCardClick: "kpiCardClick", afterSave: "afterSave", afterDelete: "afterDelete", error: "error" }, usesOnChanges: true, ngImport: i0, template: `
10867
11837
  <div class="praxis-resource-page page-container">
10868
11838
  <!-- Auto-Header Governado -->
10869
11839
  @if (effectiveTitle() || effectiveDomain()) {
@@ -10935,7 +11905,7 @@ class PraxisResourcePage {
10935
11905
  }
10936
11906
  }
10937
11907
  </div>
10938
- `, isInline: true, styles: [":host{display:block;width:100%;min-height:100%;box-sizing:border-box}.praxis-resource-page{width:100%;display:flex;flex-direction:column;gap:20px;padding:var(--praxis-page-padding, 24px);box-sizing:border-box;max-width:1540px;margin:0 auto}.praxis-resource-page__header{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap}.praxis-resource-page__header-main{min-width:0;display:flex;flex-direction:column}.praxis-resource-page__domain-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:9999px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px;align-self:flex-start;background:var(--praxis-domain-badge-bg, rgba(255, 255, 255, .08));color:var(--praxis-domain-badge-color, inherit);border:1px solid var(--praxis-domain-badge-border, rgba(255, 255, 255, .1))}.praxis-resource-page__domain-badge span.material-symbols-outlined{font-size:14px}.praxis-resource-page__title{margin:0 0 6px;font-size:var(--praxis-page-title-size, 2rem);font-weight:800;letter-spacing:-.02em;color:var(--praxis-page-title-color, var(--foreground, inherit));line-height:1.2}.praxis-resource-page__subtitle{margin:0;font-size:var(--praxis-page-subtitle-size, .9rem);color:var(--muted-foreground, #94a3b8);max-width:800px;line-height:1.5}.praxis-resource-page__crud-surface{width:100%;border-radius:var(--praxis-surface-radius, 16px);overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: PraxisCrudComponent, selector: "praxis-crud", inputs: ["metadata", "crudId", "resource", "componentInstanceId", "tableConfigPersistenceStrategy", "context", "enableCustomization", "showResourceIdentityKey", "authoringCapability", "filterCriteria", "filterTeleport", "disableAutoDrawer"], outputs: ["configureRequested", "afterOpen", "afterClose", "afterSave", "afterDelete", "reactiveDeterminationExecuted", "reactiveDeterminationPendingChange", "reactiveDeterminationStabilityChange", "error", "rowClick", "rowAction", "selectionChange", "resourceEvent", "tableRuntimeConfigChange", "drawerOpen", "drawerClose", "crudAuthoringDocumentApplied", "crudAuthoringDocumentSaved", "kpiCardClick"] }, { kind: "component", type: PraxisScopeBarComponent, selector: "praxis-scope-bar", inputs: ["items", "activeId", "showOmnibox", "omniboxPlaceholder", "omniboxValue", "omniboxDebounceMs", "leadIcon", "leadLabel", "showClearButton", "clearLabel", "target", "targetWidgetId", "scopeBarId", "searchField"], outputs: ["activeIdChange", "omniboxValueChange", "scopeChange", "searchChange", "filterCriteriaChange", "clear"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11908
+ `, isInline: true, styles: [":host{display:block;width:100%;min-height:100%;box-sizing:border-box}.praxis-resource-page{width:100%;display:flex;flex-direction:column;gap:20px;padding:var(--praxis-page-padding, 24px);box-sizing:border-box;max-width:1540px;margin:0 auto}.praxis-resource-page__header{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap}.praxis-resource-page__header-main{min-width:0;display:flex;flex-direction:column}.praxis-resource-page__domain-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:9999px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px;align-self:flex-start;background:var(--praxis-domain-badge-bg, rgba(255, 255, 255, .08));color:var(--praxis-domain-badge-color, inherit);border:1px solid var(--praxis-domain-badge-border, rgba(255, 255, 255, .1))}.praxis-resource-page__domain-badge.tone-primary,.praxis-resource-page__domain-badge.tone-info{background:color-mix(in srgb,var(--praxis-info, #0284c7) 14%,transparent);color:var(--praxis-info, #38bdf8);border-color:color-mix(in srgb,var(--praxis-info, #0284c7) 30%,transparent)}.praxis-resource-page__domain-badge.tone-success{background:color-mix(in srgb,var(--praxis-success, #059669) 14%,transparent);color:var(--praxis-success, #34d399);border-color:color-mix(in srgb,var(--praxis-success, #059669) 30%,transparent)}.praxis-resource-page__domain-badge.tone-warning{background:color-mix(in srgb,var(--praxis-warning, #d97706) 14%,transparent);color:var(--praxis-warning, #fbbf24);border-color:color-mix(in srgb,var(--praxis-warning, #d97706) 30%,transparent)}.praxis-resource-page__domain-badge.tone-danger,.praxis-resource-page__domain-badge.tone-error{background:color-mix(in srgb,var(--praxis-error, #dc2626) 14%,transparent);color:var(--praxis-error, #f87171);border-color:color-mix(in srgb,var(--praxis-error, #dc2626) 30%,transparent)}.praxis-resource-page__domain-badge span.material-symbols-outlined{font-size:14px}.praxis-resource-page__title{margin:0 0 6px;font-size:var(--praxis-page-title-size, 2rem);font-weight:800;letter-spacing:-.02em;color:var(--praxis-page-title-color, var(--foreground, inherit));line-height:1.2}.praxis-resource-page__subtitle{margin:0;font-size:var(--praxis-page-subtitle-size, .9rem);color:var(--muted-foreground, #94a3b8);max-width:800px;line-height:1.5}.praxis-resource-page__crud-surface{width:100%;border-radius:var(--praxis-surface-radius, 16px);overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: PraxisCrudComponent, selector: "praxis-crud", inputs: ["metadata", "crudId", "resource", "componentInstanceId", "tableConfigPersistenceStrategy", "context", "enableCustomization", "showResourceIdentityKey", "authoringCapability", "filterCriteria", "filterTeleport", "disableAutoDrawer"], outputs: ["configureRequested", "afterOpen", "afterClose", "afterSave", "afterDelete", "reactiveDeterminationExecuted", "reactiveDeterminationPendingChange", "reactiveDeterminationStabilityChange", "error", "rowClick", "rowAction", "selectionChange", "resourceEvent", "tableRuntimeConfigChange", "drawerOpen", "drawerClose", "crudAuthoringDocumentApplied", "crudAuthoringDocumentSaved", "kpiCardClick"] }, { kind: "component", type: PraxisScopeBarComponent, selector: "praxis-scope-bar", inputs: ["items", "activeId", "showOmnibox", "omniboxPlaceholder", "omniboxValue", "omniboxDebounceMs", "leadIcon", "leadLabel", "showClearButton", "clearLabel", "target", "targetWidgetId", "scopeBarId", "searchField"], outputs: ["activeIdChange", "omniboxValueChange", "scopeChange", "searchChange", "filterCriteriaChange", "clear"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10939
11909
  }
10940
11910
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisResourcePage, decorators: [{
10941
11911
  type: Component,
@@ -11011,7 +11981,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11011
11981
  }
11012
11982
  }
11013
11983
  </div>
11014
- `, styles: [":host{display:block;width:100%;min-height:100%;box-sizing:border-box}.praxis-resource-page{width:100%;display:flex;flex-direction:column;gap:20px;padding:var(--praxis-page-padding, 24px);box-sizing:border-box;max-width:1540px;margin:0 auto}.praxis-resource-page__header{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap}.praxis-resource-page__header-main{min-width:0;display:flex;flex-direction:column}.praxis-resource-page__domain-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:9999px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px;align-self:flex-start;background:var(--praxis-domain-badge-bg, rgba(255, 255, 255, .08));color:var(--praxis-domain-badge-color, inherit);border:1px solid var(--praxis-domain-badge-border, rgba(255, 255, 255, .1))}.praxis-resource-page__domain-badge span.material-symbols-outlined{font-size:14px}.praxis-resource-page__title{margin:0 0 6px;font-size:var(--praxis-page-title-size, 2rem);font-weight:800;letter-spacing:-.02em;color:var(--praxis-page-title-color, var(--foreground, inherit));line-height:1.2}.praxis-resource-page__subtitle{margin:0;font-size:var(--praxis-page-subtitle-size, .9rem);color:var(--muted-foreground, #94a3b8);max-width:800px;line-height:1.5}.praxis-resource-page__crud-surface{width:100%;border-radius:var(--praxis-surface-radius, 16px);overflow:hidden}\n"] }]
11984
+ `, styles: [":host{display:block;width:100%;min-height:100%;box-sizing:border-box}.praxis-resource-page{width:100%;display:flex;flex-direction:column;gap:20px;padding:var(--praxis-page-padding, 24px);box-sizing:border-box;max-width:1540px;margin:0 auto}.praxis-resource-page__header{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap}.praxis-resource-page__header-main{min-width:0;display:flex;flex-direction:column}.praxis-resource-page__domain-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:9999px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px;align-self:flex-start;background:var(--praxis-domain-badge-bg, rgba(255, 255, 255, .08));color:var(--praxis-domain-badge-color, inherit);border:1px solid var(--praxis-domain-badge-border, rgba(255, 255, 255, .1))}.praxis-resource-page__domain-badge.tone-primary,.praxis-resource-page__domain-badge.tone-info{background:color-mix(in srgb,var(--praxis-info, #0284c7) 14%,transparent);color:var(--praxis-info, #38bdf8);border-color:color-mix(in srgb,var(--praxis-info, #0284c7) 30%,transparent)}.praxis-resource-page__domain-badge.tone-success{background:color-mix(in srgb,var(--praxis-success, #059669) 14%,transparent);color:var(--praxis-success, #34d399);border-color:color-mix(in srgb,var(--praxis-success, #059669) 30%,transparent)}.praxis-resource-page__domain-badge.tone-warning{background:color-mix(in srgb,var(--praxis-warning, #d97706) 14%,transparent);color:var(--praxis-warning, #fbbf24);border-color:color-mix(in srgb,var(--praxis-warning, #d97706) 30%,transparent)}.praxis-resource-page__domain-badge.tone-danger,.praxis-resource-page__domain-badge.tone-error{background:color-mix(in srgb,var(--praxis-error, #dc2626) 14%,transparent);color:var(--praxis-error, #f87171);border-color:color-mix(in srgb,var(--praxis-error, #dc2626) 30%,transparent)}.praxis-resource-page__domain-badge span.material-symbols-outlined{font-size:14px}.praxis-resource-page__title{margin:0 0 6px;font-size:var(--praxis-page-title-size, 2rem);font-weight:800;letter-spacing:-.02em;color:var(--praxis-page-title-color, var(--foreground, inherit));line-height:1.2}.praxis-resource-page__subtitle{margin:0;font-size:var(--praxis-page-subtitle-size, .9rem);color:var(--muted-foreground, #94a3b8);max-width:800px;line-height:1.5}.praxis-resource-page__crud-surface{width:100%;border-radius:var(--praxis-surface-radius, 16px);overflow:hidden}\n"] }]
11015
11985
  }], propDecorators: { resource: [{
11016
11986
  type: Input
11017
11987
  }], resourcePath: [{