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 Season workload -- separate from the "games in view" KPIs above
(which track the upcoming range picked by the filter strip), this is (which track the upcoming range picked by the filter strip), this is
about who's actually done the refereeing this season, all-time 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 %} {% endcomment %}
<div class="flex flex-col gap-3.5"> <div class="flex flex-wrap gap-3.5">
<div class="flex flex-wrap gap-3.5"> <div class="card min-w-0 flex-1 p-4">
<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="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 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 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> </div>
<div class="card p-4.5"> <div class="card min-w-0 flex-1 p-4">
<div class="mb-1 font-display text-base font-extrabold tracking-[.08em] text-ink uppercase">{% trans "Games refereed per referee" %}</div> <div class="font-display text-xs font-bold tracking-[.12em] text-muted uppercase">{% trans "Games refereed" %}</div>
<p class="mb-3 text-sm text-muted">{% trans "This season." %}</p> <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 %} {% if referee_stats %}
<div class="h-56"> <div class="mt-1 h-20">
<canvas id="referee-games-chart"></canvas> <canvas id="referee-games-chart"></canvas>
</div> </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 %} {% 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 %} {% endif %}
</div> </div>
</div> </div>
@@ -175,10 +168,21 @@
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
indexAxis: "y", 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: { scales: {
x: {beginAtZero: true, ticks: {color: ink, precision: 0}, grid: {color: grid}}, 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_active_referees = len(stats)
kpi_total_assignments = sum(row["games"] for row in 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 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 # Top 15 for the chart's own readability -- fees ride along per bar
# every referee, so nothing is actually hidden, just not plotted. # 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 = { charts = {
"referee_games": { "referee_games": {
"labels": [f"{row['member__first_name']} {row['member__last_name']}" for row in stats[:15]], "labels": [f"{row['member__first_name']} {row['member__last_name']}" for row in stats[:15]],
"games": [row["games"] 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