Add referee assignment/eligibility system, team bulk-add, member Groups, and referee management dashboard with PDF export

Builds the referee workflow end to end: club-defined RefereeLevel/RefereeProfile
eligibility tied to teams, EventReferee assignment (member or external, with
fee/km payment tracking), an admin dashboard with KPI tiles, date-grouped game
tiles and range filters, and a downloadable payment form PDF modeled on the
club's existing paper document (using Club.legal_name when set). Also lands
team roster bulk-add, member mass-upload with family linking, and the
members.Group model, developed alongside this work.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 21:39:19 +02:00
parent 86e28c317f
commit 309bd4d83e
54 changed files with 4574 additions and 88 deletions

View File

@@ -14,7 +14,7 @@ from .services.admins import find_member_by_email
class ClubForm(forms.ModelForm):
class Meta:
model = Club
fields = ["name", "slug", "sport_type", "logo", "primary_color", "secondary_color", "season_start", "season_duration_months"]
fields = ["name", "legal_name", "slug", "sport_type", "logo", "primary_color", "secondary_color", "season_start", "season_duration_months"]
help_texts = {"slug": _("Drives the club's subdomain. Left blank, it is derived from the name.")}
# Deliberately a text input, not <input type="color">: a colour picker cannot
# express "no colour" -- it would submit #000000 for every club that never

View File

@@ -27,7 +27,7 @@
{% block heading %}{{ club.name }}{% endblock heading %}
{% block subheading %}
{{ club.slug }}.rosterchief.app &middot; {{ club.get_sport_type_display }}
{{ club.slug }}.rosterchief.app &middot; {{ club.get_sport_type_display }}{% if club.legal_name %} &middot; {{ club.legal_name }}{% endif %}
{% if club.is_archived %}
<span class="badge badge-warning badge-sm ml-2">Archived</span>
{% endif %}

View File

@@ -15,8 +15,9 @@
</div>
{% endfor %}
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
{% form_field form.name %}
{% form_field form.legal_name %}
{% form_field form.slug %}
{% form_field form.sport_type %}
</div>

View File

@@ -1,6 +1,6 @@
{% load i18n %}
<div class="w-full max-w-6xl {% if size_modifier %}lg:w-fit{% endif %}">
<div class="w-full max-w-6xl {% if field_type == "checkbox" %}h-full flex flex-col justify-center{% endif %} {% if size_modifier %}lg:w-fit{% endif %}">
<div class="flex flex-col gap-y-2">
{% if show_label %}
<div class="flex flex-row items-end min-h-6">