Add Coach mode C3: game line-up, the last of the six coach screens
New models (events/models.py): Lineup (one per event), LineupUnit (Line 1, Defence pair 1, ... -- coach-entered labels, no fixed sport structure since neither Club nor Team carries one), LineupSlot (one position, optionally filled by a member). events/services/lineup.py's publish_lineup is the reason Attendance.AttendanceStatus.SELECTED/NOT_SELECTED existed at all -- publishing flips every slotted member to SELECTED and every other available (non-out, non-silent) roster member to NOT_SELECTED, then notifies only the selected players. Placement is a native <select> per slot, batch-saved with one "Save line-up" submit, not the design mock's drag-and-drop -- this codebase has no established htmx interaction pattern yet (htmx.js is loaded but nothing uses it) to build a live per-tap version on, and a reliable plain form beats a first, unproven real-time interaction for an already-large screen. place_member handles "swap" semantics for it: placing a member vacates any other slot of theirs in the same lineup, bumping whoever was already in the target slot back to the available pool. Wires the missing pieces from C1's own docstring: the "needs you" line-up- not-published row and the tonight card's "Line-up" button, both deferred in that stage specifically because this model didn't exist yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
75
mobile/templates/mobile/coach/lineup.html
Normal file
75
mobile/templates/mobile/coach/lineup.html
Normal file
@@ -0,0 +1,75 @@
|
||||
{% extends "mobile/coach/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% comment %}
|
||||
C3 -- design_handoff_rosterchief_platform/README.md's C3 section: units
|
||||
(Line 1, Defence pair 1, ...) of ordered slots, each filled via a native
|
||||
<select> rather than the mock's drag-and-drop -- see
|
||||
CoachLineupView's own docstring for why, and for the known "+ Add line"/
|
||||
"+ Add slot" rough edge (they don't save the other slots' picks first).
|
||||
The mock's "fully dark screen" is approximated with dark cards
|
||||
throughout rather than overriding the shared .coach-sheet's own light
|
||||
background -- a real per-screen shell hook is more infrastructure than
|
||||
one screen justifies.
|
||||
{% endcomment %}
|
||||
|
||||
{% 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 %}
|
||||
</div>
|
||||
<div class="mt-1 text-xs text-on-dark-dim">{{ event.title }} · {{ event.start|date:"D d M H:i" }}</div>
|
||||
{% endblock header_extra %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post" action="{% url "mobile:coach_lineup" event.pk %}">
|
||||
{% csrf_token %}
|
||||
<div class="flex flex-col gap-3">
|
||||
{% for unit in units %}
|
||||
<div class="m-card-dark p-3">
|
||||
<div class="mb-2 font-display text-xs font-extrabold tracking-wide text-ice uppercase">{{ unit.label }}</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for slot in unit.slots.all %}
|
||||
<select class="h-10 w-full rounded-lg border border-steel bg-steel px-2 text-sm text-white" name="slot_{{ slot.pk }}" {% if not can_manage_active_team %}disabled{% endif %}>
|
||||
<option value="">{% trans "Empty" %}</option>
|
||||
{% for attendance in available %}
|
||||
<option value="{{ attendance.member.pk }}" {% if slot.member_id == attendance.member.pk %}selected{% endif %}>{{ attendance.member.get_full_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if can_manage_active_team %}
|
||||
<button class="mt-2 font-display text-xs font-extrabold tracking-wide text-ice uppercase" type="submit" formaction="{% url "mobile:coach_lineup_add_slot" unit.pk %}">{% trans "+ Add slot" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="m-card-dark p-6 text-center text-sm text-on-dark-dim">{% trans "No lines yet." %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if can_manage_active_team %}
|
||||
<div class="mt-3 flex gap-2">
|
||||
<button class="btn flex-1 bg-steel text-on-dark" type="submit" formaction="{% url "mobile:coach_lineup_add_unit" event.pk %}">{% trans "+ Add line" %}</button>
|
||||
<button class="btn btn-dark flex-1" type="submit">{% trans "Save line-up" %}</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
{% if unavailable %}
|
||||
<div class="mt-1">
|
||||
<div class="mb-2 font-display text-xs font-extrabold tracking-wide text-on-dark-dim uppercase">{% trans "Unavailable" %}</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{% for attendance in unavailable %}
|
||||
<span class="rounded-full bg-steel px-3 py-1.5 text-xs text-on-dark-faint opacity-50">{{ attendance.member.get_full_name }} · {{ attendance.get_status_display }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% 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 %}">
|
||||
{% csrf_token %}
|
||||
<button class="btn w-full bg-ice text-ice-ink" type="submit">{% trans "Publish" %}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
@@ -47,7 +47,12 @@
|
||||
<div class="px-4 pb-4">
|
||||
<p class="font-display text-xl leading-none font-extrabold uppercase">{{ tonight_event.title }}</p>
|
||||
{% if can_manage_active_team %}
|
||||
<a class="btn mt-3 w-full bg-ice text-ice-ink" href="{% url "mobile:coach_attendance" tonight_event.pk %}">{% trans "Check attendance" %}</a>
|
||||
<div class="mt-3 flex gap-2">
|
||||
<a class="btn flex-1 bg-ice text-ice-ink" href="{% url "mobile:coach_attendance" tonight_event.pk %}">{% trans "Check attendance" %}</a>
|
||||
{% if tonight_event.kind == "game" %}
|
||||
<a class="btn flex-1 bg-steel text-on-dark" href="{% url "mobile:coach_lineup" tonight_event.pk %}">{% trans "Line-up" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -64,6 +69,8 @@
|
||||
</div>
|
||||
{% if item.severity == "warn" and session_event and can_manage_active_team %}
|
||||
<a class="shrink-0 font-display text-xs font-extrabold tracking-wide text-club uppercase" href="{% url "mobile:coach_attendance" session_event.pk %}">{% trans "Review" %}</a>
|
||||
{% elif item.severity == "club" and session_event and can_manage_active_team %}
|
||||
<a class="shrink-0 font-display text-xs font-extrabold tracking-wide text-club uppercase" href="{% url "mobile:coach_lineup" session_event.pk %}">{% trans "Build" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
Reference in New Issue
Block a user