Referee PDF: the info-card background used CSS color-mix(), which WeasyPrint
doesn't support -- the rule was silently dropped, leaving the card with no
background at all. Computed in Python instead (management/pdf.py) and baked
into the template as a plain hex value; the tint is based on the club's
primary_color, falling back to secondary_color when primary is itself (near)
black or white, where a straight tint would be invisible or too harsh.
Event forms: the location picker now shows "Name — City" (plus the country
when it isn't Belgium) and is searchable by name or city, reusing the
existing single-select searchable-select.js widget.
Games API: GameOut now carries `end` (explicit, or start + 2h when a GAME was
saved without one -- Event.save() sets this, never overwriting an explicit
end; other event kinds are untouched). /games/upcoming/ now includes
anything not yet finished rather than only things that haven't started, so a
game already in progress keeps showing up until its window closes; `status`
was adjusted to match so a game returned there never calls itself
"finished".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Referee display: drop the redundant "External" pill from the PDF form, round
the event/dashboard "due" summary to 2 decimals (a fine-grained km rate like
0.083 was pushing the raw total to 3+ decimals), and theme the PDF's accent
colors off the club's own primary/secondary colors instead of a hardcoded
default.
News: title/body stay the club's own-language (Dutch) text; new optional
title_en/body_en carry a translation, with News.effective_title_en/
effective_body_en resolving the fallback to the original on read rather than
copying it in at save time -- so editing the Dutch text later never leaves a
stale English copy behind, and existing rows get correct fallback behaviour
with no backfill. The news form lays both languages out side by side; the
detail page only shows an English section when one was actually added. The
public API returns both languages in one call (title_nl/body_nl/excerpt_nl
alongside title_en/body_en/excerpt_en, the latter never blank) -- a breaking
rename of the previously-unprefixed title/body/excerpt fields.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
A large batch of club-management features built up over one session:
- Club dashboard banner warning admins 1 month before billing ends
- Full Events/EventSeries CRUD (recurrence builder, occurrence lifecycle,
per-team permissions), with match->game rename and game-specific fields
(score, competition, live status, external game ID)
- Django-admin competition dropdown, gated per-club by feature flag
- Auto-import of RBIHF fixtures (scrape -> diff -> preview -> confirm),
with location/opponent dropdowns suggested from existing club data
- Feature-flag-gated Shop/Forms nav sections, reusing the same flag
machinery for the RBIHF import button
- Team roster now scoped to members active this season or next, sorted and
grouped by position
- Club sport type (ice hockey / other), shown in the control panel's club
subtitle
- Per-season team photo upload from the team page
- New public read-only API (Django Ninja) at /api/v1/: news, team rosters,
upcoming/live/per-team games, and sponsors -- auto-documented via Swagger
UI, CORS-enabled for a club's own external website
- Club sponsors: admin-only CRUD (logo, URL, active date window) plus a
date-windowed, optionally randomized API endpoint
- Assorted fixes: NullBooleanField dropdown rendering, cross-club event
validation timing, searchable-select chip placement, btn-neutral ->
default button style sweep, calendar-month chart windows
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1gj3J1QPfP38XWpnpbFpy
Team managers/coaches now only see their own teams, can't create teams,
and can view (but not edit) positions -- admins keep full rights.
Locations and Opponents move from read-only stubs to full CRUD, gated
to admins and management-position staff, with a country dropdown
(django-countries) instead of free text. Also: the team list shows
player/staff counts, and deleting a news item's main photo promotes
another one instead of leaving the item without one.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1gj3J1QPfP38XWpnpbFpy
team/season aren't TeamMembershipForm fields (the view sets them from
the URL, not user input), so Django's own validate_unique() silently
excludes both of them -- and with them, the whole
unique_jersey_number_per_team_per_season constraint. A clashing jersey
number reached the database unrejected and came back as a raw
IntegrityError. form.clean() now checks it by hand, same as
PositionForm already does for its own check constraint; the four
roster/staff save paths also get a try/except IntegrityError backstop
so any future gap degrades to an error banner instead of a 500.
One "Teams" nav entry now, matching how Family has none of its own and
is only reached through Members -- Roster/Staff were pure club-wide
list stubs with no way to add/remove anyone except Django admin.
Opening a team shows its roster and staff for whichever season is
selected (?season=, defaulting to the current one, same pattern as the
Memberships page); adding/editing/removing either is gated to that
team's own manager or a club admin (TeamManagerRequiredMixin, defined
earlier but never actually wired up until now).
Extracts the "Grant role" member picker's typeahead combobox into a
reusable static/js/searchable-select.js (opt in via data-searchable on
the widget), and reuses it for the news form's teams field -- a proper
multiselect with removable pills and filter-as-you-type, replacing the
plain checkbox list. Needed forwarding widget attrs through the shared
select template, which never passed them to the rendered <select>.
News and NewsPhoto (team-tagged instead of categorised, one photo taggable
as main via a partial unique constraint), gated per club/services/access.py:
any current-season coach_manager, EDITOR, or ADMIN can draft and edit a news
item; only EDITOR/ADMIN can publish it, or edit it once it's live. Publishing
takes a date so it can be scheduled ahead of time rather than only right now.
Authoring/release only for now -- no member-facing reading page or public API
yet, the visibility field (internal/external/both) is there for when those land.
Positions had list/create/edit already stubbed as list-only; give it real
forms, with a check mirroring the management_position_implies_staff_position
constraint so a bad combination reads as a form error, not a 500. Roles now
groups by role (excluding the MEMBER everyone holds automatically, which was
just noise), grants via a modal instead of a separate page, and its member
picker is a small typeahead combobox instead of a long native <select>.
Gives clubs a self-service /manage/ area for members, families, teams,
roles, and season memberships, alongside real fee-payment tracking
(FeePayment, record_payment/mark_as_paid/remaining_balance) so a
membership's paid status reflects actual money received instead of a
single manually-set flag.