Files
SkinsPins/app/templates/games/new_game.html
T
Rolf 3a1da1482c Add full SkinsPins application
- FastAPI + SQLite + Alpine.js + HTMX PWA for Bingo Bango Bongo golf scoring
- Passwordless magic link auth with email (aiosmtplib) and admin invite system
- Real-time score entry via WebSocket with drag-and-drop player ordering
- Course management with per-hole par and stroke index
- Scorecard with golf score markers (birdie, eagle, bogey, etc.)
- Two-step new game form with tee selection per player
- Dashboard with stats, live games social stream, and recent game history
- Game summary view (read-only scorecard with leaderboard)
- User profile pages with win stats
- PWA install support with generated PNG icons
- Admin panel for users, courses, games, and invitations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 10:16:46 +01:00

142 lines
5.7 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.
{% extends "base.html" %}
{% block title %}New Game Skins &amp; Pins{% endblock %}
{% block extra_head %}
<script>
const TEES_BY_COURSE = {{ tees_by_course | tojson }};
</script>
{% endblock %}
{% block content %}
<div class="page"
x-data="{
step: 1,
courseId: '',
selectedPlayers: ['{{ user.id }}'],
togglePlayer(id) {
const idx = this.selectedPlayers.indexOf(id);
if (idx === -1) this.selectedPlayers.push(id);
else if (id !== '{{ user.id }}') this.selectedPlayers.splice(idx, 1);
},
hasTees() { return (TEES_BY_COURSE[this.courseId] || []).length > 0; },
tees() { return TEES_BY_COURSE[this.courseId] || []; }
}">
<div class="page-header">
<h1>New Game</h1>
</div>
{% if error %}<div class="alert alert-error">{{ error }}</div>{% endif %}
<form method="post" action="/games/new">
{# ── Step 1: Format, course, holes, players ── #}
<div x-show="step === 1">
<div class="card">
<div class="form-group">
<label>Format</label>
<div style="display:flex; gap:0.75rem; flex-wrap:wrap;">
<label class="radio-card" style="flex:1;">
<input type="radio" name="format" value="bingo_bango_bongo" checked required>
<span>Bingo Bango Bongo</span>
</label>
</div>
</div>
<div class="form-group">
<label for="course_id">Course</label>
{% if courses %}
<select id="course_id" name="course_id" required
@change="courseId = $event.target.value"
style="width:100%; padding:0.65rem 0.75rem; border-radius:0.5rem; border:1px solid var(--border); background:var(--surface); color:var(--text); font-size:1rem;">
<option value="" disabled selected>Select a course…</option>
{% for c in courses %}
<option value="{{ c.id }}">{{ c.name }}{% if c.location %} · {{ c.location }}{% endif %} ({{ c.holes }} holes)</option>
{% endfor %}
</select>
{% else %}
<p style="color:var(--text-muted); font-size:0.9rem;">No courses available. Ask the admin to add courses first.</p>
<input type="hidden" name="course_id" value="">
{% endif %}
</div>
<div class="form-group">
<label>Holes</label>
<div style="display:flex; gap:0.75rem;">
<label class="radio-card">
<input type="radio" name="holes_count" value="9" required>
<span>9 holes</span>
</label>
<label class="radio-card">
<input type="radio" name="holes_count" value="18">
<span>18 holes</span>
</label>
</div>
</div>
<div class="form-group">
<label>Players</label>
<div class="player-list">
{% for u in all_users %}
<label class="player-option">
<input type="checkbox" name="players" value="{{ u.id }}"
{% if u.id == user.id %}checked disabled{% endif %}
@change="togglePlayer('{{ u.id }}')"
:checked="selectedPlayers.includes('{{ u.id }}')">
<span class="player-avatar">
{% if u.avatar and u.avatar.startswith('/') %}
<img src="{{ u.avatar }}" style="width:1.3rem;height:1.3rem;border-radius:50%;object-fit:cover;vertical-align:middle;">
{% else %}
{{ u.avatar or '👤' }}
{% endif %}
</span>
<span class="player-name">{{ u.name }}</span>
{% if u.id == user.id %}<span style="color:var(--text-muted); font-size:0.75rem;">you</span>{% endif %}
</label>
{% endfor %}
</div>
<input type="hidden" name="players" value="{{ user.id }}">
</div>
</div>
<button type="button" x-show="hasTees()" class="btn btn-primary" @click="step = 2">Next →</button>
<button type="submit" x-show="!hasTees()" class="btn btn-primary">Start game ⛳</button>
</div>
{# ── Step 2: Tee selection ── #}
<div x-show="step === 2" style="display:none;">
<div class="card">
<h2 style="font-size:0.95rem; color:var(--text-muted); margin-bottom:1rem;">Select tees</h2>
{% for u in all_users %}
<div class="form-group" x-show="selectedPlayers.includes('{{ u.id }}')">
<label>
{% if u.avatar and u.avatar.startswith('/') %}
<img src="{{ u.avatar }}" style="width:1.1rem;height:1.1rem;border-radius:50%;object-fit:cover;vertical-align:middle;margin-right:0.3rem;">
{% else %}
{{ u.avatar or '👤' }}
{% endif %}
{{ u.name }}
</label>
<select name="tee_{{ u.id }}"
style="width:100%; padding:0.65rem 0.75rem; border-radius:0.5rem; border:1px solid var(--border); background:var(--surface); color:var(--text); font-size:1rem;">
<option value="" disabled selected>Select tee…</option>
<template x-for="t in tees()" :key="t.id">
<option :value="t.id" x-text="t.name + ' (Slope ' + t.slope + ' · Rating ' + t.rating + ')'"></option>
</template>
</select>
</div>
{% endfor %}
</div>
<div style="display:flex; gap:0.75rem;">
<button type="button" @click="step = 1"
style="flex:1; padding:0.85rem; border-radius:0.65rem; border:1px solid var(--border); background:var(--surface2); color:var(--text); font-size:1rem; cursor:pointer;">← Back</button>
<button type="submit" class="btn btn-primary" style="flex:2;">Start game ⛳</button>
</div>
</div>
</form>
</div>
{% endblock %}