{% extends "controlpanel/base.html" %} {% load static lucide %} {% block heading %}{{ club.name }}{% endblock heading %} {% block subheading %}

{{ club.slug }} {% if club.is_archived %} Archived {% endif %}

{% endblock subheading %} {% block actions %} {% lucide "pencil" size=16 %} Edit {% if club.is_archived %}
{% csrf_token %}
{% else %}
{% csrf_token %}
{% endif %} {% endblock actions %} {% block panel %} {% if club.is_archived %}
This club is archived: its subdomain no longer resolves. Nothing has been deleted — restore it to bring it back.
{% endif %} {% if attention.no_season %}
{% lucide "calendar-x" size=20 %} No season covers today, so this club cannot take a signup or schedule a match. Nothing errors — it is simply inert.
{% endif %} {% comment %} The club's own numbers that should be zero. Teams without a manager is a defect in the club's setup, not a statistic: with nobody in a management position the access service grants no authority over that team, so nobody can pick the squad. {% endcomment %}
{% lucide "banknote" size=16 %} Outstanding
€{{ attention.outstanding|floatformat:2 }}
{{ attention.unpaid_members }} member{{ attention.unpaid_members|pluralize }} unpaid this season
{% lucide "user-x" size=16 %} No coach
{{ attention.teams_without_manager }}
Teams nobody can pick a squad for
{% lucide "user-minus" size=16 %} Unrostered
{{ attention.unrostered }}
Active members on no team
{% lucide "clock" size=16 %} Pending
{{ attention.pending_approvals }}
Memberships awaiting approval

{% lucide "sparkles" size=18 %} New members

{{ attention.new_members }}
{# First season at this club — someone returning after a year away is a renewal. #}

first season at this club

{% lucide "repeat" size=18 %} Renewal

{% if attention.renewal_rate is None %} {# No prior season to compare against: a first-season club has not failed to renew anyone. #}

No previous season to compare against yet.

{% else %}
{{ attention.renewal_rate }}%

of last season's active members signed up again

{% endif %}

{% lucide "user-check" size=18 %} Attendance

{% if attention.attendance.turnout is None %}

No past events with responses this season.

{% else %}
{{ attention.attendance.turnout }}%

turnout of those who answered

{# The leading indicator: it measures whether members use the app at all. #} {{ attention.attendance.no_response }}% never responded

{% endif %}

{% lucide "hourglass" size=18 %} Unpaid, by age

{% for bucket in attention.aging %} {% endfor %}
{{ bucket.label }} €{{ bucket.total|floatformat:2 }} {{ bucket.count }} order{{ bucket.count|pluralize }}

{% lucide "user-plus" size=18 %} Signups per month

New members against returning ones.

{% lucide "wallet" size=18 %} Fee status this season

{% for group in groups %}

{% lucide group.icon size=18 %} {{ group.title }}

{% for label, value in group.stats %}
{{ label }}
{{ value }}
{% endfor %}
{% endfor %}

{% lucide "toggle-right" size=18 %} Features

Manage features
{% for entry in flags %} {% empty %} {% endfor %}
{{ entry.flag.name }} {{ entry.flag.note|default:"—" }} {% if entry.overridden %} {# `everyone` overrides club targeting, so a per-club toggle would be a lie. #} {% if entry.flag.everyone %}On for all clubs{% else %}Off everywhere{% endif %} {% else %}
{% csrf_token %}
{% endif %}
No features defined yet.
{% if not subscription %}

This club is not billed for anything. Put it on a tier to start.

{% else %}

On {{ subscription.tier.name }}. {% if subscription.auto_renew %} Renews automatically 30 days before the period ends. {% else %} Auto-renew off — you must open each period by hand, or this club uses the platform for free. {% endif %} {% if subscription.auto_archive %} Archived automatically when a period goes unpaid past its grace period. {% else %} Auto-archive off — it will never be archived for non-payment. {% endif %}

{% for due in dues %} {% for payment in due.payments.all %} {% endfor %} {% empty %} {% endfor %}
Period Billed Paid Status
{{ due.period_start|date:"j M Y" }} — {{ due.period_end|date:"j M Y" }}
{{ due.tier.name }} · {{ due.invoice.number }} · grace to {{ due.grace_until|date:"j M Y" }}
€{{ due.amount|floatformat:2 }} €{{ due.amount_paid|floatformat:2 }} {% if due.status == "paid" %} {% lucide "check" size=12 %} Paid {% elif due.status == "waived" %} Waived {% elif due.grace_until < today %} {% lucide "triangle-alert" size=12 %} Overdue {% elif due.period_end < today %} {% lucide "hourglass" size=12 %} In grace {% else %} {{ due.get_status_display }} {% endif %} {% if due.is_owing %} {% lucide "banknote" size=14 %} Pay {% if not due.payments.all %}
{% csrf_token %}
{% endif %} {% endif %} {% lucide "file-text" size=14 %} Invoice
{% lucide "corner-down-right" size=12 %} {{ payment.paid_at|date:"j M Y" }} · {{ payment.get_method_display }}{% if payment.reference %} · {{ payment.reference }}{% endif %} €{{ payment.amount|floatformat:2 }}
No periods billed yet.
{% endif %}
{% for role in admins %} {% empty %} {% endfor %}
Name Email
{{ role.member }} {{ role.member.user.email|default:"—" }}
{% csrf_token %}
No admins yet.
{% endblock panel %} {% block extra_body %} {{ charts|json_script:"chart-data" }} {% endblock extra_body %}