fix player win % and visual overhaul
This commit is contained in:
16 files changed
+3123
-1490
No files matched your search
@@ -9,19 +9,19 @@
|
||||
<h1 class="page-header__title">Leaderboard</h1>
|
||||
<p class="page-header__sub">{{ session_count }} session{{ 's' if session_count != 1 else '' }}{% if selected_session_date %} through {{ selected_session_label }}{% endif %}</p>
|
||||
</div>
|
||||
<div class="leaderboard-hero-stats">
|
||||
<div class="stat">
|
||||
<span class="kicker">Cash paid out</span>
|
||||
<div class="stat__val" style="color:var(--accent)">{{ cash_paid_out_cents | money }}</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="kicker">Sessions</span>
|
||||
<div class="stat__val" style="color:var(--text-strong)">{{ session_count }}{% if total_session_count != session_count %}<span style="color:var(--faintest);font-size:16px"> / {{ total_session_count }}</span>{% endif %}</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="kicker">Players</span>
|
||||
<div class="stat__val" style="color:var(--text-strong)">{{ all_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="leaderboard-hero-stats">
|
||||
<div class="stat">
|
||||
<span class="kicker">Cash paid out</span>
|
||||
<div class="stat__val" style="color:var(--accent)">{{ cash_paid_out_cents | money }}</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="kicker">Sessions</span>
|
||||
<div class="stat__val" style="color:var(--text-strong)">{{ session_count }}{% if total_session_count != session_count %}<span style="color:var(--faintest);font-size:16px"> / {{ total_session_count }}</span>{% endif %}</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="kicker">Players</span>
|
||||
<div class="stat__val" style="color:var(--text-strong)">{{ all_count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,10 +30,14 @@
|
||||
<div>
|
||||
<span class="kicker">Trend</span>
|
||||
<h2 class="panel__title">Cumulative profit</h2>
|
||||
<span class="panel__note" id="chartSeriesNote"></span>
|
||||
</div>
|
||||
<div style="display:flex;gap:6px;">
|
||||
<button class="btn btn--ghost btn--sm" type="button" id="collapseLeaderboardChart">Collapse</button>
|
||||
<button class="btn btn--ghost btn--sm" type="button" id="expandLeaderboardChart">Show all</button>
|
||||
</div>
|
||||
<button class="btn btn--ghost btn--sm" type="button" id="expandLeaderboardChart">Expand</button>
|
||||
</div>
|
||||
<div class="chart-wrap">
|
||||
<div class="chart-wrap" id="leaderboardChartWrap">
|
||||
<div class="chart-frame chart-frame--tall">
|
||||
<canvas id="leaderboardChart"></canvas>
|
||||
</div>
|
||||
@@ -53,15 +57,6 @@
|
||||
Recent <span class="seg__count">5 sess</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="toolbar__hint">
|
||||
{% if mode == 'eligible' %}
|
||||
{{ eligible_min_sessions }}+ sessions required. Keeps one-off guests from skewing standings.
|
||||
{% elif mode == 'all' %}
|
||||
Everyone who's sat down — rankings can swing on tiny samples.
|
||||
{% else %}
|
||||
Form guide — aggregated over the last 5 sessions only.
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="toolbar__right">
|
||||
{% if seasons %}
|
||||
@@ -90,6 +85,15 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p class="toolbar__hint">
|
||||
{% if mode == 'eligible' %}
|
||||
{{ eligible_min_sessions }}+ sessions required. Keeps one-off guests from skewing standings.
|
||||
{% elif mode == 'all' %}
|
||||
Everyone who's sat down — rankings can swing on tiny samples.
|
||||
{% else %}
|
||||
Form guide — aggregated over the last 5 sessions only.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<div class="panel tbl-scroll" style="margin-bottom:20px;">
|
||||
<table class="tbl" id="leaderboardTable" data-sort-key="net" data-sort-direction="desc">
|
||||
@@ -190,12 +194,17 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const CHART_PALETTE = ['#9b8cf0','#6fc093','#e0b15c','#cf6f86','#8f93c2','#7cb9e0','#f4a261','#a78bb0','#5cb8a0','#e58f3a'];
|
||||
const CHART_PALETTE = ['#9B8FE3','#35C6B8','#5AA2F5','#E0B85C','#58D18E','#F06F7A','#B0A6EF','#8FA4BB','#C99BE0','#6FB2C9'];
|
||||
const DEFAULT_SERIES = 6;
|
||||
const leaderboardChartData = {{ chart_data | tojson }};
|
||||
const rootStyles = getComputedStyle(document.documentElement);
|
||||
const textMuted = rootStyles.getPropertyValue('--text-muted').trim();
|
||||
const lineColor = rootStyles.getPropertyValue('--line').trim();
|
||||
const textMuted = rootStyles.getPropertyValue('--muted').trim() || '#9AA5B4';
|
||||
const gridColor = 'rgba(255,255,255,0.05)';
|
||||
const axisColor = 'rgba(255,255,255,0.10)';
|
||||
|
||||
// Order players by activity so the most relevant get the clearest colors,
|
||||
// then default the chart to the most active players to keep it readable.
|
||||
leaderboardChartData.datasets.sort((a, b) => (b.sessions_played || 0) - (a.sessions_played || 0));
|
||||
leaderboardChartData.datasets.forEach((ds, i) => {
|
||||
ds.borderColor = CHART_PALETTE[i % CHART_PALETTE.length];
|
||||
ds.backgroundColor = CHART_PALETTE[i % CHART_PALETTE.length];
|
||||
@@ -208,24 +217,31 @@ function buildChartConfig(data, compact) {
|
||||
labels: data.labels,
|
||||
datasets: data.datasets.map(ds => ({
|
||||
...ds,
|
||||
pointRadius: compact ? 0 : 2,
|
||||
pointHoverRadius: 5,
|
||||
borderWidth: compact ? 2 : 2.5,
|
||||
tension: 0.28,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4,
|
||||
borderWidth: 2,
|
||||
tension: 0.35,
|
||||
spanGaps: true,
|
||||
fill: false,
|
||||
})),
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: { mode: 'nearest', intersect: false },
|
||||
interaction: { mode: 'nearest', axis: 'x', intersect: false },
|
||||
elements: { line: { capBezierPoints: true }, point: { hitRadius: 8 } },
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'bottom',
|
||||
labels: { color: textMuted, usePointStyle: true, boxWidth: 8, boxHeight: 8, padding: 14 },
|
||||
labels: { color: textMuted, usePointStyle: true, pointStyle: 'line', boxWidth: 18, boxHeight: 2, padding: 16, font: { size: 12 } },
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'nearest', axis: 'x', intersect: false,
|
||||
backgroundColor: 'rgba(13,17,22,0.95)',
|
||||
borderColor: 'rgba(255,255,255,0.12)', borderWidth: 1,
|
||||
padding: 10, cornerRadius: 6, usePointStyle: true,
|
||||
titleColor: '#F2F0ED', bodyColor: '#C7CED8',
|
||||
callbacks: {
|
||||
label(ctx) {
|
||||
const v = ctx.parsed.y;
|
||||
@@ -236,16 +252,19 @@ function buildChartConfig(data, compact) {
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: { color: textMuted, maxRotation: 0, autoSkip: true, maxTicksLimit: compact ? 6 : 10 },
|
||||
grid: { color: lineColor, display: !compact },
|
||||
ticks: { color: textMuted, maxRotation: 0, autoSkip: true, maxTicksLimit: compact ? 6 : 10, font: { size: 11 } },
|
||||
grid: { display: false },
|
||||
border: { color: axisColor },
|
||||
},
|
||||
y: {
|
||||
ticks: {
|
||||
color: textMuted,
|
||||
maxTicksLimit: compact ? 8 : 10,
|
||||
maxTicksLimit: compact ? 6 : 9,
|
||||
font: { size: 11 },
|
||||
callback(v) { return (v < 0 ? '-' : '') + '$' + Math.abs(Number(v)).toFixed(0); }
|
||||
},
|
||||
grid: { color: lineColor },
|
||||
grid: { color: gridColor, drawTicks: false },
|
||||
border: { display: false },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,12 +274,20 @@ function buildChartConfig(data, compact) {
|
||||
const ctx = document.getElementById('leaderboardChart');
|
||||
let mainChart = null;
|
||||
if (ctx && leaderboardChartData.labels.length > 0) {
|
||||
const filtered = leaderboardChartData.datasets.filter(ds => (ds.sessions_played || 0) > 1);
|
||||
const chartData = {
|
||||
labels: leaderboardChartData.labels,
|
||||
datasets: filtered.length ? filtered : leaderboardChartData.datasets,
|
||||
datasets: leaderboardChartData.datasets.slice(0, DEFAULT_SERIES),
|
||||
};
|
||||
mainChart = new Chart(ctx, buildChartConfig(chartData, true));
|
||||
|
||||
const note = document.getElementById('chartSeriesNote');
|
||||
if (note) {
|
||||
const total = leaderboardChartData.datasets.length;
|
||||
const shown = chartData.datasets.length;
|
||||
note.textContent = total > shown
|
||||
? `Showing ${shown} most active players of ${total} — use Show all`
|
||||
: `${total} player${total === 1 ? '' : 's'}`;
|
||||
}
|
||||
}
|
||||
|
||||
const modal = document.getElementById('chartModal');
|
||||
@@ -286,6 +313,39 @@ document.querySelectorAll('[data-close-modal]').forEach(el => {
|
||||
});
|
||||
});
|
||||
|
||||
(function () {
|
||||
const wrap = document.getElementById('leaderboardChartWrap');
|
||||
const collapseBtn = document.getElementById('collapseLeaderboardChart');
|
||||
const STORAGE_KEY = 'leaderboard_chart_collapsed';
|
||||
let collapsed = localStorage.getItem(STORAGE_KEY) === '1';
|
||||
|
||||
function apply(animate) {
|
||||
if (collapsed) {
|
||||
if (animate) {
|
||||
wrap.style.transition = 'max-height .25s ease, opacity .2s ease';
|
||||
}
|
||||
wrap.style.maxHeight = '0';
|
||||
wrap.style.overflow = 'hidden';
|
||||
wrap.style.opacity = '0';
|
||||
collapseBtn.textContent = 'Expand chart';
|
||||
} else {
|
||||
wrap.style.transition = animate ? 'max-height .3s ease, opacity .25s ease' : '';
|
||||
wrap.style.maxHeight = '600px';
|
||||
wrap.style.overflow = '';
|
||||
wrap.style.opacity = '1';
|
||||
collapseBtn.textContent = 'Collapse';
|
||||
}
|
||||
}
|
||||
|
||||
apply(false);
|
||||
|
||||
collapseBtn.addEventListener('click', () => {
|
||||
collapsed = !collapsed;
|
||||
localStorage.setItem(STORAGE_KEY, collapsed ? '1' : '0');
|
||||
apply(true);
|
||||
});
|
||||
})();
|
||||
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'Escape' && modal.style.display === 'flex') {
|
||||
modal.style.display = 'none';
|
||||
|
||||
Reference in new issue
Block a user