Skip to content

feat(results): bulk delete selected rows, and bump to 0.4.1 - #7

Merged
lr00429 merged 2 commits into
mainfrom
feat/bulk-delete
Sep 23, 2026
Merged

lr00429 merged 2 commits into
mainfrom
feat/bulk-delete

Conversation

@lr00429

@lr00429 lr00429 commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Bulk delete

Deleting was one row at a time: trash button, confirm, repeat. Clearing out a few hundred DocuSign statuses or ApexLogs meant a few hundred trips through that dialog.

Selecting. A checkbox column appears whenever the rows are deletable — the same rule as the trash button (the result's org is the active one, and it has a FROM-object). Shift-click ticks a range in displayed order, so it follows the current sort. The header box selects every row or clears them. The toolbar shows N selected · Delete N · Clear, and Delete / Backspace in the grid does the same, except while a cell editor has focus.

The selection lives on the tab, keyed by record Id like pendingEdits, so sorting can't shift it onto other rows and switching tabs doesn't drop it. A re-run or an org switch clears it.

Deleting. Single rows and selections now share one confirmation dialog. The path depends on size:

records path progress can stop?
1–5, or any Tooling API result one sf data delete record per record per record yes, after the in-flight one
6+ one Bulk API 2.0 job via sf data delete bulk elapsed time no — once uploaded, the job runs in the org regardless

Tooling results always go per record because there's no Tooling Bulk API. The threshold is PER_RECORD_DELETE_MAX in ResultsGrid.tsx: a couple of seconds per record stops scaling quickly, while a Bulk job's fixed overhead (create, upload, 5s status polls) doesn't grow with the count.

After a delete:

  • Deleted rows leave the grid by Id, not by row index, because a Bulk job finishes long after positions were captured. deleteTabRecord(rowIdx) becomes removeTabRecords(ids), which also drops those rows' staged edits and selection ticks.
  • Failures stay selected, and the dialog lists each with its error. Its button then retries just those.
  • ENTITY_IS_DELETED counts as deleted, since the record is already in the Recycle Bin.
  • If a Bulk job fails as a whole (an object the Bulk API won't take, a missing permission), the dialog offers Delete one at a time.
  • If sf can't say which rows went (the 10-minute wait ran out, or an older CLI without per-record results), nothing is removed and the dialog says to re-run the query.

CLI bridge

Bulk DML runs to completion and then sets exit code 1 if any row failed, so its envelope is { "status": 1, "result": {…} } with no error name. run_sf_json flattened that into a bare "(no message)" error and lost the per-row outcomes.

run_sf_json_partial_ok keeps the result of a command that completed. A command that threw is still an error, and every existing caller keeps the strict behavior. The envelope check moved into a pure unwrap_envelope so both modes are unit-tested.

The upload CSV uses the platform's line ending. sf declares CRLF to the Bulk API on Windows and LF elsewhere, and a mismatch leaves a stray \r on every id.

0.4.1

This release also ships the light-mode context-menu hover fix from #6. The installed 0.4.0 predates it, which is why hovered menu items still render near-black under near-black text there.

Verification

  • tsc, cargo fmt --check, cargo clippy -D warnings, and cargo test (41 pass) are green. New tests cover the envelope handling in both modes, the upload CSV, and parsing Bulk results: per-record outcomes, a job still running, and output that isn't recognized.
  • I exercised the UI in a browser against mocked Tauri IPC, in both themes:
    • bulk partial failure
    • bulk rejected outright → one-at-a-time fallback
    • Stop mid-run
    • Tooling results routed per record
    • an already-deleted record
    • shift-range after a sort
    • Delete key vs. an open cell editor
    • selection surviving a tab switch and clearing on re-run
  • Not yet run against a real org. The Bulk result parsing follows plugin-data 3.5.14 (sf 2.150.6) source and fixtures. Before tagging, delete ~10 records in a sandbox, including one whose delete is blocked, to check both the success and failure paths end to end.

🤖 Generated with Claude Code

lr00429 and others added 2 commits September 23, 2026 09:00
Deleting was one row at a time: the trash button, a confirmation, repeat.
Cleaning up a few hundred DocuSign statuses or ApexLogs meant a few
hundred round trips through that dialog.

Selection. A checkbox column appears whenever the rows are deletable —
same rule as the trash button: the result's org is the active one and
it has a FROM-object. Shift-click ticks a range in displayed order, the
header box selects every row or clears them. The selection lives on the
tab, keyed by record Id like pendingEdits, so sorting can't shift it
onto other rows and switching tabs doesn't drop it. A re-run or an org
switch clears it: a selection is the input to a delete, and it should
only ever hold rows picked from what's on screen.

Deleting. "Delete N" in the toolbar, or Delete / Backspace anywhere in
the grid except while a cell editor has focus, opens the confirmation —
now one dialog for a single row and for a selection. Up to five records,
and every Tooling API result (there is no Tooling Bulk API), go one
`sf data delete record` call at a time with progress and a Stop button.
Anything bigger runs as a single Bulk API 2.0 job via
`sf data delete bulk`: a couple of seconds per record doesn't scale, a
Bulk job's fixed overhead (create, upload, 5s status polls) does. A Bulk
job has no Stop — once uploaded it runs in the org whether or not sf is
still waiting on it.

Outcomes. Deleted rows leave the grid by Id rather than by row index —
a Bulk job finishes long after positions were captured — so the store's
deleteTabRecord(rowIdx) becomes removeTabRecords(ids), dropping staged
edits and selection ticks along with the rows. Failures stay selected
and the dialog lists each with its error; its button retries just
those. ENTITY_IS_DELETED counts as deleted: the record is already in the
Recycle Bin, which is what was asked for. If a Bulk job fails as a whole
(an object the Bulk API won't take, a missing permission) the dialog
offers "Delete one at a time" instead. If sf can't say which rows went —
the 10-minute wait ran out, or an older CLI without per-record results —
nothing is removed and the dialog says to re-run the query.

CLI bridge. Bulk DML runs to completion and then sets exit code 1 when
any row failed, so its envelope is `{ "status": 1, "result": {...} }`
with no error name. run_sf_json flattened that into a bare "(no message)"
error, losing the per-row outcomes. run_sf_json_partial_ok keeps the
result of a command that completed; one that threw is still an error,
and every existing caller keeps the strict behavior. The upload CSV uses
the platform line ending, because sf declares CRLF to the Bulk API on
Windows and LF elsewhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lr00429
lr00429 merged commit 8b9b4cc into main Sep 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant