ClaimAccountModal
PURPOSE
Fullscreen overlay UI that lets a guest user upgrade their account by entering an email address and receiving a Supabase Auth magic link. Submits the email through playerStore.claimWithEmail, then switches to a “check your email” confirmation state. The actual account claim completes out-of-band when the user clicks the email link and the resulting auth state change triggers re-bootstrap in playerStore.
OWNS
- Local form state:
email(string),sending(boolean),sent(boolean),error(string or null). - The two-phase modal rendering: pre-submit form vs. post-submit confirmation.
- Email validation gate (non-empty and contains
@). - Submit lifecycle: clears prior error, sets
sending, awaitsclaimWithEmail, transitions tosent, releasessendingin afinallyblock. - Component-local error message derived from caught exceptions.
READS FROM
usePlayerStoreselectors => s.claimWithEmail— the magic-link send action.ClaimAccountModalProps.onClose— caller-provided dismiss callback.- Form input
emailvalue via controlled<input>.
PUSHES TO
playerStoreviaclaimWithEmail(trimmed)— initiates the Supabase Auth OTP send.onClose()— fired by the scrim click, the post-success “GOT IT” button, and the pre-submit “CANCEL” button.
DOES NOT
- Does not complete the account claim itself; that work happens in
playerStoreafter the magic link is clicked and the auth state changes. - Does not persist email anywhere outside React state.
- Does not handle navigation, routing, or focus trapping beyond
autoFocuson the input. - Does not render its own backdrop, panel chrome, or door styling — delegates to
MedPanelModalandMedPanel variant="doors". - Does not retry on failure or rate-limit submissions beyond the
sendingdisabled flag. - Does not validate email format beyond presence of
@.
Signals
senttransitions false to true onceclaimWithEmailresolves; the rendered output switches branches.sendinggates the submit button label (SENDING...vs.SEND MAGIC LINK) and disables both input and submit while in flight.errorpopulated by either the validation guard (“Enter a valid email address”) or a caught exception (“Failed to send link” or theError.message).
Entry points
- Default export
ClaimAccountModal({ onClose })— invoked by the metagame shell when the user taps “Claim Account”. handleSubmit(e: FormEvent)— bound to the<form onSubmit>; callspreventDefault, validates, then awaits the store action.
Pattern notes
- Built on the medical-UI primitives
MedPanelModalandMedPanel(variantdoors), per the Tick 75 refactor away from V32 dark/gold inline styling. - Sibling-paired with
WelcomeModal(T22 A2, t74) — same panel chrome, same two-phase pattern. - Uses
MedPanelModal’sonScrimClick={onClose}for backdrop dismiss; no explicit ESC handling. - Inline
styleprops remain for layout sizing (width: 'min(380px, 100%)', padding, margins); colors and typography come frommed-*CSS classes and--med-*custom properties. - Two-button hierarchy in the form state: primary
SEND MAGIC LINK(med-btn-primary) and ghostCANCEL(med-btn-ghost). The success state collapses to a singleGOT ITprimary button. - Error rendering uses
med-text-criticalwith a slightly smallerfontSize: '13px'. - Type-only import of
FormEventfollows theimport { useState, type FormEvent }style.