@praxisui/crud 10.0.0-rc.15 → 10.0.0-rc.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai/component-registry.json +2 -2
- package/fesm2022/praxisui-crud.mjs +464 -145
- package/package.json +7 -7
- package/types/praxisui-crud.d.ts +24 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-10-
|
|
3
|
+
"generatedAt": "2026-10-14T20:20:10.826Z",
|
|
4
4
|
"packageName": "@praxisui/crud",
|
|
5
|
-
"packageVersion": "10.0.0-rc.
|
|
5
|
+
"packageVersion": "10.0.0-rc.16",
|
|
6
6
|
"sourceRegistry": "praxis-component-registry-ingestion",
|
|
7
7
|
"sourceRegistryVersion": "1.0.0",
|
|
8
8
|
"componentCount": 2,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 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,
|
|
2
|
+
import { inject, DestroyRef, signal, HostListener, ViewChild, Inject, ViewEncapsulation, Component, EventEmitter, ChangeDetectorRef, Output, Input, ChangeDetectionStrategy, Injectable, InjectionToken, input, computed, effect, output, Injector, booleanAttribute, 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$1 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';
|
|
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, composeHeadersWithVersion, 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 from '@angular/common';
|
|
13
13
|
import { CommonModule } from '@angular/common';
|
|
@@ -47,11 +47,13 @@ class PraxisBulkOperationDialogComponent {
|
|
|
47
47
|
dialogRef = inject((MatDialogRef));
|
|
48
48
|
bulkClient = inject(PraxisBulkClientService);
|
|
49
49
|
destroyRef = inject(DestroyRef);
|
|
50
|
+
parametersForm;
|
|
50
51
|
step = signal('PARAMETERS', ...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
|
|
51
52
|
proposal = signal(null, ...(ngDevMode ? [{ debugName: "proposal" }] : /* istanbul ignore next */ []));
|
|
52
53
|
execution = signal(null, ...(ngDevMode ? [{ debugName: "execution" }] : /* istanbul ignore next */ []));
|
|
53
54
|
errorMessage = signal(null, ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
54
55
|
parametersValue = signal({}, ...(ngDevMode ? [{ debugName: "parametersValue" }] : /* istanbul ignore next */ []));
|
|
56
|
+
parametersFormValid = signal(true, ...(ngDevMode ? [{ debugName: "parametersFormValid" }] : /* istanbul ignore next */ []));
|
|
55
57
|
proposalItems = signal([], ...(ngDevMode ? [{ debugName: "proposalItems" }] : /* istanbul ignore next */ []));
|
|
56
58
|
proposalNextCursor = signal(null, ...(ngDevMode ? [{ debugName: "proposalNextCursor" }] : /* istanbul ignore next */ []));
|
|
57
59
|
loadingProposalResults = signal(false, ...(ngDevMode ? [{ debugName: "loadingProposalResults" }] : /* istanbul ignore next */ []));
|
|
@@ -113,7 +115,18 @@ class PraxisBulkOperationDialogComponent {
|
|
|
113
115
|
.replace(/[^a-zA-Z0-9_-]+/g, '-');
|
|
114
116
|
return `bulk-params-${resourcePart}-${actionPart}`;
|
|
115
117
|
}
|
|
118
|
+
onParametersFormReady(event) {
|
|
119
|
+
if (typeof event?.isValid === 'boolean') {
|
|
120
|
+
this.parametersFormValid.set(event.isValid);
|
|
121
|
+
}
|
|
122
|
+
if (event?.formData && typeof event.formData === 'object' && !Array.isArray(event.formData)) {
|
|
123
|
+
this.onParametersChange(event);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
116
126
|
onParametersChange(event) {
|
|
127
|
+
if (typeof event?.isValid === 'boolean') {
|
|
128
|
+
this.parametersFormValid.set(event.isValid);
|
|
129
|
+
}
|
|
117
130
|
const raw = event?.formData ?? event?.value ?? event;
|
|
118
131
|
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
|
|
119
132
|
const unwrapped = raw.parameters &&
|
|
@@ -123,7 +136,52 @@ class PraxisBulkOperationDialogComponent {
|
|
|
123
136
|
? raw.parameters
|
|
124
137
|
: raw;
|
|
125
138
|
this.parametersValue.set(unwrapped);
|
|
139
|
+
if (this.parametersFormValid()) {
|
|
140
|
+
this.errorMessage.set(null);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
getMissingRequiredParameterNames() {
|
|
145
|
+
const bulkContract = this.action.execution?.bulk;
|
|
146
|
+
const descriptors = this.action.commandParameterDescriptors ||
|
|
147
|
+
bulkContract?.commandParameterDescriptors;
|
|
148
|
+
if (!Array.isArray(descriptors)) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
const currentValues = this.parametersValue();
|
|
152
|
+
const missing = [];
|
|
153
|
+
for (const desc of descriptors) {
|
|
154
|
+
if (!desc?.required)
|
|
155
|
+
continue;
|
|
156
|
+
const key = String(desc.name || desc.field || desc.property || '').trim();
|
|
157
|
+
if (!key)
|
|
158
|
+
continue;
|
|
159
|
+
const val = currentValues[key];
|
|
160
|
+
const isEmpty = val === undefined ||
|
|
161
|
+
val === null ||
|
|
162
|
+
(typeof val === 'string' && val.trim() === '') ||
|
|
163
|
+
(Array.isArray(val) && val.length === 0);
|
|
164
|
+
if (isEmpty) {
|
|
165
|
+
missing.push(String(desc.label || desc.title || key));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return missing;
|
|
169
|
+
}
|
|
170
|
+
canEvaluateParameters() {
|
|
171
|
+
if (!this.hasParametersSchema()) {
|
|
172
|
+
return true;
|
|
126
173
|
}
|
|
174
|
+
if (!this.parametersFormValid()) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
if (this.getMissingRequiredParameterNames().length > 0) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
const dynamicFormGroup = this.parametersForm?.form;
|
|
181
|
+
if (dynamicFormGroup && Object.keys(dynamicFormGroup.controls || {}).length > 0) {
|
|
182
|
+
return this.parametersForm.effectiveFormIsValid;
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
127
185
|
}
|
|
128
186
|
isBlockedByAtomicity() {
|
|
129
187
|
const prop = this.proposal();
|
|
@@ -228,6 +286,27 @@ class PraxisBulkOperationDialogComponent {
|
|
|
228
286
|
this.errorMessage.set(`A seleção atual (${this.targets.length} itens) excede o limite máximo permitido pelo contrato (${maxTargets} itens por lote).`);
|
|
229
287
|
return;
|
|
230
288
|
}
|
|
289
|
+
if (this.hasParametersSchema()) {
|
|
290
|
+
const dynamicFormGroup = this.parametersForm?.form;
|
|
291
|
+
if (dynamicFormGroup && Object.keys(dynamicFormGroup.controls || {}).length > 0) {
|
|
292
|
+
dynamicFormGroup.markAllAsTouched();
|
|
293
|
+
if (!this.parametersForm.effectiveFormIsValid) {
|
|
294
|
+
this.parametersFormValid.set(false);
|
|
295
|
+
const missingLabels = this.parametersForm?.invalidRequiredFieldLabels || [];
|
|
296
|
+
this.errorMessage.set(missingLabels.length > 0
|
|
297
|
+
? `Preencha os campos obrigatórios antes de avaliar: ${missingLabels.join(', ')}.`
|
|
298
|
+
: 'Corrija os campos inválidos dos parâmetros antes de avaliar a proposta.');
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const missingDescriptors = this.getMissingRequiredParameterNames();
|
|
303
|
+
if (missingDescriptors.length > 0 || !this.parametersFormValid()) {
|
|
304
|
+
this.errorMessage.set(missingDescriptors.length > 0
|
|
305
|
+
? `Preencha os parâmetros obrigatórios antes de avaliar: ${missingDescriptors.join(', ')}.`
|
|
306
|
+
: 'Corrija os campos inválidos dos parâmetros antes de avaliar a proposta.');
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
231
310
|
this.confirmationIdempotencyKey = undefined;
|
|
232
311
|
this.step.set('EVALUATING');
|
|
233
312
|
this.errorMessage.set(null);
|
|
@@ -295,8 +374,9 @@ class PraxisBulkOperationDialogComponent {
|
|
|
295
374
|
this.proposalNextCursor.set(page?.nextCursor ?? page?.next ?? null);
|
|
296
375
|
this.loadingProposalResults.set(false);
|
|
297
376
|
},
|
|
298
|
-
error: () => {
|
|
377
|
+
error: (err) => {
|
|
299
378
|
this.loadingProposalResults.set(false);
|
|
379
|
+
this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao carregar os resultados detalhados da proposta.');
|
|
300
380
|
},
|
|
301
381
|
});
|
|
302
382
|
}
|
|
@@ -386,8 +466,9 @@ class PraxisBulkOperationDialogComponent {
|
|
|
386
466
|
this.executionNextCursor.set(page?.nextCursor ?? page?.next ?? null);
|
|
387
467
|
this.loadingExecutionResults.set(false);
|
|
388
468
|
},
|
|
389
|
-
error: () => {
|
|
469
|
+
error: (err) => {
|
|
390
470
|
this.loadingExecutionResults.set(false);
|
|
471
|
+
this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao carregar os resultados detalhados da execução.');
|
|
391
472
|
},
|
|
392
473
|
});
|
|
393
474
|
}
|
|
@@ -431,9 +512,16 @@ class PraxisBulkOperationDialogComponent {
|
|
|
431
512
|
});
|
|
432
513
|
}
|
|
433
514
|
onCloseSuccess() {
|
|
515
|
+
const exec = this.execution();
|
|
516
|
+
const status = exec?.status;
|
|
517
|
+
const confirmed = exec?.totals?.confirmed ?? 0;
|
|
518
|
+
const isSuccess = !exec ||
|
|
519
|
+
status === 'COMPLETED' ||
|
|
520
|
+
status === 'COMPLETED_WITH_ERRORS' ||
|
|
521
|
+
confirmed > 0;
|
|
434
522
|
this.dialogRef.close({
|
|
435
|
-
success:
|
|
436
|
-
execution:
|
|
523
|
+
success: isSuccess,
|
|
524
|
+
execution: exec,
|
|
437
525
|
});
|
|
438
526
|
}
|
|
439
527
|
onCancel() {
|
|
@@ -450,7 +538,7 @@ class PraxisBulkOperationDialogComponent {
|
|
|
450
538
|
}
|
|
451
539
|
}
|
|
452
540
|
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 });
|
|
453
|
-
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: `
|
|
541
|
+
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)" } }, viewQueries: [{ propertyName: "parametersForm", first: true, predicate: PraxisDynamicForm, descendants: true }], ngImport: i0, template: `
|
|
454
542
|
<div class="praxis-bulk-dialog" role="dialog" aria-labelledby="praxis-bulk-dialog-title" aria-describedby="praxis-bulk-dialog-subtitle">
|
|
455
543
|
<div class="praxis-bulk-dialog__header">
|
|
456
544
|
<div class="praxis-bulk-dialog__title-group">
|
|
@@ -490,6 +578,7 @@ class PraxisBulkOperationDialogComponent {
|
|
|
490
578
|
[submitMethod]="'post'"
|
|
491
579
|
[submitUrl]="''"
|
|
492
580
|
[actions]="formActions"
|
|
581
|
+
(formReady)="onParametersFormReady($event)"
|
|
493
582
|
(valueChange)="onParametersChange($event)"
|
|
494
583
|
/>
|
|
495
584
|
</div>
|
|
@@ -727,7 +816,7 @@ class PraxisBulkOperationDialogComponent {
|
|
|
727
816
|
<div class="praxis-bulk-dialog__actions">
|
|
728
817
|
@if (step() === 'PARAMETERS') {
|
|
729
818
|
<button mat-button (click)="onCancel()">Cancelar</button>
|
|
730
|
-
<button mat-flat-button color="primary" (click)="onEvaluate()">
|
|
819
|
+
<button mat-flat-button color="primary" (click)="onEvaluate()" [disabled]="!canEvaluateParameters()">
|
|
731
820
|
Avaliar Proposta
|
|
732
821
|
</button>
|
|
733
822
|
}
|
|
@@ -807,6 +896,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
807
896
|
[submitMethod]="'post'"
|
|
808
897
|
[submitUrl]="''"
|
|
809
898
|
[actions]="formActions"
|
|
899
|
+
(formReady)="onParametersFormReady($event)"
|
|
810
900
|
(valueChange)="onParametersChange($event)"
|
|
811
901
|
/>
|
|
812
902
|
</div>
|
|
@@ -1044,7 +1134,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
1044
1134
|
<div class="praxis-bulk-dialog__actions">
|
|
1045
1135
|
@if (step() === 'PARAMETERS') {
|
|
1046
1136
|
<button mat-button (click)="onCancel()">Cancelar</button>
|
|
1047
|
-
<button mat-flat-button color="primary" (click)="onEvaluate()">
|
|
1137
|
+
<button mat-flat-button color="primary" (click)="onEvaluate()" [disabled]="!canEvaluateParameters()">
|
|
1048
1138
|
Avaliar Proposta
|
|
1049
1139
|
</button>
|
|
1050
1140
|
}
|
|
@@ -1076,7 +1166,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
1076
1166
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1077
1167
|
type: Inject,
|
|
1078
1168
|
args: [MAT_DIALOG_DATA]
|
|
1079
|
-
}] }], propDecorators: {
|
|
1169
|
+
}] }], propDecorators: { parametersForm: [{
|
|
1170
|
+
type: ViewChild,
|
|
1171
|
+
args: [PraxisDynamicForm]
|
|
1172
|
+
}], onEscape: [{
|
|
1080
1173
|
type: HostListener,
|
|
1081
1174
|
args: ['keydown.escape', ['$event']]
|
|
1082
1175
|
}] } });
|
|
@@ -1180,6 +1273,7 @@ class PraxisBulkEditDialogComponent {
|
|
|
1180
1273
|
const selectedFields = this.fieldRows().map((r) => r.field);
|
|
1181
1274
|
return this.writableFields.some((f) => !selectedFields.includes(f));
|
|
1182
1275
|
}
|
|
1276
|
+
nextRowId = 1;
|
|
1183
1277
|
isFieldSelectedInOtherRow(field, currentRow) {
|
|
1184
1278
|
return this.fieldRows().some((r) => r !== currentRow && r.field === field);
|
|
1185
1279
|
}
|
|
@@ -1189,6 +1283,7 @@ class PraxisBulkEditDialogComponent {
|
|
|
1189
1283
|
this.fieldRows.update((rows) => [
|
|
1190
1284
|
...rows,
|
|
1191
1285
|
{
|
|
1286
|
+
id: `field-row-${this.nextRowId++}`,
|
|
1192
1287
|
field: nextField,
|
|
1193
1288
|
operator: 'SET',
|
|
1194
1289
|
value: '',
|
|
@@ -1198,6 +1293,21 @@ class PraxisBulkEditDialogComponent {
|
|
|
1198
1293
|
removeFieldRow(index) {
|
|
1199
1294
|
this.fieldRows.update((rows) => rows.filter((_, i) => i !== index));
|
|
1200
1295
|
}
|
|
1296
|
+
getCurrencyFieldLabel(fieldName) {
|
|
1297
|
+
const descriptor = this.getFieldDescriptor(fieldName);
|
|
1298
|
+
const prop = this.getEffectiveSchemaProperty(fieldName);
|
|
1299
|
+
const col = this.data.columns?.find((c) => c.field === fieldName);
|
|
1300
|
+
const currencySymbol = descriptor?.['currencySymbol'] ||
|
|
1301
|
+
descriptor?.['currencyCode'] ||
|
|
1302
|
+
prop?.['x-ui']?.currencySymbol ||
|
|
1303
|
+
prop?.['x-ui']?.currencyCode ||
|
|
1304
|
+
col?.['currencySymbol'] ||
|
|
1305
|
+
col?.['currencyCode'];
|
|
1306
|
+
if (typeof currencySymbol === 'string' && currencySymbol.trim()) {
|
|
1307
|
+
return `Novo Valor (${currencySymbol.trim()})`;
|
|
1308
|
+
}
|
|
1309
|
+
return 'Novo Valor Monetário';
|
|
1310
|
+
}
|
|
1201
1311
|
getFieldLabel(fieldName) {
|
|
1202
1312
|
if (!fieldName)
|
|
1203
1313
|
return '';
|
|
@@ -1517,8 +1627,9 @@ class PraxisBulkEditDialogComponent {
|
|
|
1517
1627
|
this.proposalNextCursor.set(page?.nextCursor ?? page?.next ?? null);
|
|
1518
1628
|
this.loadingProposalResults.set(false);
|
|
1519
1629
|
},
|
|
1520
|
-
error: () => {
|
|
1630
|
+
error: (err) => {
|
|
1521
1631
|
this.loadingProposalResults.set(false);
|
|
1632
|
+
this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao carregar os resultados detalhados da proposta.');
|
|
1522
1633
|
},
|
|
1523
1634
|
});
|
|
1524
1635
|
}
|
|
@@ -1606,8 +1717,9 @@ class PraxisBulkEditDialogComponent {
|
|
|
1606
1717
|
this.executionNextCursor.set(page?.nextCursor ?? page?.next ?? null);
|
|
1607
1718
|
this.loadingExecutionResults.set(false);
|
|
1608
1719
|
},
|
|
1609
|
-
error: () => {
|
|
1720
|
+
error: (err) => {
|
|
1610
1721
|
this.loadingExecutionResults.set(false);
|
|
1722
|
+
this.errorMessage.set(err?.error?.message || err?.message || 'Falha ao carregar os resultados detalhados da execução.');
|
|
1611
1723
|
},
|
|
1612
1724
|
});
|
|
1613
1725
|
}
|
|
@@ -1649,9 +1761,16 @@ class PraxisBulkEditDialogComponent {
|
|
|
1649
1761
|
});
|
|
1650
1762
|
}
|
|
1651
1763
|
onCloseSuccess() {
|
|
1764
|
+
const exec = this.execution();
|
|
1765
|
+
const status = exec?.status;
|
|
1766
|
+
const confirmed = exec?.totals?.confirmed ?? 0;
|
|
1767
|
+
const isSuccess = !exec ||
|
|
1768
|
+
status === 'COMPLETED' ||
|
|
1769
|
+
status === 'COMPLETED_WITH_ERRORS' ||
|
|
1770
|
+
confirmed > 0;
|
|
1652
1771
|
this.dialogRef.close({
|
|
1653
|
-
success:
|
|
1654
|
-
execution:
|
|
1772
|
+
success: isSuccess,
|
|
1773
|
+
execution: exec,
|
|
1655
1774
|
});
|
|
1656
1775
|
}
|
|
1657
1776
|
onCancel() {
|
|
@@ -1738,7 +1857,7 @@ class PraxisBulkEditDialogComponent {
|
|
|
1738
1857
|
</div>
|
|
1739
1858
|
} @else {
|
|
1740
1859
|
<div class="field-rows-container">
|
|
1741
|
-
@for (row of fieldRows(); track $index) {
|
|
1860
|
+
@for (row of fieldRows(); track row.id; let idx = $index) {
|
|
1742
1861
|
<div class="field-row">
|
|
1743
1862
|
<!-- Seleção do Campo -->
|
|
1744
1863
|
<mat-form-field appearance="outline" class="field-select">
|
|
@@ -1769,7 +1888,7 @@ class PraxisBulkEditDialogComponent {
|
|
|
1769
1888
|
@switch (getFieldType(row.field)) {
|
|
1770
1889
|
@case ('currency') {
|
|
1771
1890
|
<mat-form-field appearance="outline" class="value-input">
|
|
1772
|
-
<mat-label>
|
|
1891
|
+
<mat-label>{{ getCurrencyFieldLabel(row.field) }}</mat-label>
|
|
1773
1892
|
<input matInput type="number" step="0.01" [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)" placeholder="0.00" />
|
|
1774
1893
|
</mat-form-field>
|
|
1775
1894
|
}
|
|
@@ -1819,7 +1938,7 @@ class PraxisBulkEditDialogComponent {
|
|
|
1819
1938
|
}
|
|
1820
1939
|
|
|
1821
1940
|
<!-- Remover Linha -->
|
|
1822
|
-
<button mat-icon-button color="warn" (click)="removeFieldRow(
|
|
1941
|
+
<button mat-icon-button color="warn" (click)="removeFieldRow(idx)" title="Remover">
|
|
1823
1942
|
<mat-icon>delete_outline</mat-icon>
|
|
1824
1943
|
</button>
|
|
1825
1944
|
</div>
|
|
@@ -2128,7 +2247,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
2128
2247
|
</div>
|
|
2129
2248
|
} @else {
|
|
2130
2249
|
<div class="field-rows-container">
|
|
2131
|
-
@for (row of fieldRows(); track $index) {
|
|
2250
|
+
@for (row of fieldRows(); track row.id; let idx = $index) {
|
|
2132
2251
|
<div class="field-row">
|
|
2133
2252
|
<!-- Seleção do Campo -->
|
|
2134
2253
|
<mat-form-field appearance="outline" class="field-select">
|
|
@@ -2159,7 +2278,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
2159
2278
|
@switch (getFieldType(row.field)) {
|
|
2160
2279
|
@case ('currency') {
|
|
2161
2280
|
<mat-form-field appearance="outline" class="value-input">
|
|
2162
|
-
<mat-label>
|
|
2281
|
+
<mat-label>{{ getCurrencyFieldLabel(row.field) }}</mat-label>
|
|
2163
2282
|
<input matInput type="number" step="0.01" [(ngModel)]="row.value" (ngModelChange)="errorMessage.set(null)" placeholder="0.00" />
|
|
2164
2283
|
</mat-form-field>
|
|
2165
2284
|
}
|
|
@@ -2209,7 +2328,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
2209
2328
|
}
|
|
2210
2329
|
|
|
2211
2330
|
<!-- Remover Linha -->
|
|
2212
|
-
<button mat-icon-button color="warn" (click)="removeFieldRow(
|
|
2331
|
+
<button mat-icon-button color="warn" (click)="removeFieldRow(idx)" title="Remover">
|
|
2213
2332
|
<mat-icon>delete_outline</mat-icon>
|
|
2214
2333
|
</button>
|
|
2215
2334
|
</div>
|
|
@@ -2528,6 +2647,11 @@ class PraxisKpiBandComponent {
|
|
|
2528
2647
|
* para cliques em cards. Quando explicitamente falso, evita a falsa affordance de botões.
|
|
2529
2648
|
*/
|
|
2530
2649
|
isInteractiveHost;
|
|
2650
|
+
/**
|
|
2651
|
+
* Filtro ativo sincronizado pelo host (ex.: PraxisCrudComponent / PraxisResourcePage)
|
|
2652
|
+
* para destacar visualmente o card de KPI correspondente.
|
|
2653
|
+
*/
|
|
2654
|
+
activeFilter;
|
|
2531
2655
|
cardClick = new EventEmitter();
|
|
2532
2656
|
statsLoaded = new EventEmitter();
|
|
2533
2657
|
statsError = new EventEmitter();
|
|
@@ -2578,7 +2702,7 @@ class PraxisKpiBandComponent {
|
|
|
2578
2702
|
if (!this.initialized) {
|
|
2579
2703
|
return;
|
|
2580
2704
|
}
|
|
2581
|
-
if (changes['config'] || changes['dataContext']) {
|
|
2705
|
+
if (changes['config'] || changes['dataContext'] || changes['activeFilter'] || changes['isInteractiveHost']) {
|
|
2582
2706
|
if (changes['config'] && this.config?.source !== changes['config'].previousValue?.source) {
|
|
2583
2707
|
this.refresh();
|
|
2584
2708
|
}
|
|
@@ -2677,6 +2801,7 @@ class PraxisKpiBandComponent {
|
|
|
2677
2801
|
const interactive = card.interactive !== undefined
|
|
2678
2802
|
? card.interactive
|
|
2679
2803
|
: !!(card.action || card.filter || card.route || isObserved);
|
|
2804
|
+
const active = this.isCardFilterActive(card.filter, this.activeFilter);
|
|
2680
2805
|
return {
|
|
2681
2806
|
raw: card,
|
|
2682
2807
|
id: card.id,
|
|
@@ -2687,12 +2812,35 @@ class PraxisKpiBandComponent {
|
|
|
2687
2812
|
icon: card.icon,
|
|
2688
2813
|
tone: card.tone || 'neutral',
|
|
2689
2814
|
interactive,
|
|
2815
|
+
active,
|
|
2690
2816
|
className: card.className,
|
|
2691
2817
|
tooltip: card.tooltip,
|
|
2692
2818
|
};
|
|
2693
2819
|
});
|
|
2694
2820
|
this.cdr.markForCheck();
|
|
2695
2821
|
}
|
|
2822
|
+
isCardFilterActive(cardFilter, activeFilter) {
|
|
2823
|
+
if (cardFilter === undefined ||
|
|
2824
|
+
cardFilter === null ||
|
|
2825
|
+
!activeFilter ||
|
|
2826
|
+
typeof activeFilter !== 'object') {
|
|
2827
|
+
return false;
|
|
2828
|
+
}
|
|
2829
|
+
if (typeof cardFilter !== 'object' || Array.isArray(cardFilter)) {
|
|
2830
|
+
return false;
|
|
2831
|
+
}
|
|
2832
|
+
const cardRecord = cardFilter;
|
|
2833
|
+
const cardKeys = Object.keys(cardRecord).sort();
|
|
2834
|
+
const activeKeys = Object.keys(activeFilter).sort();
|
|
2835
|
+
if (cardKeys.length !== activeKeys.length) {
|
|
2836
|
+
return false;
|
|
2837
|
+
}
|
|
2838
|
+
if (cardKeys.length === 0) {
|
|
2839
|
+
return true;
|
|
2840
|
+
}
|
|
2841
|
+
return cardKeys.every((key, idx) => key === activeKeys[idx] &&
|
|
2842
|
+
JSON.stringify(cardRecord[key]) === JSON.stringify(activeFilter[key]));
|
|
2843
|
+
}
|
|
2696
2844
|
onCardClick(card, index) {
|
|
2697
2845
|
if (!card.interactive)
|
|
2698
2846
|
return;
|
|
@@ -2703,7 +2851,7 @@ class PraxisKpiBandComponent {
|
|
|
2703
2851
|
});
|
|
2704
2852
|
}
|
|
2705
2853
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisKpiBandComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2706
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: PraxisKpiBandComponent, isStandalone: true, selector: "praxis-kpi-band", inputs: { config: "config", dataContext: "dataContext", isInteractiveHost: "isInteractiveHost" }, outputs: { cardClick: "cardClick", statsLoaded: "statsLoaded", statsError: "statsError", refreshClick: "refreshClick" }, usesOnChanges: true, ngImport: i0, template: `
|
|
2854
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: PraxisKpiBandComponent, isStandalone: true, selector: "praxis-kpi-band", inputs: { config: "config", dataContext: "dataContext", isInteractiveHost: "isInteractiveHost", activeFilter: "activeFilter" }, outputs: { cardClick: "cardClick", statsLoaded: "statsLoaded", statsError: "statsError", refreshClick: "refreshClick" }, usesOnChanges: true, ngImport: i0, template: `
|
|
2707
2855
|
<div
|
|
2708
2856
|
*ngIf="isVisible"
|
|
2709
2857
|
class="praxis-kpi-band"
|
|
@@ -2746,9 +2894,11 @@ class PraxisKpiBandComponent {
|
|
|
2746
2894
|
*ngFor="let card of evaluatedCards; let i = index"
|
|
2747
2895
|
class="praxis-kpi-card"
|
|
2748
2896
|
[class.interactive]="card.interactive"
|
|
2897
|
+
[class.active]="card.active"
|
|
2749
2898
|
[ngClass]="[card.tone ? 'tone-' + card.tone : 'tone-neutral', card.className || '']"
|
|
2750
2899
|
[attr.role]="card.interactive ? 'button' : null"
|
|
2751
2900
|
[attr.tabindex]="card.interactive ? 0 : null"
|
|
2901
|
+
[attr.aria-pressed]="card.interactive && card.raw.filter !== undefined ? (card.active ? 'true' : 'false') : null"
|
|
2752
2902
|
[matTooltip]="card.tooltip || ''"
|
|
2753
2903
|
(click)="onCardClick(card, i)"
|
|
2754
2904
|
(keydown.enter)="onCardClick(card, i)"
|
|
@@ -2774,7 +2924,7 @@ class PraxisKpiBandComponent {
|
|
|
2774
2924
|
</ng-template>
|
|
2775
2925
|
</div>
|
|
2776
2926
|
</div>
|
|
2777
|
-
`, 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.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.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$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2927
|
+
`, 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.active{border-color:var(--praxis-primary, #0284c7);box-shadow:0 0 0 1px color-mix(in srgb,var(--praxis-primary, #0284c7) 45%,transparent),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.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.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$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2778
2928
|
}
|
|
2779
2929
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisKpiBandComponent, decorators: [{
|
|
2780
2930
|
type: Component,
|
|
@@ -2821,9 +2971,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
2821
2971
|
*ngFor="let card of evaluatedCards; let i = index"
|
|
2822
2972
|
class="praxis-kpi-card"
|
|
2823
2973
|
[class.interactive]="card.interactive"
|
|
2974
|
+
[class.active]="card.active"
|
|
2824
2975
|
[ngClass]="[card.tone ? 'tone-' + card.tone : 'tone-neutral', card.className || '']"
|
|
2825
2976
|
[attr.role]="card.interactive ? 'button' : null"
|
|
2826
2977
|
[attr.tabindex]="card.interactive ? 0 : null"
|
|
2978
|
+
[attr.aria-pressed]="card.interactive && card.raw.filter !== undefined ? (card.active ? 'true' : 'false') : null"
|
|
2827
2979
|
[matTooltip]="card.tooltip || ''"
|
|
2828
2980
|
(click)="onCardClick(card, i)"
|
|
2829
2981
|
(keydown.enter)="onCardClick(card, i)"
|
|
@@ -2849,13 +3001,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
2849
3001
|
</ng-template>
|
|
2850
3002
|
</div>
|
|
2851
3003
|
</div>
|
|
2852
|
-
`, 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"] }]
|
|
3004
|
+
`, 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.active{border-color:var(--praxis-primary, #0284c7);box-shadow:0 0 0 1px color-mix(in srgb,var(--praxis-primary, #0284c7) 45%,transparent),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"] }]
|
|
2853
3005
|
}], propDecorators: { config: [{
|
|
2854
3006
|
type: Input
|
|
2855
3007
|
}], dataContext: [{
|
|
2856
3008
|
type: Input
|
|
2857
3009
|
}], isInteractiveHost: [{
|
|
2858
3010
|
type: Input
|
|
3011
|
+
}], activeFilter: [{
|
|
3012
|
+
type: Input
|
|
2859
3013
|
}], cardClick: [{
|
|
2860
3014
|
type: Output
|
|
2861
3015
|
}], statsLoaded: [{
|
|
@@ -6869,11 +7023,14 @@ class PraxisCrudComponent {
|
|
|
6869
7023
|
}
|
|
6870
7024
|
onKpiCardClick(event) {
|
|
6871
7025
|
this.kpiCardClick.emit(event);
|
|
6872
|
-
if (event.card.filter && typeof event.card.filter === 'object') {
|
|
6873
|
-
this.
|
|
6874
|
-
...(this.
|
|
7026
|
+
if (!this.kpiCardClick.observed && event.card.filter && typeof event.card.filter === 'object') {
|
|
7027
|
+
this.tableFilterCriteria = {
|
|
7028
|
+
...this.resolveFilterCriteria(this.resolvedMetadata),
|
|
6875
7029
|
...event.card.filter,
|
|
6876
7030
|
};
|
|
7031
|
+
if (this.table) {
|
|
7032
|
+
this.table.applyAdvancedFilterCriteria(this.tableFilterCriteria);
|
|
7033
|
+
}
|
|
6877
7034
|
this.cdr.markForCheck();
|
|
6878
7035
|
}
|
|
6879
7036
|
}
|
|
@@ -7054,6 +7211,9 @@ class PraxisCrudComponent {
|
|
|
7054
7211
|
this.snack.open('Nenhum registro selecionado para a operação em lote.', undefined, { duration: 3000 });
|
|
7055
7212
|
return;
|
|
7056
7213
|
}
|
|
7214
|
+
if (this.openingActionLabel()) {
|
|
7215
|
+
return;
|
|
7216
|
+
}
|
|
7057
7217
|
const resourcePath = String(this.resolveResourcePath(this.resolvedMetadata) || '').trim();
|
|
7058
7218
|
const resourceKey = String(this.resolvedMetadata?.resource?.resourceKey ||
|
|
7059
7219
|
this.tableCrudContext?.tableId ||
|
|
@@ -7064,118 +7224,164 @@ class PraxisCrudComponent {
|
|
|
7064
7224
|
(a.id || a.action) === action);
|
|
7065
7225
|
const tableBulkAction = (this.effectiveTableConfig?.actions?.bulk?.actions || []).find((a) => this.normalizeCrudActionName(a.id || a.action) === normalizedAction ||
|
|
7066
7226
|
(a.id || a.action) === action);
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
resolvedBulk =
|
|
7227
|
+
this.openingActionLabel.set(this.resolveOpeningActionLabel(action, event));
|
|
7228
|
+
try {
|
|
7229
|
+
let resolvedBulk = this.resolveSyncBulkExecutionContractForAction(action, normalizedAction, configAction, crudAction, tableBulkAction);
|
|
7230
|
+
if (!resolvedBulk.contract && this.tableCollectionLinks && (!crudAction || !this.hasExplicitOpenBinding(crudAction))) {
|
|
7231
|
+
resolvedBulk = await this.resolveAsyncDiscoveredBulkContractForAction(action, normalizedAction);
|
|
7232
|
+
}
|
|
7233
|
+
const bulkContract = resolvedBulk.contract;
|
|
7234
|
+
const discoveredCatalogAction = resolvedBulk.catalogAction;
|
|
7235
|
+
if (bulkContract) {
|
|
7236
|
+
const selectedCount = Array.isArray(rawTargets) && rawTargets.length > 0 ? rawTargets.length : rows.length;
|
|
7237
|
+
const maxTargets = bulkContract.limits?.maxTargets;
|
|
7238
|
+
if (typeof maxTargets === 'number' && maxTargets > 0 && selectedCount > maxTargets) {
|
|
7239
|
+
this.snack.open(`A seleção atual (${selectedCount} itens) excede o limite máximo permitido (${maxTargets} itens por lote).`, undefined, { duration: 4000 });
|
|
7240
|
+
return;
|
|
7241
|
+
}
|
|
7242
|
+
const requiresResourceVersion = discoveredCatalogAction?.execution?.preconditions?.resourceVersion !== 'NONE';
|
|
7243
|
+
const targets = Array.isArray(rawTargets) && rawTargets.length > 0
|
|
7244
|
+
? rawTargets
|
|
7245
|
+
: await this.resolveBulkTargetsFromRows(rows, requiresResourceVersion);
|
|
7246
|
+
if (!targets.length) {
|
|
7247
|
+
this.snack.open('Nenhum registro selecionado para a operação em lote.', undefined, { duration: 3000 });
|
|
7248
|
+
return;
|
|
7249
|
+
}
|
|
7250
|
+
const columns = this.table?.config?.columns?.length
|
|
7251
|
+
? this.table.config.columns
|
|
7252
|
+
: this.effectiveTableConfig?.columns;
|
|
7253
|
+
const schema = this.resolvedMetadata?.schema || this.resolvedMetadata?.resource?.schema;
|
|
7254
|
+
if (bulkContract.mode === 'UNIFORM_UPDATE') {
|
|
7255
|
+
const actionTitle = configAction?.title ||
|
|
7256
|
+
tableBulkAction?.title ||
|
|
7257
|
+
discoveredCatalogAction?.title;
|
|
7258
|
+
const dialogRef = this.dialog.open(PraxisBulkEditDialogComponent, {
|
|
7259
|
+
width: '680px',
|
|
7260
|
+
maxWidth: '92vw',
|
|
7261
|
+
data: {
|
|
7262
|
+
bulkContract,
|
|
7263
|
+
targets,
|
|
7264
|
+
resourcePath,
|
|
7265
|
+
resourceKey,
|
|
7266
|
+
endpointKey: this.resolvedMetadata?.resource?.endpointKey,
|
|
7267
|
+
apiUrlEntry: this.resolveDiscoveryApiEntry(),
|
|
7268
|
+
columns,
|
|
7269
|
+
schema,
|
|
7270
|
+
title: actionTitle || 'Atualização de Registros em Massa',
|
|
7271
|
+
},
|
|
7272
|
+
panelClass: ['praxis-bulk-dialog-panel'],
|
|
7273
|
+
backdropClass: ['praxis-bulk-dialog-backdrop'],
|
|
7274
|
+
disableClose: true,
|
|
7275
|
+
});
|
|
7276
|
+
dialogRef
|
|
7277
|
+
.afterClosed()
|
|
7278
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
7279
|
+
.subscribe((res) => {
|
|
7280
|
+
this.handleBulkDialogResult(res, discoveredCatalogAction?.execution?.refresh, 'Atualização em lote concluída com sucesso.');
|
|
7281
|
+
});
|
|
7282
|
+
return;
|
|
7283
|
+
}
|
|
7284
|
+
if (bulkContract.mode === 'DOMAIN_COMMAND') {
|
|
7285
|
+
const actionRef = discoveredCatalogAction || crudAction || configAction || {
|
|
7286
|
+
id: action,
|
|
7287
|
+
action,
|
|
7288
|
+
label: configAction?.label || tableBulkAction?.label || action,
|
|
7289
|
+
title: configAction?.title || configAction?.label || tableBulkAction?.label || action,
|
|
7290
|
+
execution: { bulk: bulkContract },
|
|
7291
|
+
};
|
|
7292
|
+
if (!actionRef.execution?.bulk) {
|
|
7293
|
+
actionRef.execution = { ...(actionRef.execution || {}), bulk: bulkContract };
|
|
7294
|
+
}
|
|
7295
|
+
actionRef.title = actionRef.title || actionRef.label || action;
|
|
7296
|
+
const dialogRef = this.dialog.open(PraxisBulkOperationDialogComponent, {
|
|
7297
|
+
width: '680px',
|
|
7298
|
+
maxWidth: '92vw',
|
|
7299
|
+
data: {
|
|
7300
|
+
action: actionRef,
|
|
7301
|
+
targets,
|
|
7302
|
+
resourcePath,
|
|
7303
|
+
resourceKey,
|
|
7304
|
+
endpointKey: this.resolvedMetadata?.resource?.endpointKey,
|
|
7305
|
+
apiUrlEntry: this.resolveDiscoveryApiEntry(),
|
|
7306
|
+
columns,
|
|
7307
|
+
schema,
|
|
7308
|
+
},
|
|
7309
|
+
panelClass: ['praxis-bulk-dialog-panel'],
|
|
7310
|
+
backdropClass: ['praxis-bulk-dialog-backdrop'],
|
|
7311
|
+
disableClose: true,
|
|
7312
|
+
});
|
|
7313
|
+
dialogRef
|
|
7314
|
+
.afterClosed()
|
|
7315
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
7316
|
+
.subscribe((res) => {
|
|
7317
|
+
this.handleBulkDialogResult(res, discoveredCatalogAction?.execution?.refresh, 'Operação em lote executada com sucesso.');
|
|
7318
|
+
});
|
|
7319
|
+
return;
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
7322
|
+
}
|
|
7323
|
+
finally {
|
|
7324
|
+
this.openingActionLabel.set(null);
|
|
7325
|
+
}
|
|
7326
|
+
const row = this.extractSelectedRowFromBulkAction(event);
|
|
7327
|
+
if (row) {
|
|
7328
|
+
this.selectedRow = row;
|
|
7070
7329
|
}
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7330
|
+
await this.onAction(action, row ?? undefined, event);
|
|
7331
|
+
}
|
|
7332
|
+
handleBulkDialogResult(res, refreshPolicy, defaultSuccessMessage) {
|
|
7333
|
+
const exec = res?.execution;
|
|
7334
|
+
if (exec && exec.status) {
|
|
7335
|
+
const status = exec.status;
|
|
7336
|
+
const confirmed = exec.totals?.['confirmed'] ?? 0;
|
|
7337
|
+
const rejected = (exec.totals?.['denied'] ?? 0) +
|
|
7338
|
+
(exec.totals?.['invalid'] ?? 0) +
|
|
7339
|
+
(exec.totals?.['conflict'] ?? 0) +
|
|
7340
|
+
(exec.totals?.['notProcessed'] ?? 0) +
|
|
7341
|
+
(exec.totals?.['unknown'] ?? 0);
|
|
7342
|
+
if (status === 'COMPLETED' && rejected === 0) {
|
|
7343
|
+
this.handleBulkExecutionCompleted(refreshPolicy, true);
|
|
7344
|
+
this.snack.open(defaultSuccessMessage, undefined, { duration: 3000 });
|
|
7078
7345
|
return;
|
|
7079
7346
|
}
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
|
|
7083
|
-
: await this.resolveBulkTargetsFromRows(rows, requiresResourceVersion);
|
|
7084
|
-
if (!targets.length) {
|
|
7085
|
-
this.snack.open('Nenhum registro selecionado para a operação em lote.', undefined, { duration: 3000 });
|
|
7347
|
+
if (status === 'COMPLETED_WITH_ERRORS' || (status === 'COMPLETED' && rejected > 0)) {
|
|
7348
|
+
this.handleBulkExecutionCompleted(refreshPolicy, false);
|
|
7349
|
+
this.snack.open(`Operação em lote concluída parcialmente (${confirmed} efetivado(s), ${rejected} com restrição).`, undefined, { duration: 4000 });
|
|
7086
7350
|
return;
|
|
7087
7351
|
}
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
discoveredCatalogAction?.title;
|
|
7096
|
-
const dialogRef = this.dialog.open(PraxisBulkEditDialogComponent, {
|
|
7097
|
-
width: '680px',
|
|
7098
|
-
maxWidth: '92vw',
|
|
7099
|
-
data: {
|
|
7100
|
-
bulkContract,
|
|
7101
|
-
targets,
|
|
7102
|
-
resourcePath,
|
|
7103
|
-
resourceKey,
|
|
7104
|
-
endpointKey: this.resolvedMetadata?.resource?.endpointKey,
|
|
7105
|
-
apiUrlEntry: this.resolveDiscoveryApiEntry(),
|
|
7106
|
-
columns,
|
|
7107
|
-
schema,
|
|
7108
|
-
title: actionTitle || 'Atualização de Registros em Massa',
|
|
7109
|
-
},
|
|
7110
|
-
panelClass: ['praxis-bulk-dialog-panel'],
|
|
7111
|
-
backdropClass: ['praxis-bulk-dialog-backdrop'],
|
|
7112
|
-
disableClose: true,
|
|
7113
|
-
});
|
|
7114
|
-
dialogRef
|
|
7115
|
-
.afterClosed()
|
|
7116
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
7117
|
-
.subscribe((res) => {
|
|
7118
|
-
if (res?.success) {
|
|
7119
|
-
this.handleBulkExecutionCompleted(discoveredCatalogAction?.execution?.refresh);
|
|
7120
|
-
this.snack.open('Atualização em lote concluída com sucesso.', undefined, { duration: 3000 });
|
|
7121
|
-
}
|
|
7122
|
-
});
|
|
7352
|
+
if (status === 'CANCELLED') {
|
|
7353
|
+
if (confirmed > 0) {
|
|
7354
|
+
this.handleBulkExecutionCompleted(refreshPolicy, false);
|
|
7355
|
+
}
|
|
7356
|
+
this.snack.open(confirmed > 0
|
|
7357
|
+
? `Execução em lote cancelada após efetivar ${confirmed} registro(s).`
|
|
7358
|
+
: 'Execução em lote cancelada pelo operador.', undefined, { duration: 4000 });
|
|
7123
7359
|
return;
|
|
7124
7360
|
}
|
|
7125
|
-
if (
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
action,
|
|
7129
|
-
label: configAction?.label || tableBulkAction?.label || action,
|
|
7130
|
-
title: configAction?.title || configAction?.label || tableBulkAction?.label || action,
|
|
7131
|
-
execution: { bulk: bulkContract },
|
|
7132
|
-
};
|
|
7133
|
-
if (!actionRef.execution?.bulk) {
|
|
7134
|
-
actionRef.execution = { ...(actionRef.execution || {}), bulk: bulkContract };
|
|
7361
|
+
if (status === 'STOPPED' || status === 'RECONCILIATION_REQUIRED') {
|
|
7362
|
+
if (confirmed > 0 || status === 'RECONCILIATION_REQUIRED') {
|
|
7363
|
+
this.handleBulkExecutionCompleted(refreshPolicy, false);
|
|
7135
7364
|
}
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
maxWidth: '92vw',
|
|
7140
|
-
data: {
|
|
7141
|
-
action: actionRef,
|
|
7142
|
-
targets,
|
|
7143
|
-
resourcePath,
|
|
7144
|
-
resourceKey,
|
|
7145
|
-
endpointKey: this.resolvedMetadata?.resource?.endpointKey,
|
|
7146
|
-
apiUrlEntry: this.resolveDiscoveryApiEntry(),
|
|
7147
|
-
columns,
|
|
7148
|
-
schema,
|
|
7149
|
-
},
|
|
7150
|
-
panelClass: ['praxis-bulk-dialog-panel'],
|
|
7151
|
-
backdropClass: ['praxis-bulk-dialog-backdrop'],
|
|
7152
|
-
disableClose: true,
|
|
7153
|
-
});
|
|
7154
|
-
dialogRef
|
|
7155
|
-
.afterClosed()
|
|
7156
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
7157
|
-
.subscribe((res) => {
|
|
7158
|
-
if (res?.success) {
|
|
7159
|
-
this.handleBulkExecutionCompleted(discoveredCatalogAction?.execution?.refresh);
|
|
7160
|
-
this.snack.open('Operação em lote executada com sucesso.', undefined, { duration: 3000 });
|
|
7161
|
-
}
|
|
7162
|
-
});
|
|
7365
|
+
this.snack.open(status === 'RECONCILIATION_REQUIRED'
|
|
7366
|
+
? 'Execução em lote exige reconciliação. Verifique os diagnósticos.'
|
|
7367
|
+
: 'Execução em lote interrompida por restrição. Seleção preservada.', undefined, { duration: 4000 });
|
|
7163
7368
|
return;
|
|
7164
7369
|
}
|
|
7165
7370
|
}
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
this.
|
|
7371
|
+
if (res?.success) {
|
|
7372
|
+
this.handleBulkExecutionCompleted(refreshPolicy, true);
|
|
7373
|
+
this.snack.open(defaultSuccessMessage, undefined, { duration: 3000 });
|
|
7169
7374
|
}
|
|
7170
|
-
await this.onAction(action, row ?? undefined, event);
|
|
7171
7375
|
}
|
|
7172
|
-
handleBulkExecutionCompleted(refreshPolicy) {
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7376
|
+
handleBulkExecutionCompleted(refreshPolicy, clearSelection = true) {
|
|
7377
|
+
if (clearSelection) {
|
|
7378
|
+
try {
|
|
7379
|
+
this.table?.clearSelection?.();
|
|
7380
|
+
this.table?.selection?.clear?.();
|
|
7381
|
+
}
|
|
7382
|
+
catch {
|
|
7383
|
+
// Best-effort table selection cleanup
|
|
7384
|
+
}
|
|
7179
7385
|
}
|
|
7180
7386
|
if (refreshPolicy?.collection !== false) {
|
|
7181
7387
|
this.refreshTable();
|
|
@@ -7185,7 +7391,7 @@ class PraxisCrudComponent {
|
|
|
7185
7391
|
this.onCollectionLinksChange(this.tableCollectionLinks);
|
|
7186
7392
|
}
|
|
7187
7393
|
}
|
|
7188
|
-
async resolveBulkTargetsFromRows(rows, fetchSelfWhenVersionMissing = true) {
|
|
7394
|
+
async resolveBulkTargetsFromRows(rows, fetchSelfWhenVersionMissing = true, fallbackResourcePath) {
|
|
7189
7395
|
const options = this.buildDiscoveryOptions();
|
|
7190
7396
|
const results = new Array(rows.length);
|
|
7191
7397
|
const concurrency = 4;
|
|
@@ -7199,11 +7405,23 @@ class PraxisCrudComponent {
|
|
|
7199
7405
|
(r?.etag != null ? String(r.etag).trim() : null) ||
|
|
7200
7406
|
(r?.eTag != null ? String(r.eTag).trim() : null) ||
|
|
7201
7407
|
undefined;
|
|
7202
|
-
if (fetchSelfWhenVersionMissing && !expectedVersion
|
|
7408
|
+
if (fetchSelfWhenVersionMissing && !expectedVersion) {
|
|
7203
7409
|
try {
|
|
7204
|
-
const
|
|
7410
|
+
const discovery = this.getResourceDiscovery();
|
|
7411
|
+
let selfHref = r?._links?.self
|
|
7412
|
+
? discovery.resolveLinkHref(r._links, 'self', options)
|
|
7413
|
+
: null;
|
|
7414
|
+
if (!selfHref && fallbackResourcePath && id != null) {
|
|
7415
|
+
const candidatePath = `${fallbackResourcePath.replace(/\/+$/, '')}/${encodeURIComponent(String(id))}`;
|
|
7416
|
+
selfHref =
|
|
7417
|
+
discovery.resolveHref?.(candidatePath, options) ??
|
|
7418
|
+
candidatePath;
|
|
7419
|
+
}
|
|
7205
7420
|
if (selfHref) {
|
|
7206
|
-
const resp = await firstValueFrom(this.http.get(selfHref, {
|
|
7421
|
+
const resp = await firstValueFrom(this.http.get(selfHref, {
|
|
7422
|
+
headers: composeHeadersWithVersion(this.resolveDiscoveryApiEntry() ?? {}),
|
|
7423
|
+
observe: 'response',
|
|
7424
|
+
}));
|
|
7207
7425
|
const etagHeader = resp.headers?.get('ETag');
|
|
7208
7426
|
const bodyVersion = resp.body?.data?.resourceVersion ??
|
|
7209
7427
|
resp.body?.resourceVersion ??
|
|
@@ -7294,6 +7512,9 @@ class PraxisCrudComponent {
|
|
|
7294
7512
|
}
|
|
7295
7513
|
ngOnChanges(changes) {
|
|
7296
7514
|
if (!changes['metadata'] && !changes['context'] && !changes['resource']) {
|
|
7515
|
+
if (changes['filterCriteria'] && !changes['filterCriteria'].firstChange) {
|
|
7516
|
+
this.applyFilterCriteria(this.filterCriteria);
|
|
7517
|
+
}
|
|
7297
7518
|
return;
|
|
7298
7519
|
}
|
|
7299
7520
|
if (changes['metadata']) {
|
|
@@ -7375,11 +7596,16 @@ class PraxisCrudComponent {
|
|
|
7375
7596
|
this.emitInspectOnlyDiagnostics(this.resolvedMetadata);
|
|
7376
7597
|
this.tableQueryContext = this.resolveQueryContext(this.resolvedMetadata);
|
|
7377
7598
|
this.tableFilterCriteria = this.resolveFilterCriteria(this.resolvedMetadata);
|
|
7378
|
-
|
|
7379
|
-
this.
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7599
|
+
const nextResourceIdentity = this.resolveCrudResourceIdentity(this.resolvedMetadata);
|
|
7600
|
+
if (nextResourceIdentity !== this.lastAppliedResourceIdentity ||
|
|
7601
|
+
!nextResourceIdentity ||
|
|
7602
|
+
this.isInspectOnly(this.resolvedMetadata)) {
|
|
7603
|
+
this.tableCollectionLinks = null;
|
|
7604
|
+
this.collectionCapabilities = null;
|
|
7605
|
+
this.collectionCapabilitiesRequestHref = null;
|
|
7606
|
+
this.collectionCapabilitiesResolvedHref = null;
|
|
7607
|
+
this.collectionCapabilitiesRequestSeq += 1;
|
|
7608
|
+
}
|
|
7383
7609
|
this.applyResolvedCrudState(this.resolvedMetadata);
|
|
7384
7610
|
}
|
|
7385
7611
|
catch (err) {
|
|
@@ -8225,12 +8451,29 @@ class PraxisCrudComponent {
|
|
|
8225
8451
|
let payload;
|
|
8226
8452
|
try {
|
|
8227
8453
|
const resourceId = this.resolveRowResourceId(row);
|
|
8454
|
+
const preconditions = discoveredAction.execution?.preconditions;
|
|
8455
|
+
const needsItemResourceVersion = !!row &&
|
|
8456
|
+
preconditions?.resourceVersion != null &&
|
|
8457
|
+
preconditions.resourceVersion !== 'NONE' &&
|
|
8458
|
+
preconditions.resourceVersionTransport === 'IF_MATCH' &&
|
|
8459
|
+
!String(preconditions.resourceVersionTargetResourceKey ?? '').trim() &&
|
|
8460
|
+
!String(preconditions.resourceVersionTargetIdField ?? '').trim();
|
|
8461
|
+
let resourceVersion;
|
|
8462
|
+
if (row) {
|
|
8463
|
+
const resolvedTargets = await this.resolveBulkTargetsFromRows([row], needsItemResourceVersion, needsItemResourceVersion ? resourcePath : undefined);
|
|
8464
|
+
resourceVersion = resolvedTargets[0]?.expectedVersion;
|
|
8465
|
+
}
|
|
8228
8466
|
payload = this.getActionOpenAdapter().toPayload(discoveredAction, {
|
|
8229
8467
|
resourcePath,
|
|
8230
8468
|
resourceId,
|
|
8231
8469
|
endpointKey: this.resolvedMetadata?.resource?.endpointKey,
|
|
8232
8470
|
apiUrlEntry: this.resolveDiscoveryApiEntry(),
|
|
8233
8471
|
group: catalog?.group ?? null,
|
|
8472
|
+
...(preconditions?.correlationId &&
|
|
8473
|
+
preconditions.correlationId !== 'NONE'
|
|
8474
|
+
? { correlationId }
|
|
8475
|
+
: {}),
|
|
8476
|
+
...(resourceVersion != null ? { resourceVersion } : {}),
|
|
8234
8477
|
});
|
|
8235
8478
|
const resourceIdentity = runtimeEvent?.resourceIdentity ?? null;
|
|
8236
8479
|
if (resourceId != null && resourceIdentity) {
|
|
@@ -9319,6 +9562,7 @@ class PraxisCrudComponent {
|
|
|
9319
9562
|
<praxis-kpi-band
|
|
9320
9563
|
[config]="resolvedKpiBandConfig"
|
|
9321
9564
|
[dataContext]="kpiDataContext"
|
|
9565
|
+
[activeFilter]="tableFilterCriteria"
|
|
9322
9566
|
[isInteractiveHost]="isKpiInteractiveHost"
|
|
9323
9567
|
(cardClick)="onKpiCardClick($event)"
|
|
9324
9568
|
/>
|
|
@@ -9361,7 +9605,7 @@ class PraxisCrudComponent {
|
|
|
9361
9605
|
}
|
|
9362
9606
|
}
|
|
9363
9607
|
</div>
|
|
9364
|
-
`, isInline: true, styles: [":host{display:block;width:100%;min-width:0;max-width:100%}.praxis-crud-runtime{position:relative;min-width:0}.praxis-crud-opening-status{position:absolute;z-index:6;top:.7rem;right:.7rem;display:inline-flex;align-items:center;gap:.55rem;min-height:2.25rem;max-width:min(24rem,calc(100% - 1.4rem));padding:.48rem .72rem;border:1px solid color-mix(in srgb,var(--md-sys-color-primary, #3f51b5) 32%,transparent);border-radius:999px;color:var(--md-sys-color-on-surface, currentColor);background:color-mix(in srgb,var(--md-sys-color-surface-container-high, #fff) 94%,transparent);box-shadow:0 10px 28px #0f172a29;font-size:.82rem;font-weight:650;line-height:1.25;pointer-events:none}.praxis-crud-opening-status__spinner{width:.95rem;height:.95rem;flex:0 0 auto;border:2px solid color-mix(in srgb,var(--md-sys-color-primary, #3f51b5) 26%,transparent);border-top-color:var(--md-sys-color-primary, #3f51b5);border-radius:50%;animation:praxis-crud-opening-spin .72s linear infinite}@keyframes praxis-crud-opening-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.praxis-crud-opening-status__spinner{animation:none;border-top-color:currentColor}}\n"], dependencies: [{ kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "data", "tableId", "componentInstanceId", "configPersistenceStrategy", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "filterCriteria", "filterTeleport", "widgetId", "queryContext", "aiContext", "aiAssistantVoiceInputMode", "aiAssistantVoiceLanguage", "horizontalScroll", "enableCustomization", "authoringCapability", "dense"], outputs: ["queryContextClear", "queryContextChange", "rowClick", "widgetEvent", "resourceEvent", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "exportAction", "columnReorder", "columnReorderAttempt", "columnResize", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "configChange", "metadataChange", "loadingStateChange", "collectionLinksChange", "selectionChange", "drawerOpen", "drawerClose", "drawerTabChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone", "variant", "alignment", "density", "iconContainer"] }, { kind: "component", type: PraxisKpiBandComponent, selector: "praxis-kpi-band", inputs: ["config", "dataContext", "isInteractiveHost"], outputs: ["cardClick", "statsLoaded", "statsError", "refreshClick"] }] });
|
|
9608
|
+
`, isInline: true, styles: [":host{display:block;width:100%;min-width:0;max-width:100%}.praxis-crud-runtime{position:relative;min-width:0}.praxis-crud-opening-status{position:absolute;z-index:6;top:.7rem;right:.7rem;display:inline-flex;align-items:center;gap:.55rem;min-height:2.25rem;max-width:min(24rem,calc(100% - 1.4rem));padding:.48rem .72rem;border:1px solid color-mix(in srgb,var(--md-sys-color-primary, #3f51b5) 32%,transparent);border-radius:999px;color:var(--md-sys-color-on-surface, currentColor);background:color-mix(in srgb,var(--md-sys-color-surface-container-high, #fff) 94%,transparent);box-shadow:0 10px 28px #0f172a29;font-size:.82rem;font-weight:650;line-height:1.25;pointer-events:none}.praxis-crud-opening-status__spinner{width:.95rem;height:.95rem;flex:0 0 auto;border:2px solid color-mix(in srgb,var(--md-sys-color-primary, #3f51b5) 26%,transparent);border-top-color:var(--md-sys-color-primary, #3f51b5);border-radius:50%;animation:praxis-crud-opening-spin .72s linear infinite}@keyframes praxis-crud-opening-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.praxis-crud-opening-status__spinner{animation:none;border-top-color:currentColor}}\n"], dependencies: [{ kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "data", "tableId", "componentInstanceId", "configPersistenceStrategy", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "filterCriteria", "filterTeleport", "widgetId", "queryContext", "aiContext", "aiAssistantVoiceInputMode", "aiAssistantVoiceLanguage", "horizontalScroll", "enableCustomization", "authoringCapability", "dense"], outputs: ["queryContextClear", "queryContextChange", "rowClick", "widgetEvent", "resourceEvent", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "exportAction", "columnReorder", "columnReorderAttempt", "columnResize", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "configChange", "metadataChange", "loadingStateChange", "collectionLinksChange", "selectionChange", "drawerOpen", "drawerClose", "drawerTabChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone", "variant", "alignment", "density", "iconContainer"] }, { kind: "component", type: PraxisKpiBandComponent, selector: "praxis-kpi-band", inputs: ["config", "dataContext", "isInteractiveHost", "activeFilter"], outputs: ["cardClick", "statsLoaded", "statsError", "refreshClick"] }] });
|
|
9365
9609
|
}
|
|
9366
9610
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisCrudComponent, decorators: [{
|
|
9367
9611
|
type: Component,
|
|
@@ -9380,6 +9624,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
9380
9624
|
<praxis-kpi-band
|
|
9381
9625
|
[config]="resolvedKpiBandConfig"
|
|
9382
9626
|
[dataContext]="kpiDataContext"
|
|
9627
|
+
[activeFilter]="tableFilterCriteria"
|
|
9383
9628
|
[isInteractiveHost]="isKpiInteractiveHost"
|
|
9384
9629
|
(cardClick)="onKpiCardClick($event)"
|
|
9385
9630
|
/>
|
|
@@ -9540,8 +9785,14 @@ class DynamicFormDialogHostComponent {
|
|
|
9540
9785
|
contextIdentity = null;
|
|
9541
9786
|
showResourceIdentityKey = true;
|
|
9542
9787
|
schemaUrl;
|
|
9788
|
+
readUrl;
|
|
9543
9789
|
submitUrl;
|
|
9544
9790
|
submitMethod;
|
|
9791
|
+
submitIdempotencyKey;
|
|
9792
|
+
submitCorrelationId;
|
|
9793
|
+
submitResourceVersion;
|
|
9794
|
+
submitResourceVersionTargetResourceKey;
|
|
9795
|
+
submitResourceVersionTargetIdField;
|
|
9545
9796
|
apiEndpointKey;
|
|
9546
9797
|
apiUrlEntry;
|
|
9547
9798
|
layoutPolicy;
|
|
@@ -9615,8 +9866,19 @@ class DynamicFormDialogHostComponent {
|
|
|
9615
9866
|
this.resourceId = this.data.inputs?.[this.idField];
|
|
9616
9867
|
this.initialValue = this.extractInitialValue(this.data.inputs);
|
|
9617
9868
|
this.schemaUrl = this.data.inputs?.['schemaUrl'] ?? null;
|
|
9869
|
+
this.readUrl = this.data.inputs?.['readUrl'] ?? null;
|
|
9618
9870
|
this.submitUrl = this.data.inputs?.['submitUrl'] ?? null;
|
|
9619
9871
|
this.submitMethod = this.data.inputs?.['submitMethod'] ?? null;
|
|
9872
|
+
this.submitIdempotencyKey =
|
|
9873
|
+
this.data.inputs?.['submitIdempotencyKey'] ?? null;
|
|
9874
|
+
this.submitCorrelationId =
|
|
9875
|
+
this.data.inputs?.['submitCorrelationId'] ?? null;
|
|
9876
|
+
this.submitResourceVersion =
|
|
9877
|
+
this.data.inputs?.['submitResourceVersion'] ?? null;
|
|
9878
|
+
this.submitResourceVersionTargetResourceKey =
|
|
9879
|
+
this.data.inputs?.['submitResourceVersionTargetResourceKey'] ?? null;
|
|
9880
|
+
this.submitResourceVersionTargetIdField =
|
|
9881
|
+
this.data.inputs?.['submitResourceVersionTargetIdField'] ?? null;
|
|
9620
9882
|
this.apiEndpointKey =
|
|
9621
9883
|
this.data.inputs?.['apiEndpointKey'] ??
|
|
9622
9884
|
this.data.metadata?.resource?.endpointKey ??
|
|
@@ -9699,8 +9961,14 @@ class DynamicFormDialogHostComponent {
|
|
|
9699
9961
|
const reserved = new Set([
|
|
9700
9962
|
this.idField,
|
|
9701
9963
|
'schemaUrl',
|
|
9964
|
+
'readUrl',
|
|
9702
9965
|
'submitUrl',
|
|
9703
9966
|
'submitMethod',
|
|
9967
|
+
'submitIdempotencyKey',
|
|
9968
|
+
'submitCorrelationId',
|
|
9969
|
+
'submitResourceVersion',
|
|
9970
|
+
'submitResourceVersionTargetResourceKey',
|
|
9971
|
+
'submitResourceVersionTargetIdField',
|
|
9704
9972
|
'apiEndpointKey',
|
|
9705
9973
|
'apiUrlEntry',
|
|
9706
9974
|
'initialValue',
|
|
@@ -10189,8 +10457,14 @@ class DynamicFormDialogHostComponent {
|
|
|
10189
10457
|
[mode]="mode"
|
|
10190
10458
|
[config]="formConfig"
|
|
10191
10459
|
[schemaUrl]="schemaUrl"
|
|
10460
|
+
[readUrl]="readUrl"
|
|
10192
10461
|
[submitUrl]="submitUrl"
|
|
10193
10462
|
[submitMethod]="submitMethod"
|
|
10463
|
+
[submitIdempotencyKey]="submitIdempotencyKey"
|
|
10464
|
+
[submitCorrelationId]="submitCorrelationId"
|
|
10465
|
+
[submitResourceVersion]="submitResourceVersion"
|
|
10466
|
+
[submitResourceVersionTargetResourceKey]="submitResourceVersionTargetResourceKey"
|
|
10467
|
+
[submitResourceVersionTargetIdField]="submitResourceVersionTargetIdField"
|
|
10194
10468
|
[apiEndpointKey]="apiEndpointKey"
|
|
10195
10469
|
[apiUrlEntry]="apiUrlEntry"
|
|
10196
10470
|
[configPersistenceStrategy]="formConfigPersistenceStrategy"
|
|
@@ -10313,8 +10587,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
10313
10587
|
[mode]="mode"
|
|
10314
10588
|
[config]="formConfig"
|
|
10315
10589
|
[schemaUrl]="schemaUrl"
|
|
10590
|
+
[readUrl]="readUrl"
|
|
10316
10591
|
[submitUrl]="submitUrl"
|
|
10317
10592
|
[submitMethod]="submitMethod"
|
|
10593
|
+
[submitIdempotencyKey]="submitIdempotencyKey"
|
|
10594
|
+
[submitCorrelationId]="submitCorrelationId"
|
|
10595
|
+
[submitResourceVersion]="submitResourceVersion"
|
|
10596
|
+
[submitResourceVersionTargetResourceKey]="submitResourceVersionTargetResourceKey"
|
|
10597
|
+
[submitResourceVersionTargetIdField]="submitResourceVersionTargetIdField"
|
|
10318
10598
|
[apiEndpointKey]="apiEndpointKey"
|
|
10319
10599
|
[apiUrlEntry]="apiUrlEntry"
|
|
10320
10600
|
[configPersistenceStrategy]="formConfigPersistenceStrategy"
|
|
@@ -11820,7 +12100,10 @@ class PraxisResourcePage {
|
|
|
11820
12100
|
activeScopeFilter = signal({}, ...(ngDevMode ? [{ debugName: "activeScopeFilter" }] : /* istanbul ignore next */ []));
|
|
11821
12101
|
effectiveResource = computed(() => {
|
|
11822
12102
|
this.inputsVersion();
|
|
11823
|
-
return this.resource ||
|
|
12103
|
+
return (this.resource ||
|
|
12104
|
+
this.resourcePath ||
|
|
12105
|
+
this.routeResourceSignal() ||
|
|
12106
|
+
this.extractResourcePathFromMetadata(this.metadata || this.routeMetadataSignal()));
|
|
11824
12107
|
}, ...(ngDevMode ? [{ debugName: "effectiveResource" }] : /* istanbul ignore next */ []));
|
|
11825
12108
|
effectiveTitle = computed(() => {
|
|
11826
12109
|
this.inputsVersion();
|
|
@@ -12031,9 +12314,8 @@ class PraxisResourcePage {
|
|
|
12031
12314
|
}
|
|
12032
12315
|
if (this.route.data) {
|
|
12033
12316
|
this.routeSub.add(this.route.data.subscribe((d) => {
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
this.routeResourceSignal.set(nextRes);
|
|
12317
|
+
if ('resource' in d || 'resourcePath' in d) {
|
|
12318
|
+
this.routeResourceSignal.set(d['resource'] || d['resourcePath'] || null);
|
|
12037
12319
|
}
|
|
12038
12320
|
this.extractRouteData(d);
|
|
12039
12321
|
}));
|
|
@@ -12076,10 +12358,17 @@ class PraxisResourcePage {
|
|
|
12076
12358
|
const cardFilter = event.card.filter;
|
|
12077
12359
|
if (cardFilter !== undefined && cardFilter !== null) {
|
|
12078
12360
|
const sb = this.effectiveScopeBar();
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12361
|
+
const matched = sb?.items?.find((item) => this.areScopeFiltersEqual(item.filter, cardFilter));
|
|
12362
|
+
if (matched) {
|
|
12363
|
+
this.onScopeChange(matched);
|
|
12364
|
+
}
|
|
12365
|
+
else {
|
|
12366
|
+
const normalized = this.normalizeScopeFilterExpression(cardFilter);
|
|
12367
|
+
if (Object.keys(normalized).length === 0) {
|
|
12368
|
+
this.onScopeClear();
|
|
12369
|
+
}
|
|
12370
|
+
else {
|
|
12371
|
+
this.activeScopeFilter.set(normalized);
|
|
12083
12372
|
}
|
|
12084
12373
|
}
|
|
12085
12374
|
}
|
|
@@ -12088,6 +12377,30 @@ class PraxisResourcePage {
|
|
|
12088
12377
|
applyItemFilter(item) {
|
|
12089
12378
|
this.activeScopeFilter.set(this.normalizeScopeFilterExpression(item.filter));
|
|
12090
12379
|
}
|
|
12380
|
+
extractResourcePathFromMetadata(metaSource) {
|
|
12381
|
+
if (!metaSource)
|
|
12382
|
+
return null;
|
|
12383
|
+
if (typeof metaSource === 'string') {
|
|
12384
|
+
const trimmed = metaSource.trim();
|
|
12385
|
+
if (!trimmed)
|
|
12386
|
+
return null;
|
|
12387
|
+
if (trimmed.startsWith('{')) {
|
|
12388
|
+
try {
|
|
12389
|
+
const parsed = JSON.parse(trimmed);
|
|
12390
|
+
return (parsed?.resource?.path ||
|
|
12391
|
+
parsed?.table?.resourcePath ||
|
|
12392
|
+
null);
|
|
12393
|
+
}
|
|
12394
|
+
catch {
|
|
12395
|
+
return null;
|
|
12396
|
+
}
|
|
12397
|
+
}
|
|
12398
|
+
return trimmed;
|
|
12399
|
+
}
|
|
12400
|
+
return (metaSource.resource?.path ||
|
|
12401
|
+
metaSource.table?.resourcePath ||
|
|
12402
|
+
null);
|
|
12403
|
+
}
|
|
12091
12404
|
normalizeScopeFilterExpression(filter) {
|
|
12092
12405
|
if (filter && typeof filter === 'object' && !Array.isArray(filter)) {
|
|
12093
12406
|
return { ...filter };
|
|
@@ -12142,13 +12455,19 @@ class PraxisResourcePage {
|
|
|
12142
12455
|
return {};
|
|
12143
12456
|
}
|
|
12144
12457
|
areScopeFiltersEqual(a, b) {
|
|
12458
|
+
if (a === undefined || a === null || b === undefined || b === null) {
|
|
12459
|
+
return false;
|
|
12460
|
+
}
|
|
12145
12461
|
const normA = this.normalizeScopeFilterExpression(a);
|
|
12146
12462
|
const normB = this.normalizeScopeFilterExpression(b);
|
|
12147
12463
|
const keysA = Object.keys(normA).sort();
|
|
12148
12464
|
const keysB = Object.keys(normB).sort();
|
|
12149
|
-
if (keysA.length
|
|
12465
|
+
if (keysA.length !== keysB.length) {
|
|
12150
12466
|
return false;
|
|
12151
12467
|
}
|
|
12468
|
+
if (keysA.length === 0) {
|
|
12469
|
+
return true;
|
|
12470
|
+
}
|
|
12152
12471
|
return keysA.every((key, idx) => key === keysB[idx] &&
|
|
12153
12472
|
JSON.stringify(normA[key]) === JSON.stringify(normB[key]));
|
|
12154
12473
|
}
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/crud",
|
|
3
|
-
"version": "10.0.0-rc.
|
|
3
|
+
"version": "10.0.0-rc.16",
|
|
4
4
|
"description": "CRUD building blocks for Praxis UI: integrates dynamic forms and tables with unified configuration and services.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^21.0.0",
|
|
7
7
|
"@angular/core": "^21.0.0",
|
|
8
|
-
"@praxisui/dynamic-form": "^10.0.0-rc.
|
|
9
|
-
"@praxisui/table": "^10.0.0-rc.
|
|
10
|
-
"@praxisui/core": "^10.0.0-rc.
|
|
11
|
-
"@praxisui/dynamic-fields": "^10.0.0-rc.
|
|
12
|
-
"@praxisui/settings-panel": "^10.0.0-rc.
|
|
8
|
+
"@praxisui/dynamic-form": "^10.0.0-rc.16",
|
|
9
|
+
"@praxisui/table": "^10.0.0-rc.16",
|
|
10
|
+
"@praxisui/core": "^10.0.0-rc.16",
|
|
11
|
+
"@praxisui/dynamic-fields": "^10.0.0-rc.16",
|
|
12
|
+
"@praxisui/settings-panel": "^10.0.0-rc.16",
|
|
13
13
|
"@angular/cdk": "^21.0.0",
|
|
14
14
|
"@angular/forms": "^21.0.0",
|
|
15
15
|
"@angular/material": "^21.0.0",
|
|
16
16
|
"@angular/router": "^21.0.0",
|
|
17
|
-
"@praxisui/ai": "^10.0.0-rc.
|
|
17
|
+
"@praxisui/ai": "^10.0.0-rc.16",
|
|
18
18
|
"rxjs": "~7.8.0"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
package/types/praxisui-crud.d.ts
CHANGED
|
@@ -354,6 +354,7 @@ declare class PraxisCrudComponent implements OnChanges {
|
|
|
354
354
|
onTableDrawerOpen(event: DrawerOpenEvent<DynamicDataRecord>): void;
|
|
355
355
|
onTableDrawerClose(): void;
|
|
356
356
|
onBulkAction(event: any): Promise<void>;
|
|
357
|
+
private handleBulkDialogResult;
|
|
357
358
|
private handleBulkExecutionCompleted;
|
|
358
359
|
private resolveBulkTargetsFromRows;
|
|
359
360
|
private resolveSyncBulkExecutionContractForAction;
|
|
@@ -622,8 +623,14 @@ declare class DynamicFormDialogHostComponent implements OnInit, AfterViewInit {
|
|
|
622
623
|
contextIdentity: MaterializedResourceIdentity | null;
|
|
623
624
|
showResourceIdentityKey: boolean;
|
|
624
625
|
schemaUrl?: string | null;
|
|
626
|
+
readUrl?: string | null;
|
|
625
627
|
submitUrl?: string | null;
|
|
626
628
|
submitMethod?: string | null;
|
|
629
|
+
submitIdempotencyKey?: string | null;
|
|
630
|
+
submitCorrelationId?: string | null;
|
|
631
|
+
submitResourceVersion?: string | number | null;
|
|
632
|
+
submitResourceVersionTargetResourceKey?: string | null;
|
|
633
|
+
submitResourceVersionTargetIdField?: string | null;
|
|
627
634
|
apiEndpointKey?: ApiEndpoint | string | null;
|
|
628
635
|
apiUrlEntry?: ApiUrlEntry | null;
|
|
629
636
|
layoutPolicy?: DynamicFormLayoutPolicy | null;
|
|
@@ -961,6 +968,7 @@ interface EvaluatedKpiCard {
|
|
|
961
968
|
icon?: string;
|
|
962
969
|
tone?: string;
|
|
963
970
|
interactive: boolean;
|
|
971
|
+
active?: boolean;
|
|
964
972
|
className?: string;
|
|
965
973
|
tooltip?: string;
|
|
966
974
|
}
|
|
@@ -973,6 +981,11 @@ declare class PraxisKpiBandComponent implements OnInit, OnChanges {
|
|
|
973
981
|
* para cliques em cards. Quando explicitamente falso, evita a falsa affordance de botões.
|
|
974
982
|
*/
|
|
975
983
|
isInteractiveHost?: boolean;
|
|
984
|
+
/**
|
|
985
|
+
* Filtro ativo sincronizado pelo host (ex.: PraxisCrudComponent / PraxisResourcePage)
|
|
986
|
+
* para destacar visualmente o card de KPI correspondente.
|
|
987
|
+
*/
|
|
988
|
+
activeFilter?: Record<string, unknown> | null;
|
|
976
989
|
cardClick: EventEmitter<{
|
|
977
990
|
card: PraxisKpiBandCard;
|
|
978
991
|
index: number;
|
|
@@ -1002,9 +1015,10 @@ declare class PraxisKpiBandComponent implements OnInit, OnChanges {
|
|
|
1002
1015
|
private resolveSourceUrl;
|
|
1003
1016
|
private fetchFromSource;
|
|
1004
1017
|
recomputeCards(): void;
|
|
1018
|
+
private isCardFilterActive;
|
|
1005
1019
|
onCardClick(card: EvaluatedKpiCard, index: number): void;
|
|
1006
1020
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<PraxisKpiBandComponent, never>;
|
|
1007
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<PraxisKpiBandComponent, "praxis-kpi-band", never, { "config": { "alias": "config"; "required": false; }; "dataContext": { "alias": "dataContext"; "required": false; }; "isInteractiveHost": { "alias": "isInteractiveHost"; "required": false; }; }, { "cardClick": "cardClick"; "statsLoaded": "statsLoaded"; "statsError": "statsError"; "refreshClick": "refreshClick"; }, never, never, true, never>;
|
|
1021
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<PraxisKpiBandComponent, "praxis-kpi-band", never, { "config": { "alias": "config"; "required": false; }; "dataContext": { "alias": "dataContext"; "required": false; }; "isInteractiveHost": { "alias": "isInteractiveHost"; "required": false; }; "activeFilter": { "alias": "activeFilter"; "required": false; }; }, { "cardClick": "cardClick"; "statsLoaded": "statsLoaded"; "statsError": "statsError"; "refreshClick": "refreshClick"; }, never, never, true, never>;
|
|
1008
1022
|
}
|
|
1009
1023
|
|
|
1010
1024
|
/**
|
|
@@ -1135,6 +1149,7 @@ declare class PraxisResourcePage implements OnInit, OnChanges, OnDestroy {
|
|
|
1135
1149
|
data: any;
|
|
1136
1150
|
}): void;
|
|
1137
1151
|
private applyItemFilter;
|
|
1152
|
+
private extractResourcePathFromMetadata;
|
|
1138
1153
|
private normalizeScopeFilterExpression;
|
|
1139
1154
|
private areScopeFiltersEqual;
|
|
1140
1155
|
private extractRouteData;
|
|
@@ -1159,11 +1174,13 @@ declare class PraxisBulkOperationDialogComponent implements OnInit {
|
|
|
1159
1174
|
private readonly dialogRef;
|
|
1160
1175
|
private readonly bulkClient;
|
|
1161
1176
|
private readonly destroyRef;
|
|
1177
|
+
parametersForm?: PraxisDynamicForm;
|
|
1162
1178
|
readonly step: _angular_core.WritableSignal<BulkDialogStep>;
|
|
1163
1179
|
readonly proposal: _angular_core.WritableSignal<_praxisui_core.BulkProposal | null>;
|
|
1164
1180
|
readonly execution: _angular_core.WritableSignal<_praxisui_core.BulkExecution | null>;
|
|
1165
1181
|
readonly errorMessage: _angular_core.WritableSignal<string | null>;
|
|
1166
1182
|
readonly parametersValue: _angular_core.WritableSignal<Record<string, unknown>>;
|
|
1183
|
+
readonly parametersFormValid: _angular_core.WritableSignal<boolean>;
|
|
1167
1184
|
readonly proposalItems: _angular_core.WritableSignal<BulkProposalItemResult<_praxisui_core.BulkWireId>[]>;
|
|
1168
1185
|
readonly proposalNextCursor: _angular_core.WritableSignal<string | null>;
|
|
1169
1186
|
readonly loadingProposalResults: _angular_core.WritableSignal<boolean>;
|
|
@@ -1181,7 +1198,10 @@ declare class PraxisBulkOperationDialogComponent implements OnInit {
|
|
|
1181
1198
|
hasParametersSchema(): boolean;
|
|
1182
1199
|
parametersSchemaUrl(): string | null;
|
|
1183
1200
|
getParametersFormId(): string;
|
|
1201
|
+
onParametersFormReady(event: any): void;
|
|
1184
1202
|
onParametersChange(event: any): void;
|
|
1203
|
+
private getMissingRequiredParameterNames;
|
|
1204
|
+
canEvaluateParameters(): boolean;
|
|
1185
1205
|
isBlockedByAtomicity(): boolean;
|
|
1186
1206
|
isConfirmDisabled(): boolean;
|
|
1187
1207
|
isDiagnosticError(diag: any): boolean;
|
|
@@ -1218,6 +1238,7 @@ interface PraxisBulkEditDialogData {
|
|
|
1218
1238
|
}
|
|
1219
1239
|
type BulkEditStep = 'FIELDS' | 'EVALUATING' | 'PREVIEW' | 'CONFIRMING' | 'RESULT';
|
|
1220
1240
|
interface EditableFieldRow {
|
|
1241
|
+
id: string;
|
|
1221
1242
|
field: string;
|
|
1222
1243
|
operator: 'SET' | 'CLEAR';
|
|
1223
1244
|
value: string;
|
|
@@ -1253,9 +1274,11 @@ declare class PraxisBulkEditDialogComponent {
|
|
|
1253
1274
|
private getFieldDescriptor;
|
|
1254
1275
|
isFieldClearable(fieldName: string): boolean;
|
|
1255
1276
|
hasAvailableFieldsToAdd(): boolean;
|
|
1277
|
+
private nextRowId;
|
|
1256
1278
|
isFieldSelectedInOtherRow(field: string, currentRow: EditableFieldRow): boolean;
|
|
1257
1279
|
addFieldRow(): void;
|
|
1258
1280
|
removeFieldRow(index: number): void;
|
|
1281
|
+
getCurrencyFieldLabel(fieldName: string): string;
|
|
1259
1282
|
getFieldLabel(fieldName: string): string;
|
|
1260
1283
|
getFieldType(fieldName: string): 'currency' | 'number' | 'date' | 'boolean' | 'enum' | 'string';
|
|
1261
1284
|
getFieldEnumOptions(fieldName: string): Array<{
|