Coach lineup polish, referee level/position delete, feature-flag nav gating, per-job pause

- Coach line-up screen: more breathing room above the sheet, brighter event
  subtitle, and the "Schedule" button now matches the date input's height.
- Positions and referee levels can now be deleted from Settings (blocked with
  a friendly message if still in use on a roster/referee profile/inheritance
  chain).
- The Evaluations nav placeholder is now gated on the formbuilder flag, same
  as Forms itself, since the design reuses formbuilder underneath.
- Control panel: each scheduled platform job can now be paused/resumed
  individually (features.models.JobToggle), independent of the platform-wide
  Maintenance lock.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-23 11:17:46 +02:00
parent e621086f37
commit 1365dcf18e
23 changed files with 422 additions and 3029 deletions

View File

@@ -12,28 +12,32 @@
{% block panel %}
<div class="rounded border border-info-border bg-info-bg px-4 py-3 text-sm text-info-text">
{% lucide "info" size=16 class="inline -mt-0.5 mr-1" %}
These run on Celery Beat's own schedule (see <span class="font-mono">rosterchief/settings.py</span>) — this page is monitoring only, there is no "run now" here. A run that never appears at its scheduled time is the signal something's wrong with <span class="font-mono">worker</span>/<span class="font-mono">beat</span>, not with this page.
These run on Celery Beat's own schedule (see <span class="font-mono">rosterchief/settings.py</span>) — there is no "run now" here, but each job can be paused individually below. A run that never appears at its scheduled time (and isn't paused) is the signal something's wrong with <span class="font-mono">worker</span>/<span class="font-mono">beat</span>, not with this page.
</div>
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2 xl:grid-cols-3">
{% for job in jobs %}
<div class="card flex flex-col">
<div class="card flex flex-col {% if not job.enabled %}opacity-70{% endif %}">
<div class="flex items-start justify-between gap-3 border-b border-line px-4 py-3">
<div>
<div class="font-display text-sm font-extrabold tracking-[.08em] text-ink uppercase">{{ job.label }}</div>
<div class="mt-0.5 font-mono text-[11px] text-muted">{{ job.name }}</div>
</div>
{% if job.latest %}
{% if job.latest.status == "success" %}
<span class="badge badge-success shrink-0">ok</span>
{% elif job.latest.status == "failure" %}
<span class="badge badge-error shrink-0">error</span>
<div class="flex shrink-0 flex-col items-end gap-1">
{% if not job.enabled %}
<span class="badge badge-warning shrink-0">paused</span>
{% elif job.latest %}
{% if job.latest.status == "success" %}
<span class="badge badge-success shrink-0">ok</span>
{% elif job.latest.status == "failure" %}
<span class="badge badge-error shrink-0">error</span>
{% else %}
<span class="badge badge-info shrink-0">running</span>
{% endif %}
{% else %}
<span class="badge badge-info shrink-0">running</span>
<span class="badge shrink-0">never run</span>
{% endif %}
{% else %}
<span class="badge shrink-0">never run</span>
{% endif %}
</div>
</div>
<div class="flex flex-1 flex-col gap-2 px-4 py-3 text-sm">
{# flex-1 here so the paragraph absorbs whatever space a shorter description leaves, keeping the schedule pill pinned to the bottom of the content block across every card in the row instead of drifting with description length. #}
@@ -55,7 +59,7 @@
</div>
{% if job.runs|length > 1 %}
<div class="mt-auto flex flex-col gap-1.5 border-t border-rule px-4 py-3">
<div class="flex flex-col gap-1.5 border-t border-rule px-4 py-3">
<div class="font-mono text-[10px] tracking-[.06em] text-dim uppercase">Recent runs</div>
{% for run in job.runs|slice:":5" %}
<div class="flex items-center gap-2 font-mono text-[11px]">
@@ -71,6 +75,17 @@
{% endfor %}
</div>
{% endif %}
<div class="mt-auto border-t border-rule px-4 py-3">
<form method="post" action="{% url 'controlpanel:job_toggle' job.name %}">
{% csrf_token %}
{% if job.enabled %}
<button type="submit" class="btn btn-outline btn-sm btn-error w-full gap-2">{% lucide "pause" size=14 %} Pause job</button>
{% else %}
<button type="submit" class="btn btn-outline btn-sm btn-success w-full gap-2">{% lucide "play" size=14 %} Resume job</button>
{% endif %}
</form>
</div>
</div>
{% endfor %}
</div>