Let a coach schedule a line-up to publish itself later

Publish now still works exactly as before (default action), but a coach
can also pick a future date/time -- events.services.lineup.
schedule_lineup_publish sets Lineup.scheduled_publish_at, and a new
periodic task (events.tasks.publish_scheduled_lineups, every 15 minutes)
publishes it once that time arrives via the same publish_lineup used for a
manual publish, so selection/Attendance/notifications all work identically
either way. A pending schedule can be cancelled or published early from the
same screen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
2026-08-22 20:52:22 +02:00
parent 3b634fda22
commit 1d0c2ef299
11 changed files with 288 additions and 16 deletions

View File

@@ -21,7 +21,11 @@
{% block header_extra %}
<div class="mt-3 flex items-center justify-between">
<span class="font-display text-xl leading-none font-extrabold text-white uppercase">{% trans "Line-up" %}</span>
{% if lineup.published_at %}<span class="pill pill-info">{% trans "Published" %}</span>{% endif %}
{% if lineup.published_at %}
<span class="pill pill-info">{% trans "Published" %}</span>
{% elif lineup.scheduled_publish_at %}
<span class="pill pill-warn">{% trans "Scheduled" %}</span>
{% endif %}
</div>
<div class="mt-1 text-xs text-on-dark-dim">{{ event.title }} &middot; {{ event.start|date:"D d M H:i" }}</div>
{% endblock header_extra %}
@@ -75,9 +79,37 @@
{% endif %}
{% if can_manage_active_team and not lineup.published_at %}
<form class="mt-2" method="post" action="{% url "mobile:coach_lineup_publish" event.pk %}" hx-boost="false">
{% csrf_token %}
<button class="btn w-full bg-ice text-ice-ink" type="submit">{% trans "Publish" %}</button>
</form>
{% if lineup.scheduled_publish_at %}
<div class="m-card-dark mt-2 p-4">
<p class="text-sm text-white">{% blocktrans with time=lineup.scheduled_publish_at|date:"D d M H:i" %}Scheduled to publish {{ time }}{% endblocktrans %}</p>
<div class="mt-3 grid grid-cols-2 gap-2">
<form method="post" action="{% url "mobile:coach_lineup_publish" event.pk %}" hx-boost="false">
{% csrf_token %}
<input type="hidden" name="action" value="publish_now">
<button class="btn w-full bg-ice text-ice-ink" type="submit">{% trans "Publish now" %}</button>
</form>
<form method="post" action="{% url "mobile:coach_lineup_publish" event.pk %}" hx-boost="false">
{% csrf_token %}
<input type="hidden" name="action" value="cancel_schedule">
<button class="btn bg-steel text-on-dark w-full" type="submit">{% trans "Cancel schedule" %}</button>
</form>
</div>
</div>
{% else %}
<form class="mt-2" method="post" action="{% url "mobile:coach_lineup_publish" event.pk %}" hx-boost="false">
{% csrf_token %}
<input type="hidden" name="action" value="publish_now">
<button class="btn w-full bg-ice text-ice-ink" type="submit">{% trans "Publish" %}</button>
</form>
<details class="mt-2">
<summary class="cursor-pointer font-display text-xs font-extrabold tracking-wide text-on-dark-dim uppercase">{% trans "Or schedule for later" %}</summary>
<form class="mt-2 flex items-center gap-2" method="post" action="{% url "mobile:coach_lineup_publish" event.pk %}" hx-boost="false">
{% csrf_token %}
<input type="hidden" name="action" value="schedule">
<input type="datetime-local" name="publish_at" class="h-10 min-w-0 flex-1 rounded-lg border border-steel bg-steel px-2 text-sm text-white" required>
<button class="btn shrink-0 bg-steel text-on-dark" type="submit">{% trans "Schedule" %}</button>
</form>
</details>
{% endif %}
{% endif %}
{% endblock content %}