Add-ons screen
/p/{token}/addons is a read-only status overview of a guest’s
requested add-ons. Each card reflects the live state of the
pre_checkin_submissions row.
States
For early check-in:
payment_captured_at | payment_status | Card |
|---|---|---|
| set | — | ✓ Confirmed, charged $X |
| null | canceled | Unable to accommodate, hold released |
| null | anything else | Pending Bill’s decision |
For late check-out:
payment_captured_at + late_checkout_chosen_time | late_checkout_decision | Card |
|---|---|---|
| both set | — | ✓ Confirmed, departure {time} |
| — | decline (or payment_status=canceled) | Unable to accommodate |
| neither | — | “Pick your time” → links to /forms/late-checkout?b=N |
The late-checkout tier picker UI itself still lives at the legacy
/forms/late-checkout?b={id} route. Folding it into the portal is a
fast-follow once we see how guests use the status page.
No-requests fallback
If the booking has no pre_checkin_submissions row at all, OR the row
has addon_early_checkin=0 AND addon_late_checkout=0, the page
shows a friendly “you haven’t requested any add-ons yet” with a button
to the pre-check-in form where requests are made.
How the picker URL gets here
The M4d workflow phase (runLateCheckoutAvailabilityCheck at D-3
15:00Z) emails the guest with the tier-picker link. As of P12.M6 that
link is /p/{token}/addons (was previously /forms/late-checkout?b=N).
Landing on the portal page shows the picker card, which links onward
to the actual picker form for the time selection.
Source
- Route:
src/routes/portal.tshandleAddonsScreen+renderEarlyCheckinCard/renderLateCheckoutCard - Tests:
test/routes/portal.test.ts— 5 M5 cases covering no-requests, early-confirmed, early-declined, late-awaiting-picker, late-confirmed