Files
SkinsPins/app/templates/games/summary.html
T
Rolf 1daf6a99c4 Add playing handicap and stroke allocation to game summary
- Calculate course handicap per player (WHS formula) in game_summary
- Show Playing Handicaps card with HI, HC, and tee per player
- Mark stroke-receiving holes with accent dots in strokes and skins scorecards
- Add HC footer row to strokes scorecard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 11:16:26 +01:00

284 lines
12 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>
{# Playing handicaps #}
{% if playing_handicaps %}
<div class="card" style="margin-top:0.75rem; padding:0.75rem 1rem;">
<div style="font-size:0.8rem; color:var(--text-muted); font-weight:500; margin-bottom:0.5rem; text-transform:uppercase; letter-spacing:0.05em;">Playing Handicaps</div>
{% for p in players %}
<div style="display:flex; align-items:center; gap:0.6rem; padding:0.35rem 0; {% if not loop.last %}border-bottom:1px solid var(--border);{% endif %} font-size:0.9rem;">
<span>
{% 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 %}
</span>
<span style="flex:1; font-weight:500;">{{ p.name }}</span>
{% if p.handicap_snapshot is not none %}
<span style="color:var(--text-muted);">HI&nbsp;{{ p.handicap_snapshot }}</span>
{% endif %}
{% set ph = playing_handicaps.get(p.id) %}
{% if ph is not none %}
<span style="font-weight:600; color:var(--accent); min-width:3rem; text-align:right;">HC&nbsp;{{ ph }}</span>
{% else %}
<span style="color:var(--text-muted); min-width:3rem; text-align:right;">HC&nbsp;</span>
{% endif %}
{% if p.tee_name %}
<span style="color:var(--text-muted); font-size:0.8rem; min-width:4rem; text-align:right;">{{ p.tee_name }}</span>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
{# 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) %}
{% set sr = strokes_per_hole.get(p.id, {}).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 %}
{% if sr > 0 %}<sup style="color:var(--accent); font-size:0.6em; line-height:0;">{{ '·' * sr }}</sup>{% 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>
{% if playing_handicaps %}
<tr>
<td style="color:var(--text-muted); font-size:0.8rem;">HC</td>
{% if hole_pars %}<td></td>{% endif %}
{% for p in players %}
<td style="color:var(--text-muted); font-size:0.8rem;">{{ playing_handicaps.get(p.id, '—') if playing_handicaps.get(p.id) is not none else '—' }}</td>
{% endfor %}
</tr>
{% endif %}
</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) %}
{% set sr = strokes_per_hole.get(p.id, {}).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 %}
{% if sr > 0 %}<sup style="color:var(--accent); font-size:0.6em; line-height:0;">{{ '·' * sr }}</sup>{% 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 %}