[C04] Reassignment Dialog
Summary
Cross-cutting, reusable dialog used wherever an event participant holds a race number and the operator needs to swap it. First caller: E02 Event Participants. Future callers: E07 Pre-assignment.
The dialog provides the operator’s explicit signal for what should happen to the old number X. Without it, X can sit in ISSUED indefinitely after a physical replacement — ghost inventory.
Actor & Context
Actor: event organiser, tenant admin, or stock operator.
Frequency: ad-hoc; surfaces whenever a number swap is needed (damaged at sign-in, lost in transit, operator override).
Precondition: an EP exists with an assigned number; user has permission to reassign.
Entry point: Reassign number action on E02 row; Assign inline action on E01 finalisation readiness panel; future per-EP screens.
Main Flow
-
Dialog opens with EP context (event, name, current number).
-
New-number picker — pre-populated from the WS1b last-used pick list for
person_id(same subtype first; pool fall-through). -
Swap-reason picker (required):
Damaged,Lost,Other+ optional free-text note. -
Temporary checkbox —
Temporary — do not cascade to future events(default unchecked). -
Cascade preview — "This change will also detach N future EP(s)."
-
Submit:
-
Damaged→POST /api/event-participants/{id}/reassign-numberthen chainPOST /api/race-numbers/flag-unfitfor old X. -
Lost→ reassignment then chainPOST /api/race-numbers/mark-lostfor old X. -
Other→ reassignment only.
-
-
Success banner reports cascade summary: originating EP swapped, N future EPs detached, temporary y/n.
Alternative Flows
-
AF-1: Reassignment succeeds, chained X-side action fails — surface error; admin told X needs manual attention. Primary swap not rolled back.
-
AF-2: Cascade preview shows zero future EPs — proceed without warning.
-
AF-3: Permission denied — dialog closes with a clear error.
Acceptance Criteria
-
Dialog renders per Claude Design pass.
-
Component is reusable (no E02-specific imports).
-
All three swap-reason chains behave per Session 9 of the journal.
-
Cascade summary surfaced after submit.
-
Failure-handling path tested.
API Surface
| Call | Purpose |
|---|---|
|
Last-used pick list for the new-number picker (WS1b). |
|
Primary reassignment with |
|
Chained for |
|
Chained for |
Out of Scope
-
Bulk reassignment across many EPs — single-EP only for v1.
-
Auto-disposal chain from
Damaged— admin runs dispose as a separate step. -
SMS notification when X is flagged — separate Feature.
Design Anchors
-
design-journal/2026-03/number-tag-management.adoc— Session 9 (DETACH-and-reconcile) -
E02 Event Participants — primary caller
Design Decisions
-
Per-reason consequence-on-X — inline, live-updating (2026-04-29). Below the swap-reason picker, a single line updates live as the operator changes the radio:
-
Damaged→ "Bib X will be flagged Unfit For Service and removed from the available pool." -
Lost→ "Bib X will be marked Lost." -
Other→ "Bib X returns to the available pool."Rationale: explicit signal for what happens to X is the entire reason the dialog exists (avoids ghost inventory in
ISSUED); hiding it behind a confirm step demotes the most important decision in the dialog. Implies the swap-reason picker is radios, not a dropdown, so the consequence line is always visible alongside the choice.
-
-
Single-EP scope (2026-04-29). Confirmed: dialog handles one EP / one number swap at a time. Bulk reassignment remains out of scope per existing spec.
-
"About this dialog" information panel (2026-04-29). Persistent collapsible panel near the top of the dialog with a short purpose paragraph + bulleted operational details (the same plain-language explanations of the three reason consequences). Default expanded on first open per browser, collapsed thereafter (state persisted in
localStorage). Trains operators in-flow without forcing them to read external docs. Cross-cutting pattern — applied symmetrically in C05 and E06; candidate for promotion to UI Design Principles.
Notes
Reuses backend behaviour from US #505 (shipped). UI candidate for @ems/shared-ui migration once a second consumer surfaces.
|
Active design iteration in progress. See admin-portal Screen Design Prompt Iteration for the broader handoff workflow. Next step: Claude Design pass on the v3 prompt persisted in the appendix below. |
Appendix A: Claude Design Prompts
|
Prompts persisted for audit trail. Most recent first. The v3 prompt is the active hand-off prompt; earlier versions are retained for lessons-learned reference. |
v3 — 2026-04-29 — derived from this .adoc
Status: drafted; ready for hand-off. Source: this .adoc at :status: in-design.
I'm designing a modal dialog for the EMS admin portal — same Spring Boot
+ Angular stack, same visual language as the screens you've designed
in this project (C01, C03, E01, E05). Match modal-width, header
treatment, button placement, and footer toolbar to C01.
Design **C04 — the Reassignment Dialog**: a cross-cutting reusable
modal where an event operator swaps one race number for another on an
event participant (EP). Single-EP, single-swap. First caller is E02
(event participants list); future callers include E07 (pre-assignment)
and an event-finalisation readiness panel — the dialog must have NO
caller-specific imports.
The dialog's central job: capture the operator's explicit signal for
what should happen to the OLD number X. Without this signal, X can sit
in `ISSUED` indefinitely after a physical replacement, creating ghost
inventory.
============================================================
Dialog structure (top to bottom)
============================================================
1. Header
- Title: "Reassign number".
- Sub-title: EP context — "<event> · <participant name> · currently
assigned bib <X>".
- Close (X) button top-right.
2. "About this dialog" information panel
- Collapsible, default expanded on first open per browser, collapsed
thereafter (state persisted in localStorage).
- Short purpose paragraph: "Swap a participant's race number and
decide what happens to the old number."
- Bulleted operational details — the same plain-language explanations
of the three reason consequences below, restated in plain prose.
3. New-number picker
- Pre-populated from GET /api/persons/{personId}/number-pickList
(returns the WS1b last-used pick list — same subtype first; pool
fall-through).
- Filterable dropdown (same component as C05 cell-mapping; filter
auto-appears above ~10 options).
4. Swap-reason picker — RADIOS (not a dropdown)
Three options:
- `Damaged`
- `Lost`
- `Other`
Plus an optional free-text "Operator note" field below.
5. Per-reason consequence-on-X line — INLINE, live-updating
Single line directly beneath the radios that updates as the operator
changes selection:
- `Damaged` → "Bib <X> will be flagged Unfit For Service and removed
from the available pool."
- `Lost` → "Bib <X> will be marked Lost."
- `Other` → "Bib <X> returns to the available pool."
6. Temporary checkbox
- Label: "Temporary — do not cascade to future events"
- Default: unchecked.
7. Cascade preview
- One-line message: "This change will also detach N future EP(s)."
- Hidden if N is zero.
8. Footer toolbar
- "Cancel" (left) — closes dialog, no API calls.
- "Confirm" (right, primary) — fires the API chain below.
============================================================
Submit behaviour
============================================================
On Confirm:
- Always: POST /api/event-participants/{id}/reassign-number
(request includes new-number-id, swapReason, temporary flag,
optional note).
- Then chain on the OLD bib X based on swapReason:
- `Damaged` → POST /api/race-numbers/flag-unfit
- `Lost` → POST /api/race-numbers/mark-lost
- `Other` → no chained call.
Success — close the dialog and show a success banner on the parent
screen with the cascade summary: originating EP swapped + N future EPs
detached + temporary y/n.
Failure modes:
- AF-1: Reassignment succeeds, chained X-side action fails → success
banner for the swap PLUS a warning toast that X needs manual attention.
Primary swap NOT rolled back.
- AF-3: Permission denied → close dialog, show error toast on parent.
============================================================
Output
============================================================
- HTML/JSX + matching styles for the dialog.
- README explaining:
- The three reason chains and why the consequence line is inline
(it's the operator's only signal for what happens to X).
- The reusability contract — no E02-specific imports; the dialog
receives EP context as a prop and emits a result event.
- The "About this dialog" information-panel pattern — and that the
same pattern should appear on full-screen flows like C05/E06 (right
rail rather than top-of-modal).
v1 — 2026-04-29 — discarded
Status: discarded. Reason: drafted from session memory without reading this .adoc. Invented four swap reasons (Lost / Damaged / Mis-issued / Operator override) instead of the canonical three (Damaged / Lost / Other); missed the Temporary checkbox for cascade control; missed the Cascade preview line; didn’t reference the WS1b last-used pick list for the new-number picker.
Continuing the EMS admin portal design language. C04 is a dialog (modal)
used cross-cuttingly anywhere in the portal where an operator needs to
swap one entity for another — most concretely, race-number reassignment
during the event-day operations workflow.
Context: an event participant has been assigned race-number 142.
Mid-event the operator needs to swap them to race-number 187 (lost bib,
duplicate issue, deliberate move). The dialog must:
- Show: current assignment ("Bib 142 → Jane Doe, F35-39, Started 06:42").
- Ask for the swap target: "Assign Jane Doe to bib ___" with autocomplete
on available bibs (filtered by the participant's category — bib 187 must
be in the F35-39 category's number range).
- Ask for a reason: dropdown of {Lost, Damaged/Unfit, Mis-issued, Operator
override}. The reason picker chains the right downstream consequences:
- Lost / Damaged → mark the OLD bib as UNFIT_FOR_SERVICE (don't
auto-reissue; operator does stock-take later).
- Mis-issued / Operator override → mark the OLD bib IN_STOCK (back
to the available pool).
- Show consequences inline as the operator picks a reason: "Bib 142
will be marked UNFIT_FOR_SERVICE and removed from the available pool"
vs "Bib 142 will return to the available pool". This is critical —
operators get this wrong on paper currently.
- Free-text "operator note" (optional, for audit trail).
- Confirm + Cancel buttons. Confirm fires the swap and closes the dialog.
An undo toast at the bottom of the parent page gives 10 seconds to
reverse if it was a mistake.
Edge case to design for: target bib is already assigned to someone else.
The dialog should detect this and show a conflict warning with the option
to swap them too (chained reassignment) or pick a different bib.
Style: matches the portal modal patterns established by C01 (user/tenant
switcher) — same modal width, header treatment, button placement, footer
toolbar.
Output: HTML/JSX + styles, README explaining how this dialog is reused
across at least 3 contexts (race-number reassignment, operational batch
re-print, person merge confirmation). README also describes the
"undo toast" pattern so it can be standardised across the portal.