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:
@@ -58,6 +58,15 @@
|
||||
const id = this.selectedTees[uid];
|
||||
return id ? (TEES_BY_COURSE[this.courseId] || []).find(t => t.id === id) : null;
|
||||
},
|
||||
getCourseHandicap(uid) {
|
||||
const player = this.getPlayer(uid);
|
||||
const tee = this.getTee(uid);
|
||||
const course = COURSES_DATA.find(c => c.id === this.courseId);
|
||||
if (!course || !tee || player.handicap == null) return null;
|
||||
const coursePar = (parseInt(this.holesCount) === 9 && course.holes === 18) ? course.par_9 : course.par;
|
||||
if (!coursePar) return null;
|
||||
return Math.round(player.handicap * (tee.slope / 113) + (tee.rating - coursePar));
|
||||
},
|
||||
goToConfirm() { this.step = 3; },
|
||||
backFromConfirm() { this.step = this.hasTees() ? 2 : 1; },
|
||||
formatLabel() {
|
||||
@@ -195,8 +204,10 @@
|
||||
|
||||
</div>
|
||||
|
||||
<button type="button" x-show="hasTees()" class="btn btn-primary" @click="step = 2">Next →</button>
|
||||
<button type="button" x-show="!hasTees()" class="btn btn-primary" @click="goToConfirm()">Next →</button>
|
||||
<div style="position:sticky; bottom:calc(var(--nav-height) + env(safe-area-inset-bottom)); background:var(--bg); padding:0.75rem 0; margin-top:0.5rem;">
|
||||
<button type="button" x-show="hasTees()" class="btn btn-primary" @click="step = 2">Next →</button>
|
||||
<button type="button" x-show="!hasTees()" class="btn btn-primary" @click="goToConfirm()">Next →</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ── Step 2: Tee selection ── #}
|
||||
@@ -274,10 +285,13 @@
|
||||
<div style="flex:1; min-width:0;">
|
||||
<div style="font-weight:500;" x-text="getPlayer(uid).name"></div>
|
||||
<div style="font-size:0.8rem; color:var(--text-muted);">
|
||||
<span x-text="'HCP ' + (getPlayer(uid).handicap != null ? getPlayer(uid).handicap : '—')"></span>
|
||||
<span x-text="'HI\u00a0' + (getPlayer(uid).handicap != null ? getPlayer(uid).handicap : '—')"></span>
|
||||
<template x-if="getTee(uid)">
|
||||
<span x-text="' · ' + getTee(uid).name + ' (Slope\u00a0' + getTee(uid).slope + ' · Rating\u00a0' + getTee(uid).rating + ')'"></span>
|
||||
</template>
|
||||
<template x-if="getCourseHandicap(uid) != null">
|
||||
<span style="color:var(--accent); font-weight:600;" x-text="' · HC\u00a0' + getCourseHandicap(uid)"></span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user