Make the referee dashboard's season stats one compact row, not two

KPIs and the chart now share a single flex row (chart card wider, flex-[3])
instead of a full KPI row followed by a tall standalone chart card -- the
per-referee fee breakdown moves into the chart's own tooltip instead of a
separate list underneath, so this stays compact and the actual
games-needing-a-referee list isn't pushed below the fold.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
2026-08-22 17:32:03 +02:00
parent 8ddae5e63b
commit 6efb00350b
3 changed files with 44 additions and 36 deletions

View File

@@ -50,43 +50,36 @@
Season workload -- separate from the "games in view" KPIs above
(which track the upcoming range picked by the filter strip), this is
about who's actually done the refereeing this season, all-time
within it, not just what's still upcoming.
within it, not just what's still upcoming. Deliberately one compact
row, not a tall section of its own -- the games-needing-a-referee
list below is the actual point of this page, so this shouldn't push
it below the fold. Fees ride along as a chart tooltip (see
extra_body) rather than a separate list, for the same reason.
{% endcomment %}
<div class="flex flex-col gap-3.5">
<div class="flex flex-wrap gap-3.5">
<div class="card min-w-0 flex-1 p-4">
<div class="font-display text-xs font-bold tracking-[.12em] text-muted uppercase">{% trans "Active referees" %}</div>
<div class="mt-1 font-display text-[34px] leading-none font-extrabold text-ink tabular-nums">{{ kpi_active_referees }}</div>
<div class="mt-1 text-[13px] text-muted">{% trans "This season" %}</div>
</div>
<div class="card min-w-0 flex-1 p-4">
<div class="font-display text-xs font-bold tracking-[.12em] text-muted uppercase">{% trans "Games refereed" %}</div>
<div class="mt-1 font-display text-[34px] leading-none font-extrabold text-ink tabular-nums">{{ kpi_total_assignments }}</div>
<div class="mt-1 text-[13px] text-muted">{% trans "This season" %}</div>
</div>
<div class="card min-w-0 flex-1 p-4">
<div class="font-display text-xs font-bold tracking-[.12em] text-muted uppercase">{% trans "Average per referee" %}</div>
<div class="mt-1 font-display text-[34px] leading-none font-extrabold text-ink tabular-nums">{{ kpi_avg_games_per_referee }}</div>
<div class="mt-1 text-[13px] text-muted">{% trans "This season" %}</div>
</div>
<div class="flex flex-wrap gap-3.5">
<div class="card min-w-0 flex-1 p-4">
<div class="font-display text-xs font-bold tracking-[.12em] text-muted uppercase">{% trans "Active referees" %}</div>
<div class="mt-1 font-display text-[34px] leading-none font-extrabold text-ink tabular-nums">{{ kpi_active_referees }}</div>
<div class="mt-1 text-[13px] text-muted">{% trans "This season" %}</div>
</div>
<div class="card p-4.5">
<div class="mb-1 font-display text-base font-extrabold tracking-[.08em] text-ink uppercase">{% trans "Games refereed per referee" %}</div>
<p class="mb-3 text-sm text-muted">{% trans "This season." %}</p>
<div class="card min-w-0 flex-1 p-4">
<div class="font-display text-xs font-bold tracking-[.12em] text-muted uppercase">{% trans "Games refereed" %}</div>
<div class="mt-1 font-display text-[34px] leading-none font-extrabold text-ink tabular-nums">{{ kpi_total_assignments }}</div>
<div class="mt-1 text-[13px] text-muted">{% trans "This season" %}</div>
</div>
<div class="card min-w-0 flex-1 p-4">
<div class="font-display text-xs font-bold tracking-[.12em] text-muted uppercase">{% trans "Average per referee" %}</div>
<div class="mt-1 font-display text-[34px] leading-none font-extrabold text-ink tabular-nums">{{ kpi_avg_games_per_referee }}</div>
<div class="mt-1 text-[13px] text-muted">{% trans "This season" %}</div>
</div>
<div class="card min-w-0 flex-[3] p-4">
<div class="font-display text-xs font-bold tracking-[.12em] text-muted uppercase">{% trans "Games refereed per referee" %}</div>
{% if referee_stats %}
<div class="h-56">
<div class="mt-1 h-20">
<canvas id="referee-games-chart"></canvas>
</div>
<div class="mt-3 max-h-40 overflow-y-auto border-t border-rule">
{% for row in referee_stats %}
<div class="flex items-center justify-between gap-3 py-1.5 {% if not forloop.last %}border-b border-rule{% endif %}">
<span class="truncate text-[13px] text-ink">{{ row.member__first_name }} {{ row.member__last_name }}</span>
<span class="shrink-0 font-mono text-xs text-muted">{% blocktrans count counter=row.games %}{{ counter }} game{% plural %}{{ counter }} games{% endblocktrans %}{% if row.total_fees %} &middot; &euro;{{ row.total_fees|floatformat:2 }}{% endif %}</span>
</div>
{% endfor %}
</div>
{% else %}
<p class="py-6 text-center text-sm text-muted">{% trans "No referee assignments recorded yet this season." %}</p>
<p class="mt-1 text-[13px] text-muted">{% trans "No referee assignments recorded yet this season." %}</p>
{% endif %}
</div>
</div>
@@ -175,10 +168,21 @@
responsive: true,
maintainAspectRatio: false,
indexAxis: "y",
plugins: {legend: {display: false}},
plugins: {
legend: {display: false},
tooltip: {
callbacks: {
label: (context) => {
const fee = data.referee_games.fees[context.dataIndex];
const label = `{{ games_label|escapejs }}: ${context.parsed.x}`;
return fee ? `${label} — €${fee.toFixed(2)}` : label;
},
},
},
},
scales: {
x: {beginAtZero: true, ticks: {color: ink, precision: 0}, grid: {color: grid}},
y: {ticks: {color: ink}, grid: {display: false}},
y: {ticks: {color: ink, font: {size: 11}}, grid: {display: false}},
},
},
});

View File

@@ -2791,12 +2791,16 @@ class RefereeManagementDashboardView(MemberAdminRequiredMixin, TemplateView):
kpi_active_referees = len(stats)
kpi_total_assignments = sum(row["games"] for row in stats)
kpi_avg_games_per_referee = round(kpi_total_assignments / kpi_active_referees, 1) if kpi_active_referees else 0
# Top 15 for the chart's own readability -- the table below it lists
# every referee, so nothing is actually hidden, just not plotted.
# Top 15 for the chart's own readability -- fees ride along per bar
# for the tooltip (see referee_management.html's extra_body) rather
# than a separate list underneath, to keep this a compact single row
# next to the KPIs, not a second tall section pushing the actual
# games-needing-a-referee list off screen.
charts = {
"referee_games": {
"labels": [f"{row['member__first_name']} {row['member__last_name']}" for row in stats[:15]],
"games": [row["games"] for row in stats[:15]],
"fees": [float(row["total_fees"] or 0) for row in stats[:15]],
}
}

File diff suppressed because one or more lines are too long