Show teams the account holder coaches on the Me page
New beyond the design mock: a "Teams I coach" card listing every current-season StaffAssignment self.me holds (team + role), each row linking straight into Coach mode for that team via ?team=<pk>, which CoachScopeMixin already resolves and persists to the session. Shown for any staffed team, not just ones self.me manages -- Coach mode's own screens already render read-only for a non-management position, so there's nothing to hide on this summary row. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
@@ -7,10 +7,10 @@
|
||||
calls: no license/eligibility field backing "licence OK", so each row's
|
||||
meta line is real roster data instead; "Household & contacts" has no
|
||||
screen to lead to and is omitted, same for the mockup's "Coach mode"
|
||||
promo (base.html's own precedent -- no Coach mode screens exist yet, so
|
||||
it's never rendered, not even as a dead/inert link). "Payments & dues"
|
||||
does lead somewhere (mobile:payments) and carries its "N OPEN" pill only
|
||||
once open_dues_count is actually > 0.
|
||||
promo. "Payments & dues" does lead somewhere (mobile:payments) and
|
||||
carries its "N OPEN" pill only once open_dues_count is actually > 0.
|
||||
"Teams I coach" is new, beyond the mockup -- one row per current-season
|
||||
staff assignment, linking straight into Coach mode for that team.
|
||||
The avatar/name/subtitle row lives in header_extra -- merged into the
|
||||
shared navy app-header (base.html) rather than a separately-coloured
|
||||
block of its own, matching the design canvas's own M5 markup.
|
||||
@@ -66,6 +66,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if staff_assignments %}
|
||||
<div>
|
||||
<div class="mb-2 font-display text-xs font-extrabold tracking-wide text-muted uppercase">{% trans "Teams I coach" %}</div>
|
||||
<div class="m-card overflow-hidden">
|
||||
{% for assignment in staff_assignments %}
|
||||
{% if not forloop.first %}<div class="h-px bg-rule"></div>{% endif %}
|
||||
<a class="flex items-center gap-3 p-3.5" href="{% url "mobile:coach_today" %}?team={{ assignment.team.pk }}">
|
||||
<span class="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-ink font-display text-xs font-extrabold text-ice">{{ assignment.team.short_name|slice:":2" }}</span>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="text-[15px] font-semibold text-ink">{{ assignment.team.name }}</div>
|
||||
<div class="text-xs text-muted">{{ assignment.position }}</div>
|
||||
</div>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" class="shrink-0 text-dim" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5l7 7-7 7"/></svg>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="m-card overflow-hidden">
|
||||
<a class="flex items-center gap-3 p-3.5" href="{% url "mobile:edit_profile" me.pk %}">
|
||||
<span class="flex-1 text-[15px] font-semibold text-ink">{% trans "Personal details" %}</span>
|
||||
|
||||
@@ -1343,6 +1343,26 @@ class MeViewTests(TestCase):
|
||||
|
||||
self.assertContains(response, "Team manager U16")
|
||||
|
||||
def test_no_teams_card_without_a_staff_assignment(self):
|
||||
self.client.force_login(self.user)
|
||||
|
||||
response = self._get()
|
||||
|
||||
self.assertNotContains(response, "Teams I coach")
|
||||
|
||||
def test_teams_card_lists_each_current_season_staff_assignment(self):
|
||||
team = Team.objects.create(club=self.club, name="U16", short_name="U16")
|
||||
position = Position.objects.create(club=self.club, name="Physio", short_name="PHY", staff_position=True, management_position=False)
|
||||
StaffAssignment.objects.create(team=team, member=self.member, season=self.season, position=position)
|
||||
self.client.force_login(self.user)
|
||||
|
||||
response = self._get()
|
||||
|
||||
self.assertContains(response, "Teams I coach")
|
||||
self.assertContains(response, "U16")
|
||||
self.assertContains(response, "Physio")
|
||||
self.assertContains(response, reverse("mobile:coach_today") + "?team=" + str(team.pk))
|
||||
|
||||
def test_empty_account_gets_a_graceful_empty_state(self):
|
||||
bare_user = User.objects.create_user(email="new@example.com", password="pw-secret-123")
|
||||
self.client.force_login(bare_user)
|
||||
|
||||
@@ -33,7 +33,7 @@ from members.models import FamilyMembership, Member
|
||||
from members.views import ClubScopedPublicMixin
|
||||
from news.models import News
|
||||
from notifications.models import Notification
|
||||
from teams.models import TeamMembership
|
||||
from teams.models import StaffAssignment, TeamMembership
|
||||
|
||||
from .forms import MemberProfileForm
|
||||
from .mixins import PersonScopeMixin
|
||||
@@ -530,11 +530,18 @@ class MeView(PersonScopeMixin, LoginRequiredMixin, TemplateView):
|
||||
``self.me`` and into M7 (notifications).
|
||||
|
||||
The mockup's "Household & contacts" row and its "Coach mode" promo card
|
||||
have nowhere to lead in this build (no dedicated screen, no Coach mode
|
||||
screens at all yet -- see base.html's own comment) and are deliberately
|
||||
omitted rather than built as dead or inert links. "Payments & dues" does
|
||||
lead somewhere -- PaymentsView below -- with its "N OPEN" pill only
|
||||
rendered once there's actually a balance owed.
|
||||
have nowhere to lead in this build (no dedicated screen) and are
|
||||
deliberately omitted rather than built as dead or inert links.
|
||||
"Payments & dues" does lead somewhere -- PaymentsView below -- with its
|
||||
"N OPEN" pill only rendered once there's actually a balance owed.
|
||||
|
||||
"Teams I coach" is new, beyond the mockup: one row per current-season
|
||||
StaffAssignment self.me holds (team + position/role), each linking
|
||||
straight into Coach mode for that team (mobile:coach_today?team=<pk>,
|
||||
which mobile.coach_mixins.CoachScopeMixin's own ?team= handling already
|
||||
resolves and persists). Shown for any staffed team, not just ones
|
||||
self.me *manages* -- Coach mode's own screens already render read-only
|
||||
for a non-management position, so there's nothing to hide here.
|
||||
|
||||
There's no license/eligibility field on Member or ClubMembership to power
|
||||
the mockup's "licence OK" text, so each managed person's meta line is
|
||||
@@ -586,11 +593,16 @@ class MeView(PersonScopeMixin, LoginRequiredMixin, TemplateView):
|
||||
|
||||
open_dues_count = len(open_dues_rows(club, self.managed_people, season))
|
||||
|
||||
staff_assignments = []
|
||||
if self.me is not None and season is not None:
|
||||
staff_assignments = list(StaffAssignment.objects.filter(member=self.me, season=season).select_related("team", "position").order_by("team__name"))
|
||||
|
||||
return super().get_context_data(
|
||||
member_since=member_since,
|
||||
team_manager_label=team_manager_label,
|
||||
people_rows=people_rows,
|
||||
open_dues_count=open_dues_count,
|
||||
staff_assignments=staff_assignments,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user