Files
boker/templates/session_detail.html
T
2026-03-21 14:53:47 -07:00

274 lines
9.9 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ session.session_date }} · Poker Portal{% endblock %}
{% block content %}
<section class="hero-card compact">
<div style="display: flex; justify-content: space-between;">
<div>
<p class="eyebrow">Session detail</p>
<h1>{{ session.session_date | pretty_date }}</h1>
<p class="muted-text">Current totals are derived from the event log for this date.</p>
</div>
<div style="align-content: center;">
{% if prev_session %}
<a class="secondary-button" href="{{ url_for('session_detail', session_date=prev_session.session_date) }}">Previous Session</a>
{% endif %}
{% if next_session %}
<a class="secondary-button" href="{{ url_for('session_detail', session_date=next_session.session_date) }}">Next Session</a>
{% endif %}
</div>
</div>
</section>
<section class="grid payout-summary-grid">
<article class="panel stat-card">
<span>Total payout due</span>
<strong>{{ session.total_payout_due_cents | money }}</strong>
</article>
<article class="panel stat-card">
<span>Total paid</span>
<strong class="positive">{{ session.total_paid_cents | money }}</strong>
</article>
<article class="panel stat-card">
<span>Total remaining</span>
<strong class="{{ 'negative' if session.total_remaining_cents > 0 else 'positive' }}">
{{ session.total_remaining_cents | money }}
</strong>
</article>
<article class="panel stat-card">
<span>Total fronted</span>
<strong>{{ session.total_front_cents | money }}</strong>
</article>
<article class="panel stat-card">
<span>Players still owe</span>
<strong class="{{ 'negative' if session.total_player_owes_cents > 0 else '' }}">
{{ session.total_player_owes_cents | money }}
</strong>
</article>
</section>
<section class="panel results-panel results-panel-full">
<div class="panel-header">
<div>
<p class="eyebrow">Results</p>
<h2>Player totals</h2>
</div>
</div>
<div class="table-scroll table-scroll-open">
<table class="data-table session-results-table">
<thead>
<tr>
<th>Player</th>
<th>Buy-in</th>
<th>Fronted</th>
<th>Cash-out</th>
<th>Net</th>
<th>Status</th>
<th>Paid</th>
<th>Remaining</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
{% for entry in session.entries %}
<tr>
<td>
<a href="{{ url_for('player_detail', player_name=entry.player_name) }}">
{{ entry.player_name }}
</a>
</td>
<td>{{ entry.buy_in_cents | money }}</td>
<td>{{ entry.front_cents | money }}</td>
<td>{{ entry.cash_out_cents | money }}</td>
<td class="{{ 'positive' if entry.net_cents > 0 else 'negative' if entry.net_cents < 0 else '' }}">
{{ entry.net_cents | money }}
</td>
<td>
<span class="status-badge status-{{ entry.payout_status }}">
{{ entry.payout_status }}
</span>
</td>
<td class="{{ 'positive' if entry.paid_cents > 0 else '' }}">{{ entry.paid_cents | money }}</td>
<td class="{{ 'negative' if entry.payout_remaining_cents > 0 else '' }}">
{{ entry.payout_remaining_cents | money }}
</td>
<td>
{% if entry.notes %}
<ul class="note-list">
{% for note in entry.notes %}
<li>{{ note }}</li>
{% endfor %}
</ul>
{% else %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
<section class="grid two-col chart-row">
<div class="panel chart-panel chart-panel-compact">
<div class="panel-header">
<div>
<p class="eyebrow">Breakdown</p>
<h2>Net by player</h2>
</div>
</div>
<div class="chart-frame chart-frame-compact">
<canvas id="sessionBreakdownChart"></canvas>
</div>
</div>
<div class="panel session-insights-panel">
<div class="panel-header">
<div>
<p class="eyebrow">Quick read</p>
<h2>Session snapshot</h2>
</div>
</div>
{% set winners = session.entries | selectattr('net_cents', 'gt', 0) | list %}
{% set losers = session.entries | selectattr('net_cents', 'lt', 0) | list %}
<div class="detail-grid">
<div class="detail-row">
<span>Players</span>
<strong>{{ session.entries|length }}</strong>
</div>
<div class="detail-row">
<span>Biggest winner</span>
<strong>
{% if winners %}
{{ winners | sort(attribute='net_cents', reverse=True) | first | attr('player_name') }}
{% else %}
{% endif %}
</strong>
</div>
<div class="detail-row">
<span>Best result</span>
<strong class="positive">
{% if winners %}
{{ (winners | sort(attribute='net_cents', reverse=True) | first).net_cents | money }}
{% else %}
$0.00
{% endif %}
</strong>
</div>
<div class="detail-row">
<span>Biggest loser</span>
<strong>
{% if losers %}
{{ losers | sort(attribute='net_cents') | first | attr('player_name') }}
{% else %}
{% endif %}
</strong>
</div>
<div class="detail-row">
<span>Worst result</span>
<strong class="negative">
{% if losers %}
{{ (losers | sort(attribute='net_cents') | first).net_cents | money }}
{% else %}
$0.00
{% endif %}
</strong>
</div>
<div class="detail-row">
<span>Unpaid total</span>
<strong class="{{ 'negative' if session.total_remaining_cents > 0 else 'positive' }}">
{{ session.total_remaining_cents | money }}
</strong>
</div>
</div>
</div>
</section>
<section class="panel">
<div class="panel-header">
<div>
<p class="eyebrow">Audit trail</p>
<h2>Raw ledger events</h2>
</div>
</div>
<div class="event-feed">
{% for event in raw_events | reverse %}
<article class="event-card">
<div class="event-card-top">
<strong>{{ event.player_name or "Session" }}</strong>
<span class="pill pill-{{ event.event_type }}">{{ event.event_type }}</span>
</div>
<p class="event-amount">{{ event.amount_cents | money }}</p>
{% if event.note %}
<p class="muted-text small">{{ event.note }}</p>
{% endif %}
<p class="tiny-text">{{ event.created_at }} · by {{ event.actor }}</p>
</article>
{% else %}
<p class="muted-text">No raw events recorded.</p>
{% endfor %}
</div>
</section>
<script>
const sessionChartData = {{ chart_data | tojson }};
const rootStyles = getComputedStyle(document.documentElement);
const textMuted = rootStyles.getPropertyValue('--text-muted').trim();
const lineColor = rootStyles.getPropertyValue('--line').trim();
const sessionBreakdownCtx = document.getElementById('sessionBreakdownChart');
if (sessionBreakdownCtx) {
new Chart(sessionBreakdownCtx, {
type: 'bar',
data: {
labels: sessionChartData.labels,
datasets: [{
label: 'Session Net',
data: sessionChartData.net_values,
backgroundColor: sessionChartData.net_colors,
borderColor: sessionChartData.net_colors,
borderRadius: 8,
borderSkipped: false,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
color: textMuted,
usePointStyle: true,
boxWidth: 10,
boxHeight: 10,
padding: 18,
},
},
},
scales: {
x: {
ticks: { color: textMuted, maxRotation: 0 },
grid: { color: lineColor },
},
y: {
ticks: { color: textMuted },
grid: { color: lineColor },
},
},
},
});
}
</script>
{% endblock %}