Coach mode: bench-attendance filter overhaul, squad player detail, add-staff
- Attendance sheet: "All"/"Goalies" chips replaced with "Responded" (the new default -- present/selected/maybe) and "Silent"; goalies are just another player for a practice, and neither silent nor declined members are expected to show up, so the default view skips both. - Today's KPI header gains an "Out" tile alongside Squad/In/Silent. - Squad screen: each roster row now opens a per-player detail sheet with season attendance stats, tap-to-call buttons (the player's own phone/ emergency phone, plus each guardian's for a child), and -- for whoever manages the team -- the position/jersey/captaincy edit and a remove- from-roster action that used to be desktop-only. - Staff section gets its own "Add" entry point, mirroring the roster's bulk-add flow with a shared position picker (StaffAssignment.position is required, unlike a roster spot's). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from members.models import Member
|
||||
from teams.models import Position, TeamMembership
|
||||
|
||||
#: Shared by every text-ish field below -- mobile has no equivalent of
|
||||
#: management/controlpanel's templatetags/field.html (which builds widget
|
||||
@@ -37,3 +39,37 @@ class MemberProfileForm(forms.ModelForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
for field in self.fields.values():
|
||||
field.widget.attrs["class"] = _INPUT_CLASSES
|
||||
|
||||
|
||||
class CoachRosterEditForm(forms.ModelForm):
|
||||
"""Coach-mode roster row edit -- mobile/coach/roster_member.html's player
|
||||
detail sheet. Same fields management.forms.TeamMembershipForm edits,
|
||||
minus ``member`` (fixed by the URL here, never reassigned from this
|
||||
screen) -- this used to be a desktop-only action; see CoachSquadView's
|
||||
own docstring for why that changed."""
|
||||
|
||||
class Meta:
|
||||
model = TeamMembership
|
||||
fields = ["position", "jersey_number", "is_captain", "is_alternate_captain"]
|
||||
|
||||
def __init__(self, *args, club=None, team=None, season=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.team = team
|
||||
self.season = season
|
||||
self.fields["position"].queryset = Position.objects.filter(club=club, staff_position=False)
|
||||
self.fields["position"].required = True
|
||||
self.fields["position"].widget.attrs["class"] = _INPUT_CLASSES
|
||||
self.fields["jersey_number"].widget.attrs["class"] = _INPUT_CLASSES
|
||||
self.fields["is_captain"].widget.attrs["class"] = "h-5 w-5 shrink-0 accent-ink"
|
||||
self.fields["is_alternate_captain"].widget.attrs["class"] = "h-5 w-5 shrink-0 accent-ink"
|
||||
|
||||
def clean(self):
|
||||
# Same jersey-clash check as TeamMembershipForm.clean -- team/season aren't
|
||||
# form fields, so Django's automatic validate_unique() can't catch this itself.
|
||||
cleaned = super().clean()
|
||||
jersey_number = cleaned.get("jersey_number")
|
||||
if jersey_number is not None and self.team is not None and self.season is not None:
|
||||
clash = TeamMembership.objects.filter(team=self.team, season=self.season, jersey_number=jersey_number).exclude(pk=self.instance.pk).exists()
|
||||
if clash:
|
||||
self.add_error("jersey_number", _("Another player on this team already has this jersey number this season."))
|
||||
return cleaned
|
||||
|
||||
Reference in New Issue
Block a user