Files
vercel__workflow/packages/web/app/lib/bulk-cancel-ui.ts
Karthik Kalyan 371f06e5ac feat(web): bulk-cancel selected runs from the runs table (#3349)
* feat(cli): bulk-cancel runs in a single operation

Replace the per-run cancel loop in `workflow cancel` with one `cancelRuns`
call, validate `--limit` (1-500), print a compact outcome summary with
per-run lines for surfaced failures, and exit nonzero only when a run fails.
The bulk logic lives in a dependency-injected `performBulkCancel` helper so it
is unit-testable without an oclif harness.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(cli): address bulk cancel review feedback

* feat(web): bulk-cancel selected runs in a single request

Thread a bulkCancelRuns action through the server action, RPC route,
rpc-client, and client wrappers, backed by core's cancelRuns. The runs table
now cancels the selected pending/running runs in one call, caps a batch at
BULK_CANCEL_MAX_RUN_IDS (disabling the button with guidance above the cap),
and reports a single outcome-summary toast covering only the categories that
occurred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-05 14:54:33 -07:00

43 lines
1.6 KiB
TypeScript

/**
* Pure decision helpers for the bulk-cancel UI, extracted so the toast
* severity and post-cancel selection behavior can be unit-tested without
* mounting the runs table.
*/
import type { BulkCancelWorkflowRunsResult } from '@workflow/world';
type BulkCancelSummary = BulkCancelWorkflowRunsResult['summary'];
type BulkCancelResult = BulkCancelWorkflowRunsResult['results'][number];
/**
* Choose the toast severity for a completed bulk-cancel request.
*
* `already_cancelled` is an idempotent success, so it counts toward the
* success total — a batch that another caller had already cancelled is not an
* error. The error toast is reserved for the case where nothing succeeded;
* a genuine problem (failed / not-found / not-cancellable) alongside some
* success is a warning.
*/
export function bulkCancelToastSeverity(
summary: BulkCancelSummary
): 'success' | 'warning' | 'error' {
const succeeded = summary.cancelled + summary.alreadyCancelled;
const hadProblem =
summary.failed + summary.notCancellable + summary.notFound > 0;
if (succeeded === 0) return 'error';
if (hadProblem) return 'warning';
return 'success';
}
/**
* Whether a run should stay selected after a bulk-cancel batch. Retryable
* failures are kept selected so the user can retry them without reselecting;
* every terminal outcome (cancelled, already-cancelled, not-cancellable,
* not-found, and non-retryable failures) is cleared.
*/
export function shouldRetainSelectionAfterBulkCancel(
result: BulkCancelResult
): boolean {
return result.outcome === 'failed' && result.retryable;
}