Widen News' filter row, drop nested count badges; row Attendance/Documents/Referee cards
News list: the left pane is wider (380px -> 460px) so the four status chips
fit on one line, and each chip's count is now plain text in the pill itself
("All 42") rather than a nested badge -- matches D8's own "All 42 / Drafts 3"
wording literally. Member detail: Attendance, Documents and Referee
eligibility now sit side by side in one row instead of stacking as three
full-width cards.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
@@ -167,147 +167,150 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if show_attendance %}
|
||||
{# D7's season card: a 12-bar attendance sparkline (present/absent/upcoming, oldest first) beside the season's Present/Absent/No-reply totals. #}
|
||||
<div class="card card-body mt-4">
|
||||
<h2 class="card-title">{% lucide "calendar-check" size=18 %} {% trans "Attendance" %}</h2>
|
||||
{% if not attendance_sparkline %}
|
||||
<p class="text-sm text-muted">{% trans "No events yet this season." %}</p>
|
||||
{# Attendance, Documents and Referee eligibility side by side rather than three full-width rows -- h-full keeps them matched in height regardless of which has more content. #}
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-3 mt-4">
|
||||
{% if show_attendance %}
|
||||
{# D7's season card: a 12-bar attendance sparkline (present/absent/upcoming, oldest first) beside the season's Present/Absent/No-reply totals. #}
|
||||
<div class="card card-body h-full">
|
||||
<h2 class="card-title">{% lucide "calendar-check" size=18 %} {% trans "Attendance" %}</h2>
|
||||
{% if not attendance_sparkline %}
|
||||
<p class="text-sm text-muted">{% trans "No events yet this season." %}</p>
|
||||
{% else %}
|
||||
<div class="flex flex-wrap items-center gap-6">
|
||||
<div class="attendance-sparkline">
|
||||
{% for bar in attendance_sparkline %}
|
||||
<span class="attendance-bar attendance-bar-{{ bar.state }}" title="{{ bar.event.title }} — {{ bar.event.start|date:"j M" }}"></span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="flex gap-4">
|
||||
<div>
|
||||
<div class="font-display text-2xl leading-none font-extrabold text-ok tabular-nums">{{ attendance_counts.present }}</div>
|
||||
<div class="mt-1 font-display text-[11px] font-bold tracking-[.1em] text-muted uppercase">{% trans "Present" %}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-display text-2xl leading-none font-extrabold text-club tabular-nums">{{ attendance_counts.absent }}</div>
|
||||
<div class="mt-1 font-display text-[11px] font-bold tracking-[.1em] text-muted uppercase">{% trans "Absent" %}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-display text-2xl leading-none font-extrabold text-dim tabular-nums">{{ attendance_counts.no_reply }}</div>
|
||||
<div class="mt-1 font-display text-[11px] font-bold tracking-[.1em] text-muted uppercase">{% trans "No reply" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% comment %}
|
||||
Onboarding checklist -- club.services.onboarding.checklist_for, paired with
|
||||
MemberRequirementStatus if one exists. Any staff can mark an item done or
|
||||
reopen it, not just admins (same visibility as the rest of this page) --
|
||||
see the module docstring on club/services/onboarding.py for why this stays
|
||||
separate from ClubMembership.status/fee_status.
|
||||
{% endcomment %}
|
||||
<div class="card card-body h-full">
|
||||
<h2 class="card-title">{% lucide "clipboard-check" size=18 %} {% trans "Documents" %}</h2>
|
||||
{% if not current_membership %}
|
||||
<p class="text-sm text-muted">{% trans "Not rostered for the current season -- nothing to check off yet." %}</p>
|
||||
{% elif not checklist %}
|
||||
<p class="text-sm text-muted">{% trans "This club has no onboarding requirements set up." %}</p>
|
||||
{% else %}
|
||||
<div class="flex flex-wrap items-center gap-8">
|
||||
<div class="attendance-sparkline">
|
||||
{% for bar in attendance_sparkline %}
|
||||
<span class="attendance-bar attendance-bar-{{ bar.state }}" title="{{ bar.event.title }} — {{ bar.event.start|date:"j M" }}"></span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="flex gap-6">
|
||||
<div>
|
||||
<div class="font-display text-2xl leading-none font-extrabold text-ok tabular-nums">{{ attendance_counts.present }}</div>
|
||||
<div class="mt-1 font-display text-[11px] font-bold tracking-[.1em] text-muted uppercase">{% trans "Present" %}</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
{% for requirement, status in checklist %}
|
||||
<div class="flex items-start justify-between gap-4 py-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="font-semibold text-ink">{{ requirement.name }}</span>
|
||||
{% if status.is_complete %}
|
||||
<span class="badge badge-sm badge-success">{% trans "Complete" %}</span>
|
||||
{% elif status %}
|
||||
<span class="badge badge-sm badge-warning">{% trans "Incomplete" %}</span>
|
||||
{% else %}
|
||||
<span class="badge badge-sm badge-ghost">{% trans "Not started" %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if requirement.description %}<p class="mt-1 text-sm text-muted">{{ requirement.description }}</p>{% endif %}
|
||||
{% if status.is_complete %}
|
||||
<p class="mt-1 font-mono text-xs text-dim">{% blocktrans with who=status.completed_by when=status.completed_at|date:"j M Y H:i" %}Completed by {{ who }} on {{ when }}{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
{% if status.note %}<p class="mt-1 text-sm text-slate italic">“{{ status.note }}”</p>{% endif %}
|
||||
{% if status.document %}
|
||||
<a class="mt-1 inline-flex items-center gap-1 text-sm link link-hover" href="{% url 'management:member_requirement_document' pk=member.pk requirement_pk=requirement.pk %}">{% lucide "download" size=13 %} {% trans "Download document" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="flex shrink-0 items-center gap-1.5">
|
||||
{% if status.is_complete %}
|
||||
<form method="post" action="{% url 'management:member_requirement_incomplete' pk=member.pk requirement_pk=requirement.pk %}">
|
||||
{% csrf_token %}
|
||||
<button class="btn btn-outline btn-xs gap-1" type="submit">{% lucide "rotate-ccw" size=12 %} {% trans "Reopen" %}</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<button class="btn btn-primary btn-xs gap-1" type="button" onclick="document.getElementById('{{ requirement.pk|dom_id:"requirement_complete_modal" }}').showModal()">{% lucide "check" size=12 %} {% trans "Mark complete" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-display text-2xl leading-none font-extrabold text-club tabular-nums">{{ attendance_counts.absent }}</div>
|
||||
<div class="mt-1 font-display text-[11px] font-bold tracking-[.1em] text-muted uppercase">{% trans "Absent" %}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-display text-2xl leading-none font-extrabold text-dim tabular-nums">{{ attendance_counts.no_reply }}</div>
|
||||
<div class="mt-1 font-display text-[11px] font-bold tracking-[.1em] text-muted uppercase">{% trans "No reply" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% comment %}
|
||||
Onboarding checklist -- club.services.onboarding.checklist_for, paired with
|
||||
MemberRequirementStatus if one exists. Any staff can mark an item done or
|
||||
reopen it, not just admins (same visibility as the rest of this page) --
|
||||
see the module docstring on club/services/onboarding.py for why this stays
|
||||
separate from ClubMembership.status/fee_status.
|
||||
{% endcomment %}
|
||||
<div class="card card-body mt-4">
|
||||
<h2 class="card-title">{% lucide "clipboard-check" size=18 %} {% trans "Documents" %}</h2>
|
||||
{% if not current_membership %}
|
||||
<p class="text-sm text-muted">{% trans "Not rostered for the current season -- nothing to check off yet." %}</p>
|
||||
{% elif not checklist %}
|
||||
<p class="text-sm text-muted">{% trans "This club has no onboarding requirements set up." %}</p>
|
||||
{% else %}
|
||||
<div class="flex flex-col gap-1">
|
||||
{% for requirement, status in checklist %}
|
||||
<div class="flex items-start justify-between gap-4 py-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="font-semibold text-ink">{{ requirement.name }}</span>
|
||||
{% if status.is_complete %}
|
||||
<span class="badge badge-sm badge-success">{% trans "Complete" %}</span>
|
||||
{% elif status %}
|
||||
<span class="badge badge-sm badge-warning">{% trans "Incomplete" %}</span>
|
||||
{% if referee_profile or is_club_admin %}
|
||||
<div class="card card-body h-full">
|
||||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||
<h2 class="card-title">{% lucide "flag" size=18 %} {% trans "Referee eligibility" %}</h2>
|
||||
{% if is_club_admin %}
|
||||
<button class="btn btn-outline btn-sm gap-2" type="button" onclick="document.getElementById('referee_eligibility_modal').showModal()">{% lucide "pencil" size=14 %} {% trans "Edit" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if referee_profile and referee_profile.level %}
|
||||
<dl>
|
||||
<div class="flex items-center justify-between gap-4 py-2">
|
||||
<dt class="text-sm text-muted">{% trans "Level" %}</dt>
|
||||
<dd class="text-sm font-semibold text-ink">{{ referee_profile.level }}</dd>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-4 py-2">
|
||||
<dt class="text-sm text-muted">{% trans "Valid until" %}</dt>
|
||||
<dd class="flex items-center gap-2">
|
||||
<span class="font-mono text-sm font-semibold text-ink">{{ referee_profile.valid_until|default:"—" }}</span>
|
||||
{% if referee_profile.is_currently_valid %}
|
||||
<span class="badge badge-sm badge-success">{% trans "Valid" %}</span>
|
||||
{% elif referee_profile.valid_until %}
|
||||
<span class="badge badge-sm badge-error">{% trans "Expired" %}</span>
|
||||
{% else %}
|
||||
<span class="badge badge-sm badge-ghost">{% trans "Not started" %}</span>
|
||||
<span class="badge badge-sm badge-warning">{% trans "No validity set" %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if requirement.description %}<p class="mt-1 text-sm text-muted">{{ requirement.description }}</p>{% endif %}
|
||||
{% if status.is_complete %}
|
||||
<p class="mt-1 font-mono text-xs text-dim">{% blocktrans with who=status.completed_by when=status.completed_at|date:"j M Y H:i" %}Completed by {{ who }} on {{ when }}{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
{% if status.note %}<p class="mt-1 text-sm text-slate italic">“{{ status.note }}”</p>{% endif %}
|
||||
{% if status.document %}
|
||||
<a class="mt-1 inline-flex items-center gap-1 text-sm link link-hover" href="{% url 'management:member_requirement_document' pk=member.pk requirement_pk=requirement.pk %}">{% lucide "download" size=13 %} {% trans "Download document" %}</a>
|
||||
{% endif %}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="flex shrink-0 items-center gap-1.5">
|
||||
{% if status.is_complete %}
|
||||
<form method="post" action="{% url 'management:member_requirement_incomplete' pk=member.pk requirement_pk=requirement.pk %}">
|
||||
{% csrf_token %}
|
||||
<button class="btn btn-outline btn-xs gap-1" type="submit">{% lucide "rotate-ccw" size=12 %} {% trans "Reopen" %}</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<button class="btn btn-primary btn-xs gap-1" type="button" onclick="document.getElementById('{{ requirement.pk|dom_id:"requirement_complete_modal" }}').showModal()">{% lucide "check" size=12 %} {% trans "Mark complete" %}</button>
|
||||
{% endif %}
|
||||
</dl>
|
||||
|
||||
{% if referee_profile.is_eligible %}
|
||||
<div class="mt-2 mb-1 font-display text-xs font-bold tracking-[.1em] text-muted uppercase">{% trans "Can referee for" %}</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{% for team in referee_profile.eligible_teams %}
|
||||
<span class="badge badge-outline">{{ team.name }}</span>
|
||||
{% empty %}
|
||||
<span class="text-sm text-muted">{% trans "This level has no teams linked yet." %}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="alert alert-warning mt-2">
|
||||
{% lucide "triangle-alert" size=16 %}
|
||||
<span>{% trans "Not currently eligible to referee -- set or extend the validity date above to reinstate them." %}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="text-sm text-muted">{% trans "Not eligible to referee for any team." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if referee_profile or is_club_admin %}
|
||||
<div class="card card-body mt-4">
|
||||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||
<h2 class="card-title">{% lucide "flag" size=18 %} {% trans "Referee eligibility" %}</h2>
|
||||
{% if is_club_admin %}
|
||||
<button class="btn btn-outline btn-sm gap-2" type="button" onclick="document.getElementById('referee_eligibility_modal').showModal()">{% lucide "pencil" size=14 %} {% trans "Edit" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if referee_profile and referee_profile.level %}
|
||||
<dl>
|
||||
<div class="flex items-center justify-between gap-4 py-2">
|
||||
<dt class="text-sm text-muted">{% trans "Level" %}</dt>
|
||||
<dd class="text-sm font-semibold text-ink">{{ referee_profile.level }}</dd>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-4 py-2">
|
||||
<dt class="text-sm text-muted">{% trans "Valid until" %}</dt>
|
||||
<dd class="flex items-center gap-2">
|
||||
<span class="font-mono text-sm font-semibold text-ink">{{ referee_profile.valid_until|default:"—" }}</span>
|
||||
{% if referee_profile.is_currently_valid %}
|
||||
<span class="badge badge-sm badge-success">{% trans "Valid" %}</span>
|
||||
{% elif referee_profile.valid_until %}
|
||||
<span class="badge badge-sm badge-error">{% trans "Expired" %}</span>
|
||||
{% else %}
|
||||
<span class="badge badge-sm badge-warning">{% trans "No validity set" %}</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
{% if referee_profile.is_eligible %}
|
||||
<div class="mt-2 mb-1 font-display text-xs font-bold tracking-[.1em] text-muted uppercase">{% trans "Can referee for" %}</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{% for team in referee_profile.eligible_teams %}
|
||||
<span class="badge badge-outline">{{ team.name }}</span>
|
||||
{% empty %}
|
||||
<span class="text-sm text-muted">{% trans "This level has no teams linked yet." %}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning mt-2">
|
||||
{% lucide "triangle-alert" size=16 %}
|
||||
<span>{% trans "Not currently eligible to referee -- set or extend the validity date above to reinstate them." %}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="text-sm text-muted">{% trans "Not eligible to referee for any team." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if is_club_admin %}
|
||||
{% url 'management:member_referee_eligibility_update' member.pk as referee_eligibility_url %}
|
||||
{% trans "Referee eligibility" as referee_eligibility_title %}
|
||||
{% trans "Save" as save_label %}
|
||||
{% include "controlpanel/_modal_form.html" with modal_id="referee_eligibility_modal" title=referee_eligibility_title form=referee_eligibility_form action_url=referee_eligibility_url submit_label=save_label submit_icon="save" %}
|
||||
{% endif %}
|
||||
{% if is_club_admin %}
|
||||
{% url 'management:member_referee_eligibility_update' member.pk as referee_eligibility_url %}
|
||||
{% trans "Referee eligibility" as referee_eligibility_title %}
|
||||
{% trans "Save" as save_label %}
|
||||
{% include "controlpanel/_modal_form.html" with modal_id="referee_eligibility_modal" title=referee_eligibility_title form=referee_eligibility_form action_url=referee_eligibility_url submit_label=save_label submit_icon="save" %}
|
||||
{% endif %}
|
||||
|
||||
{% trans "Add parent" as add_parent_label %}
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
|
||||
{% block panel %}
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="flex w-[380px] shrink-0 flex-col gap-3">
|
||||
{# D8's filter chips -- All/Drafts/Scheduled, plus Published for symmetry with the statuses this app actually has. #}
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<a href="{% querystring status=None selected=None page=None %}" class="btn btn-sm gap-1.5 {% if status_filter == "all" %}btn-primary{% else %}btn-outline{% endif %}">{% trans "All" %} <span class="badge badge-sm badge-ghost">{{ counts.all }}</span></a>
|
||||
<a href="{% querystring status="draft" selected=None page=None %}" class="btn btn-sm gap-1.5 {% if status_filter == "draft" %}btn-primary{% else %}btn-outline{% endif %}">{% trans "Drafts" %} <span class="badge badge-sm badge-ghost">{{ counts.draft }}</span></a>
|
||||
<a href="{% querystring status="scheduled" selected=None page=None %}" class="btn btn-sm gap-1.5 {% if status_filter == "scheduled" %}btn-primary{% else %}btn-outline{% endif %}">{% trans "Scheduled" %} <span class="badge badge-sm badge-ghost">{{ counts.scheduled }}</span></a>
|
||||
<a href="{% querystring status="published" selected=None page=None %}" class="btn btn-sm gap-1.5 {% if status_filter == "published" %}btn-primary{% else %}btn-outline{% endif %}">{% trans "Published" %} <span class="badge badge-sm badge-ghost">{{ counts.published }}</span></a>
|
||||
<div class="flex w-[460px] shrink-0 flex-col gap-3">
|
||||
{# D8's filter chips -- "All 42 / Drafts 3 / Scheduled 1", a raw number in the pill's own text, not a nested badge. Plus Published for symmetry with the statuses this app actually has. #}
|
||||
<div class="flex flex-nowrap gap-1.5">
|
||||
<a href="{% querystring status=None selected=None page=None %}" class="btn btn-sm {% if status_filter == "all" %}btn-primary{% else %}btn-outline{% endif %}">{% trans "All" %} {{ counts.all }}</a>
|
||||
<a href="{% querystring status="draft" selected=None page=None %}" class="btn btn-sm {% if status_filter == "draft" %}btn-primary{% else %}btn-outline{% endif %}">{% trans "Drafts" %} {{ counts.draft }}</a>
|
||||
<a href="{% querystring status="scheduled" selected=None page=None %}" class="btn btn-sm {% if status_filter == "scheduled" %}btn-primary{% else %}btn-outline{% endif %}">{% trans "Scheduled" %} {{ counts.scheduled }}</a>
|
||||
<a href="{% querystring status="published" selected=None page=None %}" class="btn btn-sm {% if status_filter == "published" %}btn-primary{% else %}btn-outline{% endif %}">{% trans "Published" %} {{ counts.published }}</a>
|
||||
</div>
|
||||
|
||||
<div class="card overflow-hidden divide-y">
|
||||
|
||||
@@ -3795,6 +3795,9 @@
|
||||
.w-\[380px\] {
|
||||
width: 380px;
|
||||
}
|
||||
.w-\[460px\] {
|
||||
width: 460px;
|
||||
}
|
||||
.w-auto {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user