Maps applied_promo_code from the stats endpoint onto the roster rows as a
small outline badge and includes it in the fuzzy search keys.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ByAwHU4pRnyE58YocQvAas
usePromoCode drives an Apply flow against the validate endpoint (states
idle / checking / applied / invalid / unverified; quantity changes
re-validate with a debounce; stale responses are ignored; 404 from an
older extension keeps the code and sends it unverified). The dialog shows
the struck-through original and the discounted total in the header, price
block, conversion preview and invoice amount; the code rides on both the
Lightning and card requests. A 100 %-off code on the card rail lands on the
success screen via finalizePurchasedTickets instead of the "no checkout URL"
error, and a guest whose total becomes zero gets an inline log-in prompt
(free claims stay login-gated, events#29) rather than a swapped form.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ByAwHU4pRnyE58YocQvAas
Collapsible "Promo codes" section: rows of code (upper-cased as typed),
discount %, max uses (blank = unlimited), active switch, remove; per-row
errors from validatePromoRows gate submit alongside the vee-validate form.
extra.promo_codes is always sent explicitly (empty editor = clear); edit
mode prefills from the authenticated record.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ByAwHU4pRnyE58YocQvAas
The authenticated fetch returns the full Event (incl. extra.promo_codes,
which public responses omit as of events ext v1.6.1-aio.12) and is what
the edit dialog must prefill from. Public records still win for events the
user does not own.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ByAwHU4pRnyE58YocQvAas
New events.create.promo.* (first key under a create namespace; the rest
of the create dialog remains hardcoded English) and events.purchase.promo.*
in en/es/fr + the types interface.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ByAwHU4pRnyE58YocQvAas
Types follow upstream v2 names (PromoCode.max_uses / used_count,
PromoValidateRequest, BasketDiscount, BasketTotals) as served by the
events extension >= v1.6.1-aio.12; EventExtra.promo_codes becomes
optional because public event responses no longer carry it.
TicketApiService.validatePromoCode posts the upper-cased code + quantity
to POST /events/api/v1/promo/validate/{event_id}; request() now attaches
the HTTP status to thrown errors so a 404 from an older extension can be
told apart from a 400. lib/promoCodes.ts holds the pure helpers shared by
the organizer editor and the buyer dialog (normalise, editor validation
returning i18n keys, wire <-> row mapping, display-only percent rounding).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ByAwHU4pRnyE58YocQvAas