Bulk delete reports real settled outcomes with live progress - #5680
Merged
norman-abramovitz merged 11 commits intoJul 22, 2026
Merged
Conversation
Previously runBulkWithProgress only opened the progress snackbar after the POST returned, and only when settlement had PENDING items — a fast bulk delete gave no visible feedback until the final summary. BulkProgressOptions now accepts an optional `count` (the caller's known selection size); when provided and > 0, the progress snackbar opens before op() is awaited, with a plain "Verb N nouns…" message. The same ref threads into settlement, which upgrades it in place to the "…N of M" wording (M from the authoritative BulkResult) and dismisses it before the final summary; POST failure dismisses it before the error snackbar. The no-count / no-PENDING fallback path (lazy open during settlement) is unchanged. All four bulk-delete callers (CF Applications, Space Apps, CF Routes, Space Routes) now pass their selection count through runBulk().
norman-abramovitz
approved these changes
Jul 22, 2026
norman-abramovitz
left a comment
Contributor
There was a problem hiding this comment.
LGTM - spinner is still not working but the messaging makes you want to wait.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bulk delete (apps and routes) previously ended with "N applications delete requested" because async CF deletes come back PENDING inside the bulk envelope and the frontend never tracked them to completion. This wires up the tracking the backend already supports: each PENDING item in a BulkResult carries a stratosjobs handoff, and the frontend now polls those jobs to settlement and reports what actually happened.
What the user sees now: from the moment the delete is confirmed, a snackbar shows "Deleting N applications…", updating to "… X of N" as async items settle, ending in "N applications deleted" (auto-dismisses after ~5s) or, when something went wrong, "X deleted, Y failed" / "… Y unconfirmed" — which stays until dismissed and triggers a list refresh so wrongly-removed rows resurface. The word "requested" is gone. Route bulk delete gets the same treatment; route unmap (synchronous server-side) gets the aligned final-summary wording with no polling phase.
How
pollBulkResult()(new, in services/async-jobs) settles a BulkResult: fast-path COMPLETE/FAILED items count immediately, PENDING items are polled via the existingpollJob(exported from write-with-job) with bounded concurrency (6, matching the backend fan-out width). Poll outcomes: COMPLETE → deleted, job FAILED → failed, UNKNOWN (404/HA rule) → unconfirmed. Never rejects.runBulkWithProgress()(same module) owns the lifecycle: opens the progress snackbar at operation start, runs the POST phase (bulk-bar spinner, optimistic row removal unchanged), resolves for the caller at POST end, and continues settlement detached — the config services are root-scoped and the snackbar is global, so progress survives navigation. All snackbar/refresh errors are contained; nothing can escape as an unhandled rejection.update(message)(in-place message updates), made explicitly inert afterdismiss()to close the race with late settlement updates.runBulkto the shared helper.Latent bug fixed along the way: the frontend typed
BulkItemResult.stateas lowercase ('succeeded' | 'failed' | 'pending') but the backend sends uppercase (COMPLETE/FAILED/PENDING), so theitem.state !== 'failed'guard never matched and failed deletes were optimistically removed from the list along with successful ones. Types now match the wire (includingjobbeing a full job object, not a string) and a guard test pins the behavior.Verification: full gate green (lint, all 8 vitest projects — 640 files, production build, backend tests). Live-verified against a real CF: progress snackbar visible through the POST and polling phases, per-item job polls on the wire, final summary correct, failed-path wording covered by unit tests. Version bumped to v5.0.0-dev.139.