[T04] Bulk Flag UNFIT / Dispose

Summary

Bulk admin actions on RaceNumbers: flag a selection as UNFIT_FOR_SERVICE, or dispose UNFIT rows to DESTROYED. Honours ADR-0003 — UNFIT is immutable; DESTROYED is the only valid next state.

Actor & Context

Actor: tenant admin. Frequency: ad-hoc; surfaces during stock takes and post-event reconciliation. Precondition: user has TENANT_ADMIN permission; selection from T02. Entry point: bulk-action menu on T02; chained from T03 disposal-recommended list.

Main Flow

  1. Operator selects rows in T02 (or arrives via T03’s disposal-recommended list).

  2. Flag UNFIT action — confirmation modal showing selected count + ADR-0003 immutability warning.

  3. Dispose action (UNFIT rows only) — confirmation modal showing count.

  4. Operator confirms — bulk POST.

  5. Per-id outcome summary (success / skipped / rejected / not-found).

  6. Audit-log entry per number (handled by the existing service layer).

Alternative Flows

  • AF-1: Mixed selection (UNFIT + non-UNFIT) for Dispose — client rejects; operator must filter.

  • AF-2: Already-DESTROYED in selection — skipped; counted in not-found or skipped.

  • AF-3: Permission denied — modal closes with clear error.

Acceptance Criteria

  • Use-case page authored.

  • Status design-todo → handoff-ready after Claude Design pass.

  • Implementation calls existing flag-unfit + dispose endpoints; one bulk request when feasible.

  • Disposing a non-UNFIT number rejected client-side with a clear message.

  • Per-id outcome summary rendered from response DTO.

API Surface

Call Purpose

POST /api/race-numbers/flag-unfit

Batch UNFIT flag (existing).

POST /api/race-numbers/dispose

Batch UNFIT → DESTROYED (existing).

Out of Scope

  • Reassignment swap-reason chaining (DAMAGED → flag-unfit) — C04.

  • Stock-take return flag-unfit chaining — T03.

  • Single-number flag-unfit (no bulk) — implicit via a 1-item selection here.

Design Anchors

Notes

Backend already exposes both endpoints. UI is a thin confirmation + summary on top.