Let a parent sign up a referee-eligible managed child, and hide scrollbars

Referee sign-up (Calendar row and event detail's own card) was scoped to
self.me only; a referee-eligible child is exactly as real as a referee-
eligible parent, and a parent signing one up is no different from
answering an RSVP on their behalf -- both surfaces and the respond view now
cover every managed person, and the calendar row names whose invite it is
once there's more than one managed person to tell apart. Event detail's
single-card layout became a per-person list, same shape as "Your answers".

Also hide the scrollbar on both mobile shells (the member shell's own
document scroll, and the coach shell's still-inner-scrolling .coach-sheet).

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 18:15:38 +02:00
parent e349fbbf03
commit 5db885c809
6 changed files with 139 additions and 78 deletions

View File

@@ -1010,9 +1010,9 @@ class CalendarViewTests(TestCase):
@override_settings(ROSTERCHIEF_BASE_DOMAIN="rosterchief.app", ALLOWED_HOSTS=["rosterchief.app", "ajax-united.rosterchief.app", "testserver"])
class CalendarRefereeSignupTests(TestCase):
"""M3's Calendar merges in the signed-in account's own referee sign-ups
(self.me only, never managed_people) -- see mobile/_calendar_referee_row.
html and CalendarView's own docstring."""
"""M3's Calendar merges in every managed person's referee sign-ups, not
just self.me -- see mobile/_calendar_referee_row.html and CalendarView's
own docstring."""
@classmethod
def setUpTestData(cls):
@@ -1074,6 +1074,22 @@ class CalendarRefereeSignupTests(TestCase):
self.assertNotContains(response, "I'll ref")
def test_a_managed_childs_invite_shows_up_and_names_them(self):
family = Family.objects.create(name="Eree")
FamilyMembership.objects.create(family=family, member=self.member, role=FamilyMembership.FamilyRole.PARENT)
child = Member.objects.create(first_name="Kid", last_name="Eree")
FamilyMembership.objects.create(family=family, member=child, role=FamilyMembership.FamilyRole.CHILD)
ClubMembership.objects.create(club=self.club, member=child, season=self.season)
RefereeProfile.objects.create(member=child, level=self.level, valid_until=timezone.localdate() + datetime.timedelta(days=30))
other_game = Event.objects.create(club=self.club, title="Second game", kind=Event.EventKind.GAME, location=self.home_ground, start=timezone.now() + datetime.timedelta(days=2))
other_game.teams.add(self.team)
self.client.force_login(self.user)
response = self._get()
self.assertTrue(RefereeSignup.objects.filter(event=other_game, member=child).exists())
self.assertContains(response, "Kid")
@override_settings(ROSTERCHIEF_BASE_DOMAIN="rosterchief.app", ALLOWED_HOSTS=["rosterchief.app", "ajax-united.rosterchief.app", "testserver"])
class RefereeSignupRespondViewTests(TestCase):
@@ -1153,6 +1169,21 @@ class RefereeSignupRespondViewTests(TestCase):
self.assertEqual(response.status_code, 404)
def test_a_parent_can_respond_on_behalf_of_a_managed_child(self):
family = Family.objects.create(name="Eree")
FamilyMembership.objects.create(family=family, member=self.member, role=FamilyMembership.FamilyRole.PARENT)
child = Member.objects.create(first_name="Kid", last_name="Eree")
FamilyMembership.objects.create(family=family, member=child, role=FamilyMembership.FamilyRole.CHILD)
ClubMembership.objects.create(club=self.club, member=child, season=self.season)
RefereeProfile.objects.create(member=child, level=self.level, valid_until=timezone.localdate() + datetime.timedelta(days=30))
child_signup = RefereeSignup.objects.create(event=self.game, member=child, status=RefereeSignup.Status.INVITED)
self.client.force_login(self.user)
response = self.client.post(reverse("mobile:referee_signup_respond", kwargs={"signup_id": child_signup.pk}), {"response": "accept"}, HTTP_HOST="ajax-united.rosterchief.app")
self.assertRedirects(response, reverse("mobile:calendar"), fetch_redirect_response=False)
self.assertTrue(EventReferee.objects.filter(event=self.game, member=child).exists())
@override_settings(ROSTERCHIEF_BASE_DOMAIN="rosterchief.app", ALLOWED_HOSTS=["rosterchief.app", "ajax-united.rosterchief.app", "testserver"])
class EventDetailScreenTests(TestCase):
@@ -1311,7 +1342,7 @@ class EventDetailScreenTests(TestCase):
response = self._get()
self.assertIsNone(response.context["referee_signup"])
self.assertEqual(list(response.context["referee_signups"]), [])
self.assertNotContains(response, "Refereeing")
def test_pending_referee_invite_shows_accept_decline(self):
@@ -1339,9 +1370,24 @@ class EventDetailScreenTests(TestCase):
response = self._get()
self.assertIsNone(response.context["referee_signup"])
self.assertEqual(list(response.context["referee_signups"]), [])
self.assertNotContains(response, "Refereeing")
def test_a_managed_childs_referee_invite_shows_up_too(self):
family = Family.objects.create(name="Bakker")
FamilyMembership.objects.create(family=family, member=self.member, role=FamilyMembership.FamilyRole.PARENT)
child = Member.objects.create(first_name="Noor", last_name="Bakker")
FamilyMembership.objects.create(family=family, member=child, role=FamilyMembership.FamilyRole.CHILD)
ClubMembership.objects.create(club=self.club, member=child, season=self.season)
RefereeSignup.objects.create(event=self.event, member=child, status=RefereeSignup.Status.INVITED)
self.client.force_login(self.user)
response = self._get()
self.assertContains(response, "Refereeing")
self.assertContains(response, "Noor Bakker")
self.assertContains(response, "I'll ref")
def test_squad_response_counts_are_correct(self):
in_member = Member.objects.create(first_name="A", last_name="In")
out_member = Member.objects.create(first_name="B", last_name="Out")