Build the club metrics
The club page now leads with its own numbers that should be zero, then the health signals underneath. - Teams with no coach. Not a statistic but a defect in the club's setup: with nobody in a management position the access service grants no authority over that team, so nobody can pick the squad. A physio does not count -- the query keys on Position.management_position, and on this season only. - Unrostered members: active, paid, and on no team. - Unpaid money bucketed by age. "€250 overdue past 60 days" drives a phone call; "€250 outstanding" does not. - Renewal rate -- last season's actives who signed up again. Exactly computable because memberships are season-scoped. - Turnout, plus the share who never responded. Silence is not an absence, so it is excluded from turnout and reported separately: no-response is the leading indicator, since it measures whether members use the app at all. Two of these return None rather than a number, deliberately: a club in its first season has not failed to renew anyone, and a season with no past events has no turnout. Rendering either as 0% would libel the club, so the page says why instead. Money is pinned to two decimals -- SQLite's Sum() drops trailing zeros, so an aggregate rendered "€250" next to a "€0.00" constant on the same card. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,8 @@
|
||||
--text-2xl--line-height: calc(2 / 1.5);
|
||||
--text-3xl: 1.875rem;
|
||||
--text-3xl--line-height: calc(2.25 / 1.875);
|
||||
--text-4xl: 2.25rem;
|
||||
--text-4xl--line-height: calc(2.5 / 2.25);
|
||||
--font-weight-medium: 500;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
@@ -1545,6 +1547,23 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.indicator {
|
||||
@layer daisyui.l1.l2.l3 {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
width: max-content;
|
||||
:where(.indicator-item) {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
top: var(--indicator-t, 0);
|
||||
bottom: var(--indicator-b, auto);
|
||||
left: var(--indicator-s, auto);
|
||||
right: var(--indicator-e, 0);
|
||||
translate: var(--indicator-x, 50%) var(--indicator-y, -50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
.table {
|
||||
@layer daisyui.l1.l2.l3 {
|
||||
font-size: 0.875rem;
|
||||
@@ -3553,6 +3572,10 @@
|
||||
font-size: var(--text-3xl);
|
||||
line-height: var(--tw-leading, var(--text-3xl--line-height));
|
||||
}
|
||||
.text-4xl {
|
||||
font-size: var(--text-4xl);
|
||||
line-height: var(--tw-leading, var(--text-4xl--line-height));
|
||||
}
|
||||
.text-base {
|
||||
font-size: var(--text-base);
|
||||
line-height: var(--tw-leading, var(--text-base--line-height));
|
||||
@@ -3664,6 +3687,9 @@
|
||||
.text-sky-500 {
|
||||
color: var(--color-sky-500);
|
||||
}
|
||||
.text-warning {
|
||||
color: var(--color-warning);
|
||||
}
|
||||
.tabular-nums {
|
||||
--tw-numeric-spacing: tabular-nums;
|
||||
font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
|
||||
@@ -3841,6 +3867,11 @@
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
.lg\:grid-cols-3 {
|
||||
@media (width >= 64rem) {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
.lg\:grid-cols-4 {
|
||||
@media (width >= 64rem) {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
|
||||
Reference in New Issue
Block a user