@praxisui/crud 10.0.0-rc.16 → 10.0.0-rc.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -365,7 +365,9 @@ and materializes them in the contextual bulk selection toolbar:
365
365
  executions until terminal state (`COMPLETED`, `COMPLETED_WITH_ERRORS`,
366
366
  `CANCELLED`, `STOPPED`, `RECONCILIATION_REQUIRED`), aggregates all rejection
367
367
  categories (`denied`, `invalid`, `conflict`, `notProcessed`, `unknown`),
368
- clears table selection, refreshes the collection, and re-fetches capabilities
368
+ clears table selection on full success or deselects only confirmed items on
369
+ partial outcomes so rejected/unprocessed rows remain selected across
370
+ `persistOnDataUpdate: true` table refreshes, and re-fetches capabilities
369
371
  when `refresh.capabilities === true`.
370
372
 
371
373
  Unavailable bulk operations (`availability.allowed === false`) remain visible
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-10-14T20:20:10.826Z",
3
+ "generatedAt": "2026-10-14T22:05:37.957Z",
4
4
  "packageName": "@praxisui/crud",
5
- "packageVersion": "10.0.0-rc.16",
5
+ "packageVersion": "10.0.0-rc.17",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 2,
@@ -522,6 +522,7 @@ class PraxisBulkOperationDialogComponent {
522
522
  this.dialogRef.close({
523
523
  success: isSuccess,
524
524
  execution: exec,
525
+ results: this.executionItems(),
525
526
  });
526
527
  }
