The dashboard leads with the numbers that are supposed to be zero, because a
dashboard of healthy counts is one nobody opens:
- Clubs with no season covering today. Seasons scope memberships, rosters and
events, so such a club cannot take a signup or schedule a match -- and it fails
silently, nothing errors, it is just inert.
- Dormant clubs: nothing on the calendar for 30 days. Churn signal.
- Admins pending MFA. RequireMFAMiddleware redirects them to enrolment, so they
are locked out of their own club until they act: a support queue, not a stat.
- Outstanding money across every club.
Then the shape of the business: an onboarding funnel (clubs → with members → with
a team → with events, which separates working clubs from shells), feature-flag
adoption per club, and two charts -- signups and revenue per month.
Charts use chart.js, self-hosted rather than pulled from a CDN, for the same
reason as the fonts: no third-party in the render path. Two things the browser
taught me: the canvas needs a height-bounded wrapper (with maintainAspectRatio
off it sizes to its parent, and a parent with no height grew it to 3489px), and
chart.js cannot read daisyUI's CSS variables, so the charts re-render on a
data-theme change or keep the light palette in dark mode.
The month series is zero-filled: a chart that skips empty months draws a smooth
line straight over a month in which nothing happened.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>