Files
SkinsPins/app/templates/auth/check_email.html
T
Rolf 8601584099 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>
2026-03-23 20:37:08 +01:00

119 lines
4.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="theme-color" content="#2d6a4f">
<link rel="manifest" href="/static/manifest.json">
<link rel="stylesheet" href="/static/style.css">
<title>Check your email Skins &amp; Pins</title>
</head>
<body>
<div class="login-container">
<div class="login-logo">📬</div>
<h1 class="login-title">Check your email</h1>
<p class="login-subtitle">We sent a code and a login link to <strong>{{ email }}</strong></p>
<div class="login-card card">
{% if error %}
<p style="color:var(--danger); font-size:0.9rem; text-align:center; margin-bottom:1rem;">{{ error }}</p>
{% endif %}
<form method="post" action="/auth/verify-code" autocomplete="off"
style="display:flex; flex-direction:column; gap:1rem;">
<input type="hidden" name="email" value="{{ email }}">
<label style="font-size:0.85rem; color:var(--text-muted); text-align:center;">
Enter your 6-digit code
</label>
<div style="display:flex; gap:0.5rem; justify-content:center;" id="code-inputs">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]"
class="code-digit" tabindex="1" autocomplete="one-time-code">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]"
class="code-digit" tabindex="2">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]"
class="code-digit" tabindex="3">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]"
class="code-digit" tabindex="4">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]"
class="code-digit" tabindex="5">
<input type="text" inputmode="numeric" maxlength="1" pattern="[0-9]"
class="code-digit" tabindex="6">
</div>
<input type="hidden" name="code" id="code-value">
<button type="submit" class="btn btn-primary" id="code-submit" disabled>Verify code</button>
</form>
<p style="color:var(--text-muted); font-size:0.85rem; text-align:center; margin-top:1rem;">
The code and link expire in 15 minutes.
</p>
</div>
<a href="/login" style="color:var(--text-muted); font-size:0.85rem; margin-top:1.5rem;">
← Use a different email
</a>
</div>
<style>
.code-digit {
width: 3rem;
height: 3.5rem;
text-align: center;
font-size: 1.6rem;
font-weight: 700;
border: 2px solid var(--surface2);
border-radius: 0.5rem;
background: var(--surface);
color: var(--text);
caret-color: transparent;
}
.code-digit:focus {
outline: none;
border-color: var(--accent);
}
</style>
<script>
const digits = [...document.querySelectorAll('.code-digit')];
const hidden = document.getElementById('code-value');
const submit = document.getElementById('code-submit');
function sync() {
const val = digits.map(d => d.value).join('');
hidden.value = val;
submit.disabled = val.length < 6;
}
digits.forEach((el, i) => {
el.addEventListener('input', () => {
el.value = el.value.replace(/\D/g, '').slice(-1);
sync();
if (el.value && i < digits.length - 1) digits[i + 1].focus();
});
el.addEventListener('keydown', (e) => {
if (e.key === 'Backspace' && !el.value && i > 0) {
digits[i - 1].focus();
digits[i - 1].value = '';
sync();
}
});
el.addEventListener('paste', (e) => {
e.preventDefault();
const pasted = (e.clipboardData || window.clipboardData).getData('text').replace(/\D/g, '');
digits.forEach((d, j) => d.value = pasted[j] || '');
sync();
const next = Math.min(pasted.length, digits.length - 1);
digits[next].focus();
});
});
digits[0].focus();
</script>
</body>
</html>