527
528
  onCancel() {
@@ -1771,6 +1772,7 @@ class PraxisBulkEditDialogComponent {
1771
1772
  this.dialogRef.close({
1772
1773
  success: isSuccess,
1773
1774
  execution: exec,
1775
+ results: this.executionItems(),
1774
1776
  });
1775
1777
  }
1776
1778
  onCancel() {
@@ -7227,8 +7229,21 @@ class PraxisCrudComponent {
7227
7229
  this.openingActionLabel.set(this.resolveOpeningActionLabel(action, event));
7228
7230
  try {
7229
7231
  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
+ if ((!resolvedBulk.contract ||
7233
+ (!resolvedBulk.catalogAction &&
7234
+ resolvedBulk.contract.mode === 'DOMAIN_COMMAND' &&
7235
+ !configAction?.requestSchemaId &&
7236
+ !configAction?.commandParameterDescriptors &&
7237
+ !crudAction?.requestSchemaId)) &&
7238
+ this.tableCollectionLinks &&
7239
+ (!crudAction || !this.hasExplicitOpenBinding(crudAction))) {
7240
+ const asyncBulk = await this.resolveAsyncDiscoveredBulkContractForAction(action, normalizedAction);
7241
+ if (asyncBulk.contract || asyncBulk.catalogAction) {
7242
+ resolvedBulk = {
7243
+ contract: resolvedBulk.contract ?? asyncBulk.contract,
7244
+ catalogAction: resolvedBulk.catalogAction ?? asyncBulk.catalogAction,
7245
+ };
7246
+ }
7232
7247
  }
7233
7248
  const bulkContract = resolvedBulk.contract;
7234
7249
  const discoveredCatalogAction = resolvedBulk.catalogAction;
@@ -7239,14 +7254,22 @@ class PraxisCrudComponent {
7239
7254
  this.snack.open(`A seleção atual (${selectedCount} itens) excede o limite máximo permitido (${maxTargets} itens por lote).`, undefined, { duration: 4000 });
7240
7255
  return;
7241
7256
  }
7242
- const requiresResourceVersion = discoveredCatalogAction?.execution?.preconditions?.resourceVersion !== 'NONE';
7257
+ const resourceVersionPrecondition = discoveredCatalogAction?.execution?.preconditions?.resourceVersion ??
7258
+ configAction?.execution?.preconditions?.resourceVersion ??
7259
+ crudAction?.execution?.preconditions?.resourceVersion ??
7260
+ bulkContract?.preconditions?.resourceVersion;
7261
+ const requiresResourceVersion = resourceVersionPrecondition !== 'NONE';
7243
7262
  const targets = Array.isArray(rawTargets) && rawTargets.length > 0
7244
7263
  ? rawTargets
7245
- : await this.resolveBulkTargetsFromRows(rows, requiresResourceVersion);
7264
+ : await this.resolveBulkTargetsFromRows(rows, requiresResourceVersion, resourcePath);
7246
7265
  if (!targets.length) {
7247
7266
  this.snack.open('Nenhum registro selecionado para a operação em lote.', undefined, { duration: 3000 });
7248
7267
  return;
7249
7268
  }
7269
+ const refreshPolicy = discoveredCatalogAction?.execution?.refresh ??
7270
+ configAction?.execution?.refresh ??
7271
+ crudAction?.execution?.refresh ??
7272
+ bulkContract?.refresh;
7250
7273
  const columns = this.table?.config?.columns?.length
7251
7274
  ? this.table.config.columns
7252
7275
  : this.effectiveTableConfig?.columns;
@@ -7277,7 +7300,7 @@ class PraxisCrudComponent {
7277
7300
  .afterClosed()
7278
7301
  .pipe(takeUntilDestroyed(this.destroyRef))
7279
7302
  .subscribe((res) => {
7280
- this.handleBulkDialogResult(res, discoveredCatalogAction?.execution?.refresh, 'Atualização em lote concluída com sucesso.');
7303
+ this.handleBulkDialogResult(res, refreshPolicy, 'Atualização em lote concluída com sucesso.');
7281
7304
  });
7282
7305
  return;
7283
7306
  }
@@ -7314,7 +7337,7 @@ class PraxisCrudComponent {
7314
7337
  .afterClosed()
7315
7338
  .pipe(takeUntilDestroyed(this.destroyRef))
7316
7339
  .subscribe((res) => {
7317
- this.handleBulkDialogResult(res, discoveredCatalogAction?.execution?.refresh, 'Operação em lote executada com sucesso.');
7340
+ this.handleBulkDialogResult(res, refreshPolicy, 'Operação em lote executada com sucesso.');
7318
7341
  });
7319
7342
  return;
7320
7343
  }
@@ -7331,6 +7354,15 @@ class PraxisCrudComponent {
7331
7354
  }
7332
7355
  handleBulkDialogResult(res, refreshPolicy, defaultSuccessMessage) {
7333
7356
  const exec = res?.execution;
7357
+ const confirmedIds = Array.isArray(res?.results)
7358
+ ? new Set(res.results
7359
+ .filter((item) => (item?.status === 'CONFIRMED' ||
7360
+ item?.status === 'UNCHANGED' ||
7361
+ item?.outcome === 'CONFIRMED' ||
7362
+ item?.outcome === 'UNCHANGED') &&
7363
+ item?.id != null)
7364
+ .map((item) => String(item.id)))
7365
+ : undefined;
7334
7366
  if (exec && exec.status) {
7335
7367
  const status = exec.status;
7336
7368
  const confirmed = exec.totals?.['confirmed'] ?? 0;
@@ -7345,13 +7377,13 @@ class PraxisCrudComponent {
7345
7377
  return;
7346
7378
  }
7347
7379
  if (status === 'COMPLETED_WITH_ERRORS' || (status === 'COMPLETED' && rejected > 0)) {
7348
- this.handleBulkExecutionCompleted(refreshPolicy, false);
7380
+ this.handleBulkExecutionCompleted(refreshPolicy, false, confirmedIds);
7349
7381
  this.snack.open(`Operação em lote concluída parcialmente (${confirmed} efetivado(s), ${rejected} com restrição).`, undefined, { duration: 4000 });
7350
7382
  return;
7351
7383
  }
7352
7384
  if (status === 'CANCELLED') {
7353
7385
  if (confirmed > 0) {
7354
- this.handleBulkExecutionCompleted(refreshPolicy, false);
7386
+ this.handleBulkExecutionCompleted(refreshPolicy, false, confirmedIds);
7355
7387
  }
7356
7388
  this.snack.open(confirmed > 0
7357
7389
  ? `Execução em lote cancelada após efetivar ${confirmed} registro(s).`
@@ -7360,7 +7392,7 @@ class PraxisCrudComponent {
7360
7392
  }
7361
7393
  if (status === 'STOPPED' || status === 'RECONCILIATION_REQUIRED') {
7362
7394
  if (confirmed > 0 || status === 'RECONCILIATION_REQUIRED') {
7363
- this.handleBulkExecutionCompleted(refreshPolicy, false);
7395
+ this.handleBulkExecutionCompleted(refreshPolicy, false, confirmedIds);
7364
7396
  }
7365
7397
  this.snack.open(status === 'RECONCILIATION_REQUIRED'
7366
7398
  ? 'Execução em lote exige reconciliação. Verifique os diagnósticos.'
@@ -7373,7 +7405,7 @@ class PraxisCrudComponent {
7373
7405
  this.snack.open(defaultSuccessMessage, undefined, { duration: 3000 });
7374
7406
  }
7375
7407
  }
7376
- handleBulkExecutionCompleted(refreshPolicy, clearSelection = true) {
7408
+ handleBulkExecutionCompleted(refreshPolicy, clearSelection = true, confirmedIds) {
7377
7409
  if (clearSelection) {
7378
7410
  try {
7379
7411
  this.table?.clearSelection?.();
@@ -7383,6 +7415,23 @@ class PraxisCrudComponent {
7383
7415
  // Best-effort table selection cleanup
7384
7416
  }
7385
7417
  }
7418
+ else if (confirmedIds && confirmedIds.size > 0) {
7419
+ try {
7420
+ const selectionModel = this.table?.selection;
7421
+ if (selectionModel && Array.isArray(selectionModel.selected)) {
7422
+ const rowsToDeselect = selectionModel.selected.filter((row) => {
7423
+ const id = this.resolveRowResourceId(row);
7424
+ return id != null && confirmedIds.has(String(id));
7425
+ });
7426
+ if (rowsToDeselect.length > 0 && typeof selectionModel.deselect === 'function') {
7427
+ selectionModel.deselect(...rowsToDeselect);
7428
+ }
7429
+ }
7430
+ }
7431
+ catch {
7432
+ // Best-effort partial selection reconciliation
7433
+ }
7434
+ }
7386
7435
  if (refreshPolicy?.collection !== false) {
7387
7436
  this.refreshTable();
7388
7437
  }
@@ -7447,12 +7496,6 @@ class PraxisCrudComponent {
7447
7496
  return results;
7448
7497
  }
7449
7498
  resolveSyncBulkExecutionContractForAction(action, normalizedAction, configAction, crudAction, tableBulkAction) {
7450
- const direct = configAction?.execution?.bulk ||
7451
- crudAction?.execution?.bulk ||
7452
- tableBulkAction?.execution?.bulk;
7453
- if (direct) {
7454
- return { contract: direct };
7455
- }
7456
7499
  const matchKey = (candidate) => {
7457
7500
  const id = String(candidate?.id || candidate?.action || candidate?.operationId || '').trim();
7458
7501
  const norm = this.normalizeCrudActionName(id).toLowerCase();
@@ -7461,8 +7504,12 @@ class PraxisCrudComponent {
7461
7504
  String(candidate?.operationId || '').trim() === action);
7462
7505
  };
7463
7506
  const capAction = (this.collectionCapabilities?.actions || []).find((a) => matchKey(a) && a.execution?.bulk);
7464
- if (capAction?.execution?.bulk) {
7465
- return { contract: capAction.execution.bulk, catalogAction: capAction };
7507
+ const direct = configAction?.execution?.bulk ||
7508
+ crudAction?.execution?.bulk ||
7509
+ tableBulkAction?.execution?.bulk ||
7510
+ capAction?.execution?.bulk;
7511
+ if (direct) {
7512
+ return { contract: direct, ...(capAction ? { catalogAction: capAction } : {}) };
7466
7513
  }
7467
7514
  const capOp = Object.values(this.collectionCapabilities?.operations || {}).find((op) => matchKey(op) && op?.bulk);
7468
7515
  if (capOp?.bulk) {
@@ -9022,7 +9069,7 @@ class PraxisCrudComponent {
9022
9069
  allowSelectAll: prevSelection?.allowSelectAll ?? true,
9023
9070
  checkboxPosition: prevSelection?.checkboxPosition || 'start',
9024
9071
  persistSelection: prevSelection?.persistSelection ?? true,
9025
- persistOnDataUpdate: prevSelection?.persistOnDataUpdate ?? false,
9072
+ persistOnDataUpdate: prevSelection?.persistOnDataUpdate ?? true,
9026
9073
  };
9027
9074
  }
9028
9075
  changed = true;
@@ -9339,6 +9386,12 @@ class PraxisCrudComponent {
9339
9386
  items.push({
9340
9387
  id: action.id,
9341
9388
  label: action.title || action.id,
9389
+ title: action.title || action.id,
9390
+ description: action.description,
9391
+ operationId: action.operationId,
9392
+ requestSchemaId: action.requestSchemaId,
9393
+ requestSchemaUrl: action.requestSchemaUrl,
9394
+ commandParameterDescriptors: action.commandParameterDescriptors,
9342
9395
  icon: action.icon || (action.execution?.bulk?.mode === 'UNIFORM_UPDATE' ? 'edit_note' : 'playlist_add_check'),
9343
9396
  action: action.id,
9344
9397
  requiresSelection: true,
@@ -9346,7 +9399,7 @@ class PraxisCrudComponent {
9346
9399
  tooltip: !available
9347
9400
  ? translateUnavailableWorkflowMessage(this.i18n, capabilities.operations?.[action.id]?.availability || action.availability) || action.description
9348
9401
  : action.description,
9349
- execution: { bulk: action.execution.bulk },
9402
+ execution: action.execution,
9350
9403
  });
9351
9404
  }
9352
9405
  for (const [opKey, op] of Object.entries(capabilities.operations || {})) {
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@praxisui/crud",
3
- "version": "10.0.0-rc.16",
3
+ "version": "10.0.0-rc.17",
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.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",
8
+ "@praxisui/dynamic-form": "^10.0.0-rc.17",
9
+ "@praxisui/table": "^10.0.0-rc.17",
10
+ "@praxisui/core": "^10.0.0-rc.17",
11
+ "@praxisui/dynamic-fields": "^10.0.0-rc.17",
12
+ "@praxisui/settings-panel": "^10.0.0-rc.17",
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.16",
17
+ "@praxisui/ai": "^10.0.0-rc.17",
18
18
  "rxjs": "~7.8.0"
19
19
  },
20
20
  "dependencies": {