Show club health on the clubs list too
The list page carried the same vanity counts the dashboard just lost. It now shows active members, unpaid members, money owed, teams (flagging those nobody can pick a squad for), upcoming events and admins, with the same No season / Dormant badges -- and the annotations survive search and the archived filter. The table lives in one partial, included by both pages, so they cannot drift apart. Archived clubs are badged "Archived" rather than "Dormant": their subdomain does not resolve, so of course nothing is scheduled, and flagging that as a problem would be noise on the one page where every row has it. assertNumQueries(1) covers the list as well now -- health is annotated per club, so the page must not fan out as clubs are added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -24,39 +24,11 @@
|
|||||||
</form>
|
</form>
|
||||||
<div class="card bg-base-100 shadow">
|
<div class="card bg-base-100 shadow">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="overflow-x-auto">
|
{% if show_archived %}
|
||||||
<table class="table">
|
{% include "controlpanel/_club_health_table.html" with empty_message="No archived clubs." %}
|
||||||
<thead>
|
{% else %}
|
||||||
<tr>
|
{% include "controlpanel/_club_health_table.html" %}
|
||||||
<th>Club</th>
|
{% endif %}
|
||||||
<th>Members</th>
|
|
||||||
<th>Teams</th>
|
|
||||||
<th>Events</th>
|
|
||||||
<th>Admins</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for club in clubs %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a class="link link-hover font-medium" href="{% url 'controlpanel:club_detail' club.pk %}">{{ club.name }}</a>
|
|
||||||
<div class="text-xs opacity-60">{{ club.slug }}</div>
|
|
||||||
</td>
|
|
||||||
<td>{{ club.member_count }}</td>
|
|
||||||
<td>{{ club.team_count }}</td>
|
|
||||||
<td>{{ club.event_count }}</td>
|
|
||||||
<td>{{ club.admin_count }}</td>
|
|
||||||
</tr>
|
|
||||||
{% empty %}
|
|
||||||
<tr>
|
|
||||||
<td colspan="5" class="text-center opacity-60">
|
|
||||||
{% if show_archived %}No archived clubs.{% else %}No clubs yet.{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock panel %}
|
{% endblock panel %}
|
||||||
|
|||||||
@@ -143,55 +143,7 @@
|
|||||||
<div class="card bg-base-100 shadow">
|
<div class="card bg-base-100 shadow">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h2 class="card-title">Clubs</h2>
|
<h2 class="card-title">Clubs</h2>
|
||||||
{% comment %}
|
{% include "controlpanel/_club_health_table.html" %}
|
||||||
Health, not vanity: a club's member count says nothing you can act on, while
|
|
||||||
"no coach", "nothing scheduled" and "€ owed" each name a thing somebody has to
|
|
||||||
go and fix. Every column here is annotated in the same single query.
|
|
||||||
{% endcomment %}
|
|
||||||
<div class="overflow-x-auto">
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Club</th>
|
|
||||||
<th class="text-right">Members</th>
|
|
||||||
<th class="text-right">Unpaid</th>
|
|
||||||
<th class="text-right">Owed</th>
|
|
||||||
<th class="text-right">Teams</th>
|
|
||||||
<th class="text-right">Upcoming</th>
|
|
||||||
<th class="text-right">Admins</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for club in clubs %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a class="link link-hover font-medium" href="{% url 'controlpanel:club_detail' club.pk %}">{{ club.name }}</a>
|
|
||||||
<div class="mt-1 flex flex-wrap items-center gap-1">
|
|
||||||
<span class="text-xs opacity-60">{{ club.slug }}</span>
|
|
||||||
{% if not club.has_season %}<span class="badge badge-warning badge-xs gap-1">{% lucide "calendar-x" size=10 %} No season</span>{% endif %}
|
|
||||||
{% if not club.upcoming_events %}<span class="badge badge-ghost badge-xs gap-1">{% lucide "moon-star" size=10 %} Dormant</span>{% endif %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="text-right tabular-nums">{{ club.active_members }}</td>
|
|
||||||
<td class="text-right tabular-nums {% if club.unpaid_members %}text-warning{% endif %}">{{ club.unpaid_members }}</td>
|
|
||||||
<td class="text-right tabular-nums {% if club.outstanding %}font-semibold text-error{% endif %}">€{{ club.outstanding|floatformat:2 }}</td>
|
|
||||||
<td class="text-right tabular-nums">
|
|
||||||
{{ club.team_count }}
|
|
||||||
{% if club.teams_without_coach %}
|
|
||||||
<span class="badge badge-error badge-xs ml-1" title="Teams with nobody able to pick the squad">{{ club.teams_without_coach }} no coach</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="text-right tabular-nums">{{ club.upcoming_events }}</td>
|
|
||||||
<td class="text-right tabular-nums {% if not club.admin_count %}text-error{% endif %}">{{ club.admin_count }}</td>
|
|
||||||
</tr>
|
|
||||||
{% empty %}
|
|
||||||
<tr>
|
|
||||||
<td colspan="7" class="text-center opacity-60">No clubs yet.</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock panel %}
|
{% endblock panel %}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from .services.platform_admins import (
|
|||||||
revoke_platform_access,
|
revoke_platform_access,
|
||||||
set_platform_access,
|
set_platform_access,
|
||||||
)
|
)
|
||||||
from .services.statistics import club_attention, club_charts, club_statistics, clubs_with_health, clubs_with_totals, flag_adoption, onboarding_funnel, platform_attention, platform_charts, platform_totals
|
from .services.statistics import club_attention, club_charts, club_statistics, clubs_with_health, flag_adoption, onboarding_funnel, platform_attention, platform_charts, platform_totals
|
||||||
|
|
||||||
Flag = get_waffle_flag_model()
|
Flag = get_waffle_flag_model()
|
||||||
Switch = get_waffle_switch_model()
|
Switch = get_waffle_switch_model()
|
||||||
@@ -52,7 +52,7 @@ class ClubListView(PlatformStaffRequiredMixin, ListView):
|
|||||||
search = self.request.GET.get("q", "").strip()
|
search = self.request.GET.get("q", "").strip()
|
||||||
if search:
|
if search:
|
||||||
clubs = clubs.filter(name__icontains=search)
|
clubs = clubs.filter(name__icontains=search)
|
||||||
return clubs_with_totals(clubs)
|
return clubs_with_health(clubs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
return super().get_context_data(nav="clubs", show_archived=self.show_archived, search=self.request.GET.get("q", ""), **kwargs)
|
return super().get_context_data(nav="clubs", show_archived=self.show_archived, search=self.request.GET.get("q", ""), **kwargs)
|
||||||
|
|||||||
Reference in New Issue
Block a user