347 lines
19 KiB
HTML
347 lines
19 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}{{ player.player_name }} · {{ league.name }} · myboker.org{% endblock %}
|
|
{% block subnav %}{% include "_league_subnav.html" %}{% endblock %}
|
|
{% block content %}
|
|
|
|
|
|
<div class="hero">
|
|
<div>
|
|
{% if player_rank %}
|
|
<p class="player-rank">Rank #{{ player_rank }}</p>
|
|
{% endif %}
|
|
<h1 class="hero__title">{{ player.player_name }}</h1>
|
|
<p class="hero__sub">{{ player.sessions_played }} session{{ 's' if player.sessions_played != 1 else '' }} · {{ "%.1f"|format(player.win_pct) }}% win rate</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="player-stat-grid" style="margin-bottom:20px;">
|
|
<div class="stat">
|
|
<span class="kicker">Total net</span>
|
|
<div class="stat__val {{ 'num-pos' if player.total_net_cents > 0 else 'num-neg' if player.total_net_cents < 0 else 'num-muted' }}">
|
|
{{ '+' if player.total_net_cents > 0 else '' }}{{ player.total_net_cents | money }}
|
|
</div>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="kicker">Win rate</span>
|
|
<div class="stat__val" style="color:var(--text-strong)">{{ "%.1f"|format(player.win_pct) }}%</div>
|
|
<div class="stat__sub">{{ player.winning_sessions }}W · {{ player.losing_sessions }}L · {{ player.break_even_sessions }}E</div>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="kicker">ROI</span>
|
|
<div class="stat__val {{ 'num-pos' if player.roi_pct > 0 else 'num-neg' if player.roi_pct < 0 else 'num-muted' }}">{{ "%.1f"|format(player.roi_pct) }}%</div>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="kicker">Sessions</span>
|
|
<div class="stat__val" style="color:var(--text-strong)">{{ player.sessions_played }}</div>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="kicker">Current W/L</span>
|
|
<div class="stat-pair">
|
|
<span class="stat-pair__val" style="color:var(--pos)">{{ player.current_win_streak }}W</span>
|
|
<span class="stat-pair__val" style="color:var(--neg)">{{ player.current_loss_streak }}L</span>
|
|
</div>
|
|
<div class="stat__sub">Longest {{ player.longest_win_streak }}W · {{ player.longest_loss_streak }}L</div>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="kicker">Best / worst</span>
|
|
<div class="stat-pair">
|
|
<span class="stat-pair__val" style="color:var(--pos)">{{ player.biggest_win_cents | money }}</span>
|
|
<span class="stat-pair__val" style="color:var(--neg)">{{ player.biggest_loss_cents | money }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="kicker">Total invested</span>
|
|
<div class="stat__val" style="color:var(--num)">{{ player.total_invested_cents | money }}</div>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="kicker">Gross cashouts</span>
|
|
<div class="stat__val" style="color:var(--num)">{{ player.total_cash_out_cents | money }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cumulative profit chart -->
|
|
<div class="panel" style="margin-bottom:20px;">
|
|
<div class="panel__head">
|
|
<span class="kicker" style="margin:0;">Trend</span>
|
|
<h2 class="panel__title">Cumulative profit</h2>
|
|
</div>
|
|
<div class="chart-wrap">
|
|
<div class="chart-frame chart-frame--tall">
|
|
<canvas id="playerCumulativeChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Charts row: session results + result mix -->
|
|
<div class="split" style="margin-bottom:20px;">
|
|
<div class="panel col-main">
|
|
<div class="panel__head">
|
|
<span class="kicker" style="margin:0;">Session by session</span>
|
|
<h2 class="panel__title">Results</h2>
|
|
</div>
|
|
<div class="chart-wrap">
|
|
<div class="chart-frame">
|
|
<canvas id="playerSessionChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="panel col-side">
|
|
<div class="panel__head">
|
|
<span class="kicker" style="margin:0;">Breakdown</span>
|
|
<h2 class="panel__title">Result mix</h2>
|
|
</div>
|
|
<div class="chart-wrap">
|
|
<div class="result-mix" aria-label="Player result mix">
|
|
{% set result_total = player.winning_sessions + player.losing_sessions + player.break_even_sessions %}
|
|
<div class="result-mix__bar">
|
|
{% if player.winning_sessions > 0 %}
|
|
<span class="result-mix__seg result-mix__seg--win" style="flex-grow:{{ player.winning_sessions }}"></span>
|
|
{% endif %}
|
|
{% if player.break_even_sessions > 0 %}
|
|
<span class="result-mix__seg result-mix__seg--even" style="flex-grow:{{ player.break_even_sessions }}"></span>
|
|
{% endif %}
|
|
{% if player.losing_sessions > 0 %}
|
|
<span class="result-mix__seg result-mix__seg--loss" style="flex-grow:{{ player.losing_sessions }}"></span>
|
|
{% endif %}
|
|
</div>
|
|
<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><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><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><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 %}
|
|
<div class="result-mix__note">±$1.00 counts as even</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Perf vs Cash two-panel split -->
|
|
<div class="split" style="margin-bottom:20px;">
|
|
|
|
<!-- Poker performance -->
|
|
<div class="panel col-main">
|
|
<div class="panel__head">
|
|
<div>
|
|
<div class="section-marker">
|
|
<span class="section-marker__dot section-marker__dot--accent"></span>
|
|
Poker performance
|
|
</div>
|
|
<h2 class="panel__title">Game results</h2>
|
|
</div>
|
|
</div>
|
|
<table style="width:100%;border-collapse:collapse;">
|
|
<tbody>
|
|
<tr>
|
|
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Total Net</td>
|
|
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);font-variant-numeric:tabular-nums;border-top:1px solid var(--divider);"
|
|
class="{{ 'num-pos' if player.total_net_cents > 0 else 'num-neg' if player.total_net_cents < 0 else '' }}">
|
|
{{ '+' if player.total_net_cents > 0 else '' }}{{ player.total_net_cents | money }}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<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);"
|
|
class="{{ 'num-pos' if player.roi_pct > 0 else 'num-neg' if player.roi_pct < 0 else '' }}">{{ "%.1f"|format(player.roi_pct) }}%</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Average Win</td>
|
|
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--pos);">{{ player.avg_win_cents | money }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Average Loss</td>
|
|
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--neg);">{{ player.avg_loss_cents | money }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Best / Worst</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.biggest_win_cents | money }}</span>
|
|
<span style="color:var(--faintest);margin:0 4px;">/</span>
|
|
<span style="color:var(--neg)">{{ player.biggest_loss_cents | money }}</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Win / Loss Streak</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.current_win_streak }}W</span>
|
|
<span style="color:var(--faintest);margin:0 4px;">·</span>
|
|
<span style="color:var(--neg)">{{ player.current_loss_streak }}L</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Longest Win / Loss</td>
|
|
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--num);">
|
|
{{ player.longest_win_streak }}W · {{ player.longest_loss_streak }}L
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Poker inputs and settlement. Fronts/rollovers are part of poker basis,
|
|
while paid/collected rows are real cash movement. -->
|
|
<div class="panel col-side">
|
|
<div class="panel__head">
|
|
<div>
|
|
<div class="section-marker">
|
|
<span class="section-marker__dot section-marker__dot--rolled"></span>
|
|
Poker basis + settlement
|
|
</div>
|
|
<h2 class="panel__title">Investment and settlement</h2>
|
|
</div>
|
|
</div>
|
|
<table style="width:100%;border-collapse:collapse;">
|
|
<tbody>
|
|
<tr>
|
|
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Buy-ins paid</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_buy_in_cents | money }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding:11px 20px;color:var(--muted);font-size:13.5px;border-top:1px solid var(--divider);">Fronted</td>
|
|
<td style="padding:11px 20px;text-align:right;font-family:var(--font-mono);border-top:1px solid var(--divider);color:var(--warn);">{{ player.total_front_cents | money }}</td>
|
|
</tr>
|
|
<tr>
|
|
<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);">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);">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_debt_repayment_cents > 0 %}
|
|
<tr>
|
|
<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_due_to_player_cents > 0 %}
|
|
<tr>
|
|
<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.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_writeoff_cents | money }}</td>
|
|
</tr>
|
|
{% endif %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const playerChartData = {{ chart_data | tojson }};
|
|
const rootStyles = getComputedStyle(document.documentElement);
|
|
const textMuted = rootStyles.getPropertyValue('--text-muted').trim();
|
|
const lineColor = rootStyles.getPropertyValue('--line').trim();
|
|
|
|
const sharedOpts = {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: { position: 'bottom', labels: { color: textMuted, usePointStyle: true, boxWidth: 9, boxHeight: 9, padding: 16 } },
|
|
},
|
|
scales: {
|
|
x: { ticks: { color: textMuted, maxRotation: 0 }, grid: { color: lineColor } },
|
|
y: { ticks: { color: textMuted }, grid: { color: lineColor } },
|
|
},
|
|
};
|
|
|
|
const cumulativeCtx = document.getElementById('playerCumulativeChart');
|
|
if (cumulativeCtx) {
|
|
new Chart(cumulativeCtx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: playerChartData.labels,
|
|
datasets: [{
|
|
label: 'Cumulative Profit',
|
|
data: playerChartData.cumulative_values,
|
|
borderColor: playerChartData.color,
|
|
backgroundColor: playerChartData.color + '38',
|
|
borderWidth: 2.5,
|
|
pointRadius: 3,
|
|
pointHoverRadius: 5,
|
|
tension: 0.22,
|
|
fill: true,
|
|
}]
|
|
},
|
|
options: sharedOpts,
|
|
});
|
|
}
|
|
|
|
const sessionCtx = document.getElementById('playerSessionChart');
|
|
if (sessionCtx) {
|
|
new Chart(sessionCtx, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: playerChartData.labels,
|
|
datasets: [{
|
|
label: 'Session Net',
|
|
data: playerChartData.net_values,
|
|
backgroundColor: playerChartData.net_colors,
|
|
borderColor: playerChartData.net_colors,
|
|
borderRadius: 6,
|
|
borderSkipped: false,
|
|
}]
|
|
},
|
|
options: sharedOpts,
|
|
});
|
|
}
|
|
|
|
</script>
|
|
{% endblock %}
|