visual overhaul + accounting corrections

This commit is contained in:
SowinskiBraeden committed 2026-06-22 13:51:38 -07:00
1 parent 77198e5b47
commit 70e62342ee
17 files changed
+1268 -389

No files matched your search

+278 -170
View File
@@ -1,5 +1,5 @@
{% extends "base.html" %}
{% block title %}Admin · boker.club{% endblock %}
{% block title %}Admin · myboker.org{% endblock %}
{% block page_class %}page--admin{% endblock %}
{% block content %}
@@ -11,7 +11,7 @@
{% endif %}
</div>
<h1 class="hero__title">Ledger controls</h1>
<p class="hero__sub">Append events to the audit trail. All history is permanent.</p>
<p class="hero__sub">Append events to the audit trail. Player ledger history stays permanent.</p>
</div>
<!-- Banker cash/book position. Cash tiles are real money only; rollovers only
@@ -22,7 +22,7 @@
<div class="stat__val" style="color:var(--num)">{{ admin_totals.cash_in_cents | money }}</div>
</div>
<div class="stat stat--rail" style="--rail:var(--pos);">
<span class="kicker">Cash out</span>
<span class="kicker">Cash paid out</span>
<div class="stat__val" style="color:var(--pos)">{{ admin_totals.cash_out_cents | money }}</div>
</div>
<div class="stat stat--rail" style="--rail:var(--neg);">
@@ -38,125 +38,255 @@
<div class="stat__val {% if admin_totals.net_book_position_cents == 0 %}num-muted{% elif admin_totals.net_book_position_cents > 0 %}num-pos{% else %}num-neg{% endif %}">
{{ admin_totals.net_book_position_cents | money }}
</div>
<div class="stat__sub">cash in - cash out + receivables - payables</div>
<div class="stat__sub">cash in - cash paid out + receivables - payables</div>
</div>
</div>
<!-- Two-column workspace -->
<!-- Admin workspace -->
<div class="admin-workspace">
<!-- LEFT: append form + recent sessions -->
<div class="col-main">
<form class="panel append" method="post" action="{{ url_for('admin_dashboard') }}">
<div class="panel__head">
<h2 class="panel__title">Append ledger event</h2>
<span class="panel__tag">fast entry</span>
</div>
<div class="append__body">
<div class="append__grid">
<label class="field">
<span class="field__label">Session</span>
<select class="control" name="session_id" required>
<option value="">Select session</option>
{% for s in open_sessions %}
<option value="{{ s.session_id }}" {{ 'selected' if append_form.session_id == s.session_id else '' }}>
{{ session_label(s) }}{{ ' (live)' if s.status == 'open' else '' }}
</option>
{% endfor %}
</select>
</label>
<label class="field">
<span class="field__label">Player</span>
<input class="control" list="player_names_list" type="text" name="player_name" placeholder="Player name" value="{{ append_form.player_name }}" required>
<datalist id="player_names_list">
{% for name in player_names %}<option value="{{ name }}"></option>{% endfor %}
</datalist>
</label>
<label class="field">
<span class="field__label">Event type</span>
<select class="control" name="event_type" required id="eventTypeSelect">
{% for value, label, color in event_types %}
<option value="{{ value }}" data-color="{{ color }}" data-label="{{ label }}" {{ 'selected' if append_form.event_type == value else '' }}>{{ label }}</option>
{% endfor %}
</select>
</label>
<label class="field">
<span class="field__label">Amount</span>
<span class="control control--money">
<span class="control__prefix">$</span>
<input name="amount" inputmode="decimal" placeholder="0" autofocus>
</span>
</label>
</div>
<label class="field append__note">
<span class="field__label">Note <span class="field__opt">(optional)</span></span>
<input class="control" name="note" placeholder="e.g. fronted buy-in, settle next week">
<form class="panel append" method="post" action="{{ url_for('admin_dashboard') }}">
<div class="panel__head">
<h2 class="panel__title">Append ledger event</h2>
<span class="panel__tag">fast entry</span>
</div>
<div class="append__body">
<div class="append__grid">
<label class="field">
<span class="field__label">Session</span>
<select class="control" name="session_id" required>
<option value="">Select session</option>
{% for s in open_sessions %}
<option value="{{ s.session_id }}" {{ 'selected' if append_form.session_id == s.session_id else '' }}>
{{ session_label(s) }}{{ ' (live)' if s.status == 'open' else '' }}
</option>
{% endfor %}
</select>
</label>
<div class="append__footer">
<button type="submit" class="btn btn--primary">Append event</button>
<span class="append__preview">Will record as
<span class="evtag-pill" id="eventTypePreview" style="color:{{ event_type_colors[append_form.event_type] }}">{{ event_type_labels[append_form.event_type] | upper }}</span>
<label class="field">
<span class="field__label">Player</span>
<input class="control" list="player_names_list" type="text" name="player_name" placeholder="Player name" value="{{ append_form.player_name }}" required>
<datalist id="player_names_list">
{% for name in player_names %}<option value="{{ name }}"></option>{% endfor %}
</datalist>
</label>
<label class="field">
<span class="field__label">Event type</span>
<select class="control" name="event_type" required id="eventTypeSelect">
{% for value, label, color in event_types %}
<option value="{{ value }}" data-color="{{ color }}" data-label="{{ label }}" {{ 'selected' if append_form.event_type == value else '' }}>{{ label }}</option>
{% endfor %}
</select>
</label>
<label class="field">
<span class="field__label">Amount</span>
<span class="control control--money">
<span class="control__prefix">$</span>
<input name="amount" inputmode="decimal" placeholder="0" autofocus>
</span>
</div>
</label>
</div>
</form>
</div><!-- /.col-main -->
<!-- RIGHT: session controls + CSV + raw events feed -->
<div class="col-side">
<div class="panel panel--pad">
<h2 class="panel__title panel__title--mb">Session controls</h2>
{% if live_session %}
<div class="livecard">
<div class="livecard__head">
<span class="dot dot--live"></span>
<span class="livecard__name">{{ session_label(live_session) }}</span>
<span class="livecard__no">{{ live_session_no }}</span>
<label class="field append__note">
<span class="field__label">Note <span class="field__opt">(optional)</span></span>
<textarea class="control append__textarea" name="note" placeholder="e.g. fronted buy-in, settle next week"></textarea>
</label>
<div class="append__footer">
<button type="submit" class="btn btn--primary">Append event</button>
<span class="append__preview">Will record as
<span class="evtag-pill" id="eventTypePreview" style="color:{{ event_type_colors[append_form.event_type] }}">{{ event_type_labels[append_form.event_type] | upper }}</span>
</span>
</div>
{% if pending_carry_items %}
<div class="carry-recs">
<div class="micro">Recommended carry-ins</div>
{% for item in pending_carry_items %}
<div class="carry-rec">
<span>{{ item.player_name }}</span>
<strong>{{ item.amount_cents | money }}</strong>
{% if live_session and item.player_name in live_session_player_names %}
<form method="post" action="{{ url_for('admin_apply_payout_carry_in') }}">
<input type="hidden" name="session_id" value="{{ live_session.session_id }}">
<input type="hidden" name="player_name" value="{{ item.player_name }}">
<button class="table-action-button" type="submit">Apply</button>
</form>
{% elif live_session %}
<em>Add buy-in first</em>
{% else %}
<em>Open session</em>
{% endif %}
</div>
<div class="livecard__metrics">
<div><div class="micro">In play</div><div class="mono16">{{ live_session.total_invested_cents | money }}</div></div>
<div><div class="micro">Seated</div><div class="mono16">{{ live_session.entries|length }}</div></div>
<div><div class="micro">Open</div><div class="mono16 mono16--warn">{{ live_unresolved_count }}</div></div>
</div>
<form method="post" action="{{ url_for('admin_session_state') }}">
<input type="hidden" name="session_id" value="{{ live_session.session_id }}">
<button class="btn-close" type="submit" name="state" value="closed">Close &amp; settle session</button>
</form>
{% endfor %}
</div>
{% endif %}
</div>
</form>
<form method="post" action="{{ url_for('admin_open_session') }}">
<input type="hidden" name="session_date" value="{{ admin_today }}">
<button class="btn-newsession {{ 'btn-newsession--primary' if not live_session else '' }}" type="submit">+ Open new session</button>
</form>
<div class="panel panel--pad session-control-panel">
<h2 class="panel__title panel__title--mb">Session controls</h2>
<div class="session-summary-strip">
<div>
<span class="micro">Today</span>
<strong>{{ admin_today }}</strong>
</div>
<div>
<span class="micro">Empty</span>
<strong>{{ empty_session_count }}</strong>
</div>
</div>
<div class="panel panel--pad">
<h2 class="panel__title" style="margin:0 0 4px">Import / export</h2>
<p class="csv__desc">Back up or migrate the raw event ledger.</p>
<form method="post" action="{{ url_for('admin_import_csv') }}" enctype="multipart/form-data" class="import-form">
<div class="csv__btns">
<a class="csv-btn-ghost" href="{{ url_for('admin_export_csv') }}">&darr; Export CSV</a>
<label class="csv-btn-ghost" for="csvTopImport">&uarr; Import CSV</label>
</div>
<input id="csvTopImport" type="file" accept=".csv,text/csv" name="csv_file" hidden onchange="this.form.submit()">
<label class="csv__drop" id="csvDrop" for="csvTopImport">Drop ledger.csv to import</label>
{% if live_session %}
<div class="livecard">
<div class="livecard__head">
<span class="dot dot--live"></span>
<span class="livecard__name">{{ session_label(live_session) }}</span>
<span class="livecard__no">{{ live_session_no }}</span>
</div>
<div class="livecard__metrics">
<div><div class="micro">Invested</div><div class="mono16">{{ live_session.total_invested_cents | money }}</div></div>
<div><div class="micro">Seated</div><div class="mono16">{{ live_session.entries|length }}</div></div>
<div><div class="micro">Open</div><div class="mono16 mono16--warn">{{ live_unresolved_count }}</div></div>
</div>
<form method="post" action="{{ url_for('admin_session_state') }}"
{% if live_unresolved_count > 0 %}data-close-warning="true"{% endif %}>
<input type="hidden" name="session_id" value="{{ live_session.session_id }}">
<button class="btn-close" type="submit" name="state" value="closed">Close session</button>
</form>
</div>
{% else %}
<div class="livecard livecard--empty">
<div class="livecard__head">
<span class="dot dot--idle"></span>
<span class="livecard__name">No open session</span>
<span class="livecard__no">idle</span>
</div>
<div class="livecard__metrics">
<div><div class="micro">Invested</div><div class="mono16">{{ 0 | money }}</div></div>
<div><div class="micro">Seated</div><div class="mono16">0</div></div>
<div><div class="micro">Open</div><div class="mono16">0</div></div>
</div>
</div>
{% endif %}
</div><!-- /.col-side -->
<form method="post" action="{{ url_for('admin_open_session') }}">
<input type="hidden" name="session_date" value="{{ admin_today }}">
<button class="btn-newsession {{ 'btn-newsession--primary' if not live_session else '' }}" type="submit">+ Open new session</button>
</form>
<form method="post" action="{{ url_for('admin_prune_empty_sessions') }}" class="prune-session-form">
<button class="secondary-button" type="submit" {% if empty_session_count == 0 %}disabled{% endif %}>
Prune empty sessions{% if empty_session_count > 0 %} ({{ empty_session_count }}){% endif %}
</button>
</form>
</div>
<div class="panel panel--pad import-export-panel">
<h2 class="panel__title" style="margin:0 0 4px">Import / export</h2>
<p class="csv__desc">Back up or migrate the raw event ledger.</p>
<form method="post" action="{{ url_for('admin_import_csv') }}" enctype="multipart/form-data" class="import-form">
<div class="csv__btns">
<a class="csv-btn-ghost" href="{{ url_for('admin_export_csv') }}">&darr; Export CSV</a>
<label class="csv-btn-ghost" for="csvTopImport">&uarr; Import CSV</label>
</div>
<input id="csvTopImport" type="file" accept=".csv,text/csv" name="csv_file" hidden onchange="this.form.submit()">
<label class="csv__drop" id="csvDrop" for="csvTopImport">Drop ledger.csv to import</label>
</form>
</div>
</div><!-- /.admin-workspace -->
<!-- Debt resolution -->
<div class="panel debt-panel">
<div class="panel__head">
<div>
<span class="kicker" style="margin:0;">Front receivables</span>
<h2 class="panel__title">Debt resolution</h2>
</div>
<div class="debt-summary">
<span class="badge badge--owes">Open {{ admin_totals.players_owe_house_cents | money }}</span>
<span class="badge badge--paid">Cash repaid {{ admin_totals.collected_cents | money }}</span>
<span class="badge badge--written_off">Written off {{ admin_totals.written_off_cents | money }}</span>
</div>
</div>
{% if debt_entries %}
<div class="debt-layout">
<div class="debt-list">
{% for item in debt_entries %}
{% set s = item.session %}{% set e = item.entry %}
<div class="debt-list-item">
<div>
<strong>{{ e.player_name }}</strong>
<span>{{ session_label(s) }}</span>
</div>
<b>{{ e.player_owes_cents | money }}</b>
</div>
{% endfor %}
</div>
<div class="debt-action-grid">
<form class="form-card debt-form" method="post" action="{{ url_for('admin_collect_front') }}">
<h3>Collect debt</h3>
<label>
<span>Outstanding debt</span>
<select name="debt_key" required>
<option value="">Select debt</option>
{% for item in debt_entries %}
{% set s = item.session %}{% set e = item.entry %}
<option value="{{ s.session_id }}||{{ e.player_name }}">
{{ e.player_name }} · {{ session_label(s) }} · owes {{ e.player_owes_cents | money }}
</option>
{% endfor %}
</select>
</label>
<label>
<span>Amount collected</span>
<input type="number" step="0.01" name="amount" placeholder="0.00" required>
</label>
<label>
<span>Note</span>
<textarea name="note" rows="2" placeholder="E-transfer received, cash, etc."></textarea>
</label>
<button class="primary-button" type="submit">Mark collected</button>
</form>
<form class="form-card debt-form" method="post" action="{{ url_for('admin_write_off_front') }}">
<h3>Write off debt</h3>
<label>
<span>Outstanding debt</span>
<select name="debt_key" required>
<option value="">Select debt</option>
{% for item in debt_entries %}
{% set s = item.session %}{% set e = item.entry %}
<option value="{{ s.session_id }}||{{ e.player_name }}">
{{ e.player_name }} · {{ session_label(s) }} · owes {{ e.player_owes_cents | money }}
</option>
{% endfor %}
</select>
</label>
<label>
<span>Amount to write off</span>
<input type="number" step="0.01" name="amount" placeholder="0.00" required>
</label>
<label>
<span>Note</span>
<textarea name="note" rows="2" placeholder="Won't be collected."></textarea>
</label>
<button class="secondary-button" type="submit">Write off debt</button>
</form>
</div>
</div>
{% else %}
<div class="debt-empty">
<div class="debt-empty__mark">$0</div>
<p>No open player debts right now.</p>
</div>
{% endif %}
</div>
<div class="admin-bottom-grid">
<div class="panel">
<div class="panel__head">
<div>
<span class="kicker" style="margin:0;">Recent sessions</span>
<span class="kicker" style="margin:0;">All sessions</span>
<h2 class="panel__title">Current totals</h2>
</div>
</div>
@@ -167,8 +297,8 @@
<th>Session</th>
<th>Players</th>
<th>Cash in</th>
<th>Cash out</th>
<th>House owes</th>
<th>Cash paid out</th>
<th>House owes players</th>
<th>Players owe house</th>
<th>Book position</th>
<th>Status</th>
@@ -180,8 +310,8 @@
<tr>
<td><a href="{{ url_for('session_detail', session_id=s.session_id) }}">{{ session_label(s) }}</a></td>
<td style="font-family:var(--font-mono);color:var(--num)">{{ s.entries|length }}</td>
<td style="font-family:var(--font-mono);color:var(--num)">{{ s.total_cash_in_cents | money }}</td>
<td style="font-family:var(--font-mono);color:var(--pos)">{{ s.total_paid_out_cents | money }}</td>
<td style="font-family:var(--font-mono);color:var(--num)">{{ s.total_banker_cash_in_cents | money }}</td>
<td style="font-family:var(--font-mono);color:var(--pos)">{{ s.total_banker_cash_out_cents | money }}</td>
<td class="{{ 'negative' if s.total_current_due_to_player_cents > 0 else '' }}" style="font-family:var(--font-mono)">{{ s.total_current_due_to_player_cents | money }}</td>
<td class="{{ 'negative' if s.total_current_due_to_house_cents > 0 else '' }}" style="font-family:var(--font-mono)">{{ s.total_current_due_to_house_cents | money }}</td>
<td class="{% if s.total_net_book_position_cents > 0 %}positive{% elif s.total_net_book_position_cents < 0 %}negative{% endif %}" style="font-family:var(--font-mono)">{{ s.total_net_book_position_cents | money }}</td>
@@ -231,6 +361,20 @@
</div>
</div>
<div class="modal-backdrop" id="closeSessionModal" hidden>
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="closeSessionModalTitle">
<div class="modal-card__head">
<span class="kicker" style="margin:0;">Open settlement</span>
<h2 class="panel__title" id="closeSessionModalTitle">Close session?</h2>
</div>
<p class="modal-card__text">This session has unpaid or uncarried cashouts, or unresolved debts. Closing stops table entry, but the open settlement items will remain tracked.</p>
<div class="modal-card__actions">
<button class="secondary-button" type="button" id="cancelCloseSession">Keep open</button>
<button class="btn btn--primary" type="button" id="confirmCloseSession">Close session</button>
</div>
</div>
</div>
<script>
const eventTypeSelect = document.getElementById('eventTypeSelect');
const eventTypePreview = document.getElementById('eventTypePreview');
@@ -255,74 +399,38 @@ csvDrop?.addEventListener('drop', (event) => {
csvInput.files = event.dataTransfer.files;
csvInput.form?.submit();
});
const closeSessionModal = document.getElementById('closeSessionModal');
const cancelCloseSession = document.getElementById('cancelCloseSession');
const confirmCloseSession = document.getElementById('confirmCloseSession');
let pendingCloseForm = null;
document.querySelectorAll('form[data-close-warning="true"]').forEach((form) => {
form.addEventListener('submit', (event) => {
if (form.dataset.confirmed === 'true') return;
event.preventDefault();
pendingCloseForm = form;
closeSessionModal.hidden = false;
confirmCloseSession?.focus();
});
});
cancelCloseSession?.addEventListener('click', () => {
closeSessionModal.hidden = true;
pendingCloseForm = null;
});
closeSessionModal?.addEventListener('click', (event) => {
if (event.target !== closeSessionModal) return;
closeSessionModal.hidden = true;
pendingCloseForm = null;
});
confirmCloseSession?.addEventListener('click', () => {
if (!pendingCloseForm) return;
pendingCloseForm.dataset.confirmed = 'true';
pendingCloseForm.submit();
});
</script>
<!-- Front debt resolution -->
{% if debt_entries %}
<div class="panel debt-panel" style="margin-top:20px;">
<div class="panel__head">
<div>
<span class="kicker" style="margin:0;">Front receivables</span>
<h2 class="panel__title">Debt resolution</h2>
</div>
<div class="debt-summary">
<span class="badge badge--owes">Open {{ admin_totals.players_owe_house_cents | money }}</span>
<span class="badge badge--paid">Repaid {{ admin_totals.collected_cents | money }}</span>
<span class="badge badge--written_off">Written off {{ admin_totals.written_off_cents | money }}</span>
</div>
</div>
<div class="debt-action-grid" style="padding:20px;">
<form class="form-card" style="padding:0;background:transparent;" method="post" action="{{ url_for('admin_collect_front') }}">
<h3>Collect debt</h3>
<label>
<span>Outstanding debt</span>
<select name="debt_key" required>
<option value="">Select debt</option>
{% for item in debt_entries %}
{% set s = item.session %}{% set e = item.entry %}
<option value="{{ s.session_id }}||{{ e.player_name }}">
{{ e.player_name }} · {{ session_label(s) }} · owes {{ e.player_owes_cents | money }}
</option>
{% endfor %}
</select>
</label>
<label>
<span>Amount collected</span>
<input type="number" step="0.01" name="amount" placeholder="0.00" required>
</label>
<label>
<span>Note</span>
<textarea name="note" rows="2" placeholder="E-transfer received, cash, etc."></textarea>
</label>
<button class="primary-button" type="submit">Mark collected</button>
</form>
<form class="form-card" style="padding:0;background:transparent;" method="post" action="{{ url_for('admin_write_off_front') }}">
<h3>Write off debt</h3>
<label>
<span>Outstanding debt</span>
<select name="debt_key" required>
<option value="">Select debt</option>
{% for item in debt_entries %}
{% set s = item.session %}{% set e = item.entry %}
<option value="{{ s.session_id }}||{{ e.player_name }}">
{{ e.player_name }} · {{ session_label(s) }} · owes {{ e.player_owes_cents | money }}
</option>
{% endfor %}
</select>
</label>
<label>
<span>Amount to write off</span>
<input type="number" step="0.01" name="amount" placeholder="0.00" required>
</label>
<label>
<span>Note</span>
<textarea name="note" rows="2" placeholder="Won't be collected."></textarea>
</label>
<button class="secondary-button" type="submit">Write off front</button>
</form>
</div>
</div>
{% endif %}
{% endblock %}
+2 -2
View File
@@ -1,5 +1,5 @@
{% extends "base.html" %}
{% block title %}Admin Login · boker.club{% endblock %}
{% block title %}Admin Login · myboker.org{% endblock %}
{% block content %}
<div class="form-shell narrow">
@@ -7,7 +7,7 @@
<div>
<p class="eyebrow">Restricted access</p>
<h1 style="font-size:28px;margin-bottom:8px;">Admin login</h1>
<p class="muted-text" style="margin-bottom:8px;">Only boker.club manager needs credentials. All stats pages stay public.</p>
<p class="muted-text" style="margin-bottom:8px;">Only myboker.org manager needs credentials. All stats pages stay public.</p>
</div>
<label>
<span>Username</span>
+2 -2
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}boker.club{% endblock %}</title>
<title>{% block title %}myboker.org{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/tokens.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
@@ -13,7 +13,7 @@
<div class="topbar__inner">
<a class="brand" href="{{ url_for('leaderboard') }}">
<span class="brand__mark"></span>
<span class="brand__name">boker.club</span>
<span class="brand__name">myboker.org</span>
</a>
<nav class="mainnav">
<a class="navlink {{ 'is-active' if request.endpoint == 'leaderboard' }}" href="{{ url_for('leaderboard') }}">Leaderboard</a>
+1 -1
View File
@@ -1,5 +1,5 @@
{% extends "base.html" %}
{% block title %}Leaderboard · boker.club{% endblock %}
{% block title %}Leaderboard · myboker.org{% endblock %}
{% block content %}
<div class="hero" style="display:flex;flex-wrap:wrap;gap:var(--s6);align-items:flex-start;justify-content:space-between;">
+43 -17
View File
@@ -1,5 +1,5 @@
{% extends "base.html" %}
{% block title %}{{ player.player_name }} · boker.club{% endblock %}
{% block title %}{{ player.player_name }} · myboker.org{% endblock %}
{% block content %}
<div class="hero">
@@ -52,7 +52,7 @@
<div class="stat__val" style="color:var(--num)">{{ player.total_invested_cents | money }}</div>
</div>
<div class="stat">
<span class="kicker">Cash-outs</span>
<span class="kicker">Gross cashouts</span>
<div class="stat__val" style="color:var(--num)">{{ player.total_cash_out_cents | money }}</div>
</div>
</div>
@@ -105,15 +105,15 @@
<div class="result-mix__legend">
<div class="result-mix__legend-row">
<span><i class="result-mix__key result-mix__key--win"></i>Wins</span>
<strong>{{ player.winning_sessions }} · {{ "%.1f"|format((player.winning_sessions / result_total * 100) if result_total else 0) }}%</strong>
<strong><b>{{ player.winning_sessions }}</b><em>{{ "%.1f"|format((player.winning_sessions / result_total * 100) if result_total else 0) }}%</em></strong>
</div>
<div class="result-mix__legend-row">
<span><i class="result-mix__key result-mix__key--even"></i>Even</span>
<strong>{{ player.break_even_sessions }} · {{ "%.1f"|format((player.break_even_sessions / result_total * 100) if result_total else 0) }}%</strong>
<strong><b>{{ player.break_even_sessions }}</b><em>{{ "%.1f"|format((player.break_even_sessions / result_total * 100) if result_total else 0) }}%</em></strong>
</div>
<div class="result-mix__legend-row">
<span><i class="result-mix__key result-mix__key--loss"></i>Losses</span>
<strong>{{ player.losing_sessions }} · {{ "%.1f"|format((player.losing_sessions / result_total * 100) if result_total else 0) }}%</strong>
<strong><b>{{ player.losing_sessions }}</b><em>{{ "%.1f"|format((player.losing_sessions / result_total * 100) if result_total else 0) }}%</em></strong>
</div>
</div>
{% if result_total > 0 %}
@@ -151,6 +151,16 @@
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Win %</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--num);">{{ "%.1f"|format(player.win_pct) }}%</td>
</tr>
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Record</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--num);">
<span style="color:var(--pos)">{{ player.winning_sessions }}W</span>
<span style="color:var(--faintest);margin:0 4px;">·</span>
<span style="color:var(--neg)">{{ player.losing_sessions }}L</span>
<span style="color:var(--faintest);margin:0 4px;">·</span>
<span>{{ player.break_even_sessions }}E</span>
</td>
</tr>
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">ROI</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);"
@@ -197,7 +207,7 @@
<div>
<div class="section-marker">
<span class="section-marker__dot section-marker__dot--rolled"></span>
Poker basis
Poker basis + settlement
</div>
<h2 class="panel__title">Investment and settlement</h2>
</div>
@@ -216,38 +226,54 @@
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Rolled in</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--rolled);">{{ player.total_rollover_in_cents | money }}</td>
</tr>
{% if player.total_payout_carry_in_cents > 0 %}
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Payout carry-in</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--rolled);">{{ player.total_payout_carry_in_cents | money }}</td>
</tr>
{% endif %}
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Total invested</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--num);">{{ player.total_invested_cents | money }}</td>
</tr>
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Cash-outs</td>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Gross cashout result</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--num);">{{ player.total_cash_out_cents | money }}</td>
</tr>
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Gross player claim</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--num);">{{ player.total_gross_payout_cents | money }}</td>
</tr>
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Rolled out</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--rolled);">{{ player.total_rollover_out_cents | money }}</td>
</tr>
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Paid out</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--pos);">{{ player.total_paid_cents | money }}</td>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Cash paid out</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--pos);">{{ player.total_paid_out_cents | money }}</td>
</tr>
{% if player.total_front_collected_cents > 0 %}
{% if player.total_debt_repayment_cents > 0 %}
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Debt repaid</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--pos);">{{ player.total_front_collected_cents | money }}</td>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Cash debt repaid</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--pos);">{{ player.total_debt_repayment_cents | money }}</td>
</tr>
{% endif %}
{% if player.current_player_owes_cents > 0 %}
{% if player.current_due_to_player_cents > 0 %}
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Still owes</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--owes);">{{ player.current_player_owes_cents | money }}</td>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">House owes player</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--neg);">{{ player.current_due_to_player_cents | money }}</td>
</tr>
{% endif %}
{% if player.total_front_writeoff_cents > 0 %}
{% if player.current_due_to_house_cents > 0 %}
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Owes house</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--owes);">{{ player.current_due_to_house_cents | money }}</td>
</tr>
{% endif %}
{% if player.total_writeoff_cents > 0 %}
<tr>
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Written off</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--neg);">{{ player.total_front_writeoff_cents | money }}</td>
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--neg);">{{ player.total_writeoff_cents | money }}</td>
</tr>
{% endif %}
</tbody>
+27 -23
View File
@@ -1,5 +1,5 @@
{% extends "base.html" %}
{% block title %}{{ session_label(session) }} · boker.club{% endblock %}
{% block title %}{{ session_label(session) }} · myboker.org{% endblock %}
{% block page_class %}page--session{% endblock %}
{% block content %}
@@ -28,20 +28,20 @@
<div class="recon-grid" style="margin-bottom:20px;">
<div class="panel">
<div class="recon-card-head">
<span class="kicker kicker--dot kicker--dot-accent">Cash flow</span>
<span class="kicker kicker--dot kicker--dot-accent">Banker cash flow</span>
</div>
<div class="recon-metric-grid">
<div class="recon-cell">
<span class="kicker" style="font-size:10px;">Cash in</span>
<div class="recon-val recon-val--num">{{ session.total_cash_in_cents | money }}</div>
<span class="kicker" style="font-size:10px;">Actual cash in</span>
<div class="recon-val recon-val--num">{{ session.total_real_cash_in_cents | money }}</div>
</div>
<div class="recon-cell">
<span class="kicker" style="font-size:10px;">Gross payout</span>
<span class="kicker" style="font-size:10px;">Gross player claim</span>
<div class="recon-val recon-val--num">{{ session.total_gross_payout_cents | money }}</div>
</div>
<div class="recon-cell">
<span class="kicker" style="font-size:10px;">Cash paid out</span>
<div class="recon-val recon-val--pos">{{ session.total_paid_cents | money }}</div>
<div class="recon-val recon-val--pos">{{ session.total_real_cash_out_cents | money }}</div>
</div>
<div class="recon-cell">
<span class="kicker" style="font-size:10px;">Rolled out</span>
@@ -55,7 +55,7 @@
</div>
<div class="recon-metric-grid">
<div class="recon-cell">
<span class="kicker" style="font-size:10px;">House owes</span>
<span class="kicker" style="font-size:10px;">House owes players</span>
<div class="recon-val {{ 'recon-val--neg' if session.total_current_due_to_player_cents > 0 else 'recon-val--pos' }}">
{{ session.total_current_due_to_player_cents | money }}
</div>
@@ -67,12 +67,12 @@
</div>
</div>
<div class="recon-cell">
<span class="kicker" style="font-size:10px;">Debt repaid</span>
<div class="recon-val" style="color:var(--pos)">{{ session.total_front_collected_cents | money }}</div>
<span class="kicker" style="font-size:10px;">Cash debt repaid</span>
<div class="recon-val" style="color:var(--pos)">{{ session.total_debt_repayment_cents | money }}</div>
</div>
<div class="recon-cell">
<span class="kicker" style="font-size:10px;">Written off</span>
<div class="recon-val" style="color:var(--neg)">{{ session.total_front_writeoff_cents | money }}</div>
<div class="recon-val" style="color:var(--neg)">{{ session.total_writeoff_cents | money }}</div>
</div>
</div>
</div>
@@ -96,14 +96,15 @@
<tr>
<th class="col-player">Player</th>
<th>Invested</th>
<th>Cash-out</th>
<th>Gross cashout</th>
<th>Net</th>
<th>Cash paid</th>
<th>Settlement</th>
<th>Status</th>
<th class="col-acct">Fronted</th>
<th>Paid out</th>
<th>Open settlement</th>
<th>Settlement status</th>
<th class="col-acct">Front</th>
<th class="col-acct">Rolled in</th>
<th class="col-acct">Gross payout</th>
<th class="col-acct">Payout carry-in</th>
<th class="col-acct">Gross player claim</th>
<th class="col-acct">Rolled out</th>
<th class="col-acct">Notes</th>
</tr>
@@ -119,10 +120,12 @@
<td class="num-net {{ 'num-pos' if entry.net_cents > 0 else 'num-neg' if entry.net_cents < 0 else 'num-muted' }}">
{{ '+' if entry.net_cents > 0 else '' }}{{ entry.net_cents | money }}
</td>
<td style="color:var(--pos)">{{ entry.paid_cents | money }}</td>
<td style="color:var(--pos)">{{ entry.paid_out_cents | money }}</td>
<td class="{{ 'num-neg' if entry.current_due_to_player_cents > 0 else '' }}">
{% if entry.current_due_to_house_cents > 0 %}
<span style="color:var(--owes)">owes {{ entry.current_due_to_house_cents | money }}</span>
{% elif entry.current_due_to_player_cents > 0 %}
<span style="color:var(--neg)">due {{ entry.current_due_to_player_cents | money }}</span>
{% else %}
{{ entry.current_due_to_player_cents | money }}
{% endif %}
@@ -132,8 +135,9 @@
</td>
<td class="col-acct" style="color:var(--warn)">{{ entry.front_cents | money }}</td>
<td class="col-acct" style="color:var(--rolled)">{{ entry.rollover_in_cents | money }}</td>
<td class="col-acct" style="color:var(--rolled)">{{ entry.payout_carry_in_cents | money }}</td>
<td class="col-acct">
{% if entry.player_owes_gross_cents > 0 %}—{% else %}{{ entry.gross_payout_cents | money }}{% endif %}
{% if entry.gross_due_to_house_cents > 0 %}—{% else %}{{ entry.gross_payout_cents | money }}{% endif %}
</td>
<td class="col-acct" style="color:var(--rolled)">{{ entry.rollover_out_cents | money }}</td>
<td class="col-acct" style="text-align:left;font-family:var(--font-ui);">
@@ -151,7 +155,7 @@
</div>
<!-- Bottom row: net bars + snapshot + audit feed -->
<div class="split" style="align-items:stretch;">
<div class="split">
<div class="panel col-main">
<div class="panel__head">
<span class="kicker" style="margin:0;">Breakdown</span>
@@ -159,17 +163,17 @@
</div>
<div class="chart-wrap">
<div class="session-net-bars" id="sessionBreakdownChart" aria-label="Net by player"></div>
</div>
</div>
</div>
<div class="panel col-side" style="display:flex;flex-direction:column;">
<div class="panel col-side">
<div class="panel__head">
<span class="kicker" style="margin:0;">Quick read</span>
<h2 class="panel__title">Session snapshot</h2>
</div>
{% set winners = session.entries | selectattr('net_cents', 'gt', 0) | list %}
{% set losers = session.entries | selectattr('net_cents', 'lt', 0) | list %}
<div class="snapshot-grid" style="flex:1;">
<div class="snapshot-grid">
<div class="snapshot-cell">
<span class="kicker" style="font-size:10px;">Biggest winner</span>
<div class="snapshot-val" style="color:var(--pos)">
@@ -185,7 +189,7 @@
{% if losers %}<div style="font-size:12px;color:var(--muted);margin-top:4px;">{{ losers | sort(attribute='net_cents') | first | attr('player_name') }}</div>{% endif %}
</div>
<div class="snapshot-cell">
<span class="kicker" style="font-size:10px;">Total cash-out</span>
<span class="kicker" style="font-size:10px;">Gross cashout result</span>
<div class="snapshot-val">{{ session.total_cash_out_cents | money }}</div>
</div>
<div class="snapshot-cell">
+2 -2
View File
@@ -1,5 +1,5 @@
{% extends "base.html" %}
{% block title %}Sessions · boker.club{% endblock %}
{% block title %}Sessions · myboker.org{% endblock %}
{% block content %}
<div class="hero">
@@ -23,7 +23,7 @@
<th>Status</th>
<th>Players</th>
<th>Invested</th>
<th>Cash out</th>
<th>Gross cashout</th>
<th>Table net</th>
</tr>
</thead>