Add a Competitions card to the control panel's Features page

Competition rows (events.services.competitions' per-club data-source
gate) could previously only be managed through the Django admin.
Adds a card alongside Flags/Switches with create/edit/delete, mirroring
the Flags card's own modal pattern -- no cascading effects to weigh on
delete since Event.competition matches by name, not a foreign key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
2026-08-20 21:40:13 +02:00
parent 3159064e2a
commit 5889b3740e
6 changed files with 269 additions and 4 deletions

View File

@@ -6,7 +6,7 @@ from waffle import get_waffle_flag_model
from billing.models import DuePayment, Plan, PlanPrice, Subscription
from club.models import Club
from events.models import Location
from events.models import Competition, Location
from .services.admins import find_member_by_email
@@ -82,6 +82,24 @@ class FlagForm(forms.ModelForm):
}
class CompetitionForm(forms.ModelForm):
"""Metadata for events.services.competitions.fetch_game_info's per-club gate --
`module` is a dotted import path to a class named `name` that implements
`update_game_information(event=...)`; there is no such class for a new
competition until one is actually written, but that's fine here, same as
editing this by hand in the Django admin today: fetch_game_info already
catches the resulting ImportError/AttributeError and treats it as "nothing to
fetch from" rather than a 500, so this form doesn't need to validate the path
against real code to be safe to use."""
class Meta:
model = Competition
fields = ["name", "module", "sport_type", "flag"]
help_texts = {
"module": _("Dotted path to the Python module implementing this competition's data source, e.g. events.services.competitions.cehl."),
}
class PlanForm(forms.ModelForm):
"""Field order is chosen for the two-column modal (see _form_fields.html): description
spans both columns, so pairing name with duration and the two day-counts with each other