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>
Club staff author body as Markdown in the control panel (help_text
now explains the syntax); the public API renders it to sanitized
HTML on the way out via news/services.py -- markdown for the
conversion, nh3 (Rust/ammonia) to strip anything staff's raw
Markdown source might smuggle through (script tags, event handler
attributes, javascript: URLs) before it reaches someone else's
public website. The control panel's own preview is untouched and
still shows the raw source.
Excerpt is now derived from the rendered HTML's plain text rather
than the raw Markdown source, so syntax like ** or [text](url)
doesn't leak into what's meant to be a short teaser.
- news: NewsItemOut gains `excerpt` (truncated body); GET /news/{slug}/
fetches a single item. slug already auto-populates on save, but a
data migration backfills any pre-existing blank ones.
- games: home_team/away_team change from plain strings to {id, name,
logo_url} objects -- home links to the actual Team (logo from the
club's own logo, since teams have none of their own), away links
to the actual Opponent (which already had a logo field). Breaking
change for any existing consumer of the old string shape.
- sponsors: SponsorOut gains logo_width/logo_height, computed in
Sponsor.save() -- Pillow for raster, a bounded regex read of the
SVG root tag for vector logos (not a full XML parse, since that's
exposed to entity-expansion attacks on untrusted uploads). A data
migration backfills dimensions for existing sponsor logos.
- teams: PlayerOut gains `license`, sourced from ClubMembership (not
Member -- it's per-club, per-season), batched in one query.
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