Add playing handicap, 6-digit login code, and game UX improvements
- Playing handicap: computed at game creation (WHS formula), stored on game_players alongside per-hole strokes allocation; displayed on game summary with HC card and / markers on scorecard (only when SI defined); live game shows +N per player card when SI exists, HC total otherwise; new game confirmation previews computed HC per player - Login: 6-digit code sent alongside magic link in every auth email; check_email page has auto-advancing digit inputs with paste support; new POST /auth/verify-code route handles code verification - Game view: Score/Card toggle fixed with position:fixed to always show above nav bar on mobile; swipe on score view changes holes, swipe on scorecard switches tabs; dirty tracking prevents saving unmodified holes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+14
-6
@@ -43,14 +43,22 @@ async def _send(to: str, subject: str, body_text: str, body_html: str) -> None:
|
||||
)
|
||||
|
||||
|
||||
async def send_magic_link(to: str, link: str) -> None:
|
||||
subject = f"Your {SITE_NAME} login link"
|
||||
body_text = f"Click the link below to log in (expires in 15 minutes):\n\n{link}\n\nIf you didn't request this, you can ignore this email."
|
||||
async def send_magic_link(to: str, link: str, code: str) -> None:
|
||||
subject = f"Your {SITE_NAME} login code: {code}"
|
||||
body_text = (
|
||||
f"Your login code is: {code}\n\n"
|
||||
f"Enter it on the app, or click the link below (both expire in 15 minutes):\n\n{link}\n\n"
|
||||
f"If you didn't request this, you can ignore this email."
|
||||
)
|
||||
body_html = f"""
|
||||
<div style="font-family:sans-serif;max-width:480px;margin:auto;">
|
||||
<h2 style="color:#2d6a4f;">⛳ {SITE_NAME}</h2>
|
||||
<p>Click the button below to log in. This link expires in 15 minutes.</p>
|
||||
<p style="margin:2rem 0;">
|
||||
<p>Enter this code in the app to log in:</p>
|
||||
<div style="font-size:2.5rem;font-weight:700;letter-spacing:0.4rem;text-align:center;
|
||||
background:#f0f7f4;border-radius:0.5rem;padding:1rem 1.5rem;margin:1.5rem 0;
|
||||
color:#1b4332;font-family:monospace;">{code}</div>
|
||||
<p style="color:#555;font-size:0.9rem;">Or click the button below — whichever is easier. Both expire in 15 minutes.</p>
|
||||
<p style="margin:1.5rem 0;">
|
||||
<a href="{link}"
|
||||
style="background:#2d6a4f;color:#fff;padding:0.75rem 1.5rem;border-radius:0.5rem;text-decoration:none;font-weight:600;">
|
||||
Log in to {SITE_NAME}
|
||||
@@ -67,7 +75,7 @@ async def send_magic_link(to: str, link: str) -> None:
|
||||
except Exception as e:
|
||||
logger.error("Failed to send magic link email to %s: %s", to, e)
|
||||
else:
|
||||
logger.info("Magic link for %s: %s", to, link)
|
||||
logger.info("Magic link for %s — code: %s link: %s", to, code, link)
|
||||
|
||||
|
||||
async def send_invitation(to: str, invited_by_name: str, login_url: str) -> None:
|
||||
|
||||
Reference in New Issue
Block a user