Files
SkinsPins/app/templates/games/summary.html
T
Rolf c51c872c33 Add Skins format, GolfCourseAPI import, and UX improvements
- Add Skins game format with optional carryover, mirroring BBB scoring pattern
- Add GolfCourseAPI integration for course search and import (httpx)
- Unwrap {"course": {}} API response envelope on import
- Add searchable course combobox and player filter to new game form
- Redirect to summary page after finishing a game
- Fix auth to allow registered users without a pending invitation
- Fix hardcoded BBB format in create_game; store carryover flag in DB
- Add game summary/scorecard view for completed games
- Add live games social stream to dashboard
- Add course name + subtitle to recent game cards
- Reorder nav: Home → Games → Admin → Profile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 09:18:04 +01:00

240 lines
9.4 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 %}{{ game.course_name or 'Game' }} Skins &amp; Pins{% endblock %}
{% block content %}
<div class="page" x-data="{ view: 'strokes' }">
<div class="page-header" style="display:flex; align-items:center; justify-content:space-between;">
<a href="javascript:history.back()" style="color:var(--text-muted); font-size:0.9rem;">← Back</a>
<div style="text-align:center;">
<div style="font-weight:600;">{{ game.course_name or 'Bingo Bango Bongo' }}</div>
<div style="font-size:0.8rem; color:var(--text-muted);">{{ game.holes_count }} holes · {{ game.created_at[:10] }}</div>
</div>
<div style="width:3rem;"></div>
</div>
{# Leaderboard #}
<div class="card" style="padding:0.75rem 1rem;">
{% for p in results %}
{% if game.format == 'skins' %}
{% set score_val = skins_won.get(p.id, 0) %}
{% set score_label = score_val | string + ' skin' + ('s' if score_val != 1 else '') %}
{% else %}
{% set t = totals.get(p.id, {}) %}
{% set score_val = t.total %}
{% set score_label = t.total | round(1) | string + ' pts' %}
{% endif %}
<div class="result-row {% if loop.first %}result-winner{% endif %}" style="padding:0.4rem 0;">
<span class="result-rank">{% if loop.first %}🏆{% else %}{{ loop.index }}{% endif %}</span>
<span class="result-avatar">
{% if p.avatar and p.avatar.startswith('/') %}
<img src="{{ p.avatar }}" style="width:1.4rem;height:1.4rem;border-radius:50%;object-fit:cover;vertical-align:middle;">
{% else %}
{{ p.avatar or '👤' }}
{% endif %}
</span>
<a href="/users/{{ p.id }}" style="flex:1; color:inherit; text-decoration:none;">{{ p.name }}</a>
<span class="result-pts">{{ score_label }}</span>
{% if game.format == 'bingo_bango_bongo' %}
<span style="font-size:0.75rem; color:var(--text-muted); margin-left:0.5rem;">
B{{ t.bingo | int }}/{{ t.bango | round(1) }}/{{ t.bongo | int }}
</span>
{% endif %}
</div>
{% endfor %}
{% if game.format == 'skins' and unclaimed %}
<div style="font-size:0.8rem; color:var(--text-muted); margin-top:0.5rem; text-align:center;">
{{ unclaimed }} skin{{ 's' if unclaimed != 1 else '' }} unclaimed
</div>
{% endif %}
</div>
{# Scorecard toggle #}
<div class="scorecard-toggle" style="margin-top:1rem;">
<button @click="view = 'strokes'" :class="{ active: view === 'strokes' }">Strokes</button>
{% if game.format == 'skins' %}
<button @click="view = 'skins'" :class="{ active: view === 'skins' }">Skins</button>
{% else %}
<button @click="view = 'points'" :class="{ active: view === 'points' }">Points</button>
{% endif %}
</div>
{# Strokes scorecard #}
<table class="scorecard" x-show="view === 'strokes'">
<thead>
<tr>
<th>Hole</th>
{% if hole_pars %}<th style="color:var(--text-muted);">Par</th>{% endif %}
{% for p in players %}
<th>
{% if p.avatar and p.avatar.startswith('/') %}
<img src="{{ p.avatar }}" style="width:1.3rem;height:1.3rem;border-radius:50%;object-fit:cover;vertical-align:middle;">
{% else %}
{{ p.avatar or '👤' }}
{% endif %}<br>
<a href="/users/{{ p.id }}" style="font-size:0.7rem; color:inherit; text-decoration:none;">{{ p.name.split()[0] }}</a>
</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for h in holes %}
<tr>
<td class="hole-num">{{ h }}</td>
{% if hole_pars %}<td class="score-cell" style="color:var(--text-muted);">{{ hole_pars.get(h, '—') }}</td>{% endif %}
{% for p in players %}
{% set s = scores.get(h, {}).get(p.id, {}) %}
{% set strokes = s.strokes if s else 0 %}
{% set par = hole_pars.get(h, 0) %}
<td class="score-cell">
{% if strokes %}
{% if par %}
{% set diff = strokes - par %}
{% if strokes == 1 %}
<span class="score-hio">{{ strokes }}</span>
{% elif diff <= -2 %}
<span class="score-eagle">{{ strokes }}</span>
{% elif diff == -1 %}
<span class="score-birdie">{{ strokes }}</span>
{% elif diff == 1 %}
<span class="score-bogey">{{ strokes }}</span>
{% elif diff >= 2 %}
<span class="score-double-bogey">{{ strokes }}</span>
{% else %}
{{ strokes }}
{% endif %}
{% else %}
{{ strokes }}
{% endif %}
{% else %}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr class="totals-row">
<td>Total</td>
{% if hole_pars %}<td style="color:var(--text-muted);">{{ hole_pars.values() | sum }}</td>{% endif %}
{% for p in players %}
{% set stroke_total = namespace(val=0) %}
{% for h in holes %}
{% set s = scores.get(h, {}).get(p.id, {}) %}
{% if s and s.strokes %}{% set stroke_total.val = stroke_total.val + s.strokes %}{% endif %}
{% endfor %}
<td>{{ stroke_total.val or '—' }}</td>
{% endfor %}
</tr>
</tfoot>
</table>
{# Points scorecard #}
<table class="scorecard" x-show="view === 'points'">
<thead>
<tr>
<th>Hole</th>
{% for p in players %}
<th>
{% if p.avatar and p.avatar.startswith('/') %}
<img src="{{ p.avatar }}" style="width:1.3rem;height:1.3rem;border-radius:50%;object-fit:cover;vertical-align:middle;">
{% else %}
{{ p.avatar or '👤' }}
{% endif %}<br>
<a href="/users/{{ p.id }}" style="font-size:0.7rem; color:inherit; text-decoration:none;">{{ p.name.split()[0] }}</a>
</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for h in holes %}
<tr>
<td class="hole-num">{{ h }}</td>
{% for p in players %}
{% set s = scores.get(h, {}).get(p.id, {}) %}
{% set pts = (s.bingo + s.bango + s.bongo) if s else 0 %}
<td class="score-cell">{{ pts | round(1) if pts else '—' }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr class="totals-row">
<td>Total</td>
{% for p in players %}
<td>{{ totals.get(p.id, {}).get('total', 0) | round(1) }}</td>
{% endfor %}
</tr>
</tfoot>
</table>
{# Skins scorecard #}
{% if game.format == 'skins' %}
<table class="scorecard" x-show="view === 'skins'">
<thead>
<tr>
<th>Hole</th>
{% if hole_pars %}<th style="color:var(--text-muted);">Par</th>{% endif %}
{% for p in players %}
<th>
{% if p.avatar and p.avatar.startswith('/') %}
<img src="{{ p.avatar }}" style="width:1.3rem;height:1.3rem;border-radius:50%;object-fit:cover;vertical-align:middle;">
{% else %}
{{ p.avatar or '👤' }}
{% endif %}<br>
<a href="/users/{{ p.id }}" style="font-size:0.7rem; color:inherit; text-decoration:none;">{{ p.name.split()[0] }}</a>
</th>
{% endfor %}
<th style="color:var(--text-muted);">Skin</th>
</tr>
</thead>
<tbody>
{% for h in holes %}
{% set hr = skins_hole_results.get(h) %}
<tr>
<td class="hole-num">{{ h }}</td>
{% if hole_pars %}<td class="score-cell" style="color:var(--text-muted);">{{ hole_pars.get(h, '—') }}</td>{% endif %}
{% for p in players %}
{% set s = scores.get(h, {}).get(p.id, {}) %}
{% set strokes = s.strokes if s else 0 %}
{% set par = hole_pars.get(h, 0) %}
<td class="score-cell">
{% if strokes %}
{% set is_winner = hr and hr.winner == p.id %}
{% if par %}
{% set diff = strokes - par %}
{% if strokes == 1 %}<span class="score-hio{% if is_winner %} skin-winner{% endif %}">{{ strokes }}</span>
{% elif diff <= -2 %}<span class="score-eagle{% if is_winner %} skin-winner{% endif %}">{{ strokes }}</span>
{% elif diff == -1 %}<span class="score-birdie{% if is_winner %} skin-winner{% endif %}">{{ strokes }}</span>
{% elif diff == 1 %}<span class="score-bogey{% if is_winner %} skin-winner{% endif %}">{{ strokes }}</span>
{% elif diff >= 2 %}<span class="score-double-bogey{% if is_winner %} skin-winner{% endif %}">{{ strokes }}</span>
{% else %}<span{% if is_winner %} class="skin-winner"{% endif %}>{{ strokes }}</span>{% endif %}
{% else %}<span{% if is_winner %} class="skin-winner"{% endif %}>{{ strokes }}</span>{% endif %}
{% else %}—{% endif %}
</td>
{% endfor %}
<td class="score-cell">
{% if hr %}
{% if hr.winner %}✓{% if hr.pot > 1 %} {{ hr.pot }}{% endif %}
{% else %}→ {{ hr.pot }}{% endif %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr class="totals-row">
<td>Skins</td>
{% if hole_pars %}<td></td>{% endif %}
{% for p in players %}
<td>{{ skins_won.get(p.id, 0) }}</td>
{% endfor %}
<td>{% if unclaimed %}({{ unclaimed }}){% endif %}</td>
</tr>
</tfoot>
</table>
{% endif %}
</div>
{% endblock %}