Show a child's guardians' phone numbers on their Personal information card
Member.guardians already scoped correctly to "the parents/guardians of the families where this member is the CHILD" -- no model changes needed. Adds clearly-labelled phone/emergency phone rows per guardian plus a dial button each, following the same tel: link pattern already used for the member's own numbers. Empty for members who aren't a child in any family.
This commit is contained in:
@@ -28,6 +28,16 @@
|
|||||||
<dt class="text-sm opacity-70">{% trans "Emergency phone" %}</dt>
|
<dt class="text-sm opacity-70">{% trans "Emergency phone" %}</dt>
|
||||||
<dd class="font-semibold">{{ member.emergency_phone.as_international|default:"-" }}</dd>
|
<dd class="font-semibold">{{ member.emergency_phone.as_international|default:"-" }}</dd>
|
||||||
</div>
|
</div>
|
||||||
|
{% for guardian in guardians %}
|
||||||
|
<div class="flex items-center justify-between py-2">
|
||||||
|
<dt class="text-sm opacity-70">{% trans "Parent phone" %} — {{ guardian.get_full_name }}</dt>
|
||||||
|
<dd class="font-semibold">{{ guardian.phone.as_international|default:"-" }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between py-2">
|
||||||
|
<dt class="text-sm opacity-70">{% trans "Parent emergency phone" %} — {{ guardian.get_full_name }}</dt>
|
||||||
|
<dd class="font-semibold">{{ guardian.emergency_phone.as_international|default:"-" }}</dd>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<div class="flex flex-row gap-2 items-center mt-2">
|
<div class="flex flex-row gap-2 items-center mt-2">
|
||||||
@@ -38,6 +48,25 @@
|
|||||||
<a class="btn btn-sm btn-outline btn-error grow" href="tel:{{ member.emergency_phone.as_international }}">{% lucide "shield-alert" size=14 %} {% trans "Emergency call" %}</a>
|
<a class="btn btn-sm btn-outline btn-error grow" href="tel:{{ member.emergency_phone.as_international }}">{% lucide "shield-alert" size=14 %} {% trans "Emergency call" %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if guardians %}
|
||||||
|
<div class="divider my-1"></div>
|
||||||
|
<div class="text-xs font-semibold uppercase opacity-60 flex items-center gap-1 mb-1">
|
||||||
|
{% lucide "users" size=12 %} {% trans "Parent/guardian contact" %}
|
||||||
|
</div>
|
||||||
|
{% for guardian in guardians %}
|
||||||
|
{% if guardian.phone or guardian.emergency_phone %}
|
||||||
|
<div class="flex flex-row gap-2 items-center mt-2">
|
||||||
|
{% if guardian.phone %}
|
||||||
|
<a class="btn btn-sm btn-outline btn-primary grow" href="tel:{{ guardian.phone.as_international }}">{% lucide "phone" size=14 %} {% blocktrans with name=guardian.get_full_name %}Call {{ name }}{% endblocktrans %}</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if guardian.emergency_phone %}
|
||||||
|
<a class="btn btn-sm btn-outline btn-error grow" href="tel:{{ guardian.emergency_phone.as_international }}">{% lucide "shield-alert" size=14 %} {% blocktrans with name=guardian.get_full_name %}Emergency: {{ name }}{% endblocktrans %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1087,6 +1087,37 @@ class MemberFamilyAttachDetachTests(ManagementTestBase):
|
|||||||
|
|
||||||
self.assertNotIn(family, response.context["attach_to_family_form"].fields["family"].queryset)
|
self.assertNotIn(family, response.context["attach_to_family_form"].fields["family"].queryset)
|
||||||
|
|
||||||
|
def test_a_childs_page_shows_their_guardians_phone_numbers(self):
|
||||||
|
family = Family.objects.create()
|
||||||
|
parent = Member.objects.create(first_name="Pat", last_name="Parent", phone="+32470000001", emergency_phone="+32470000002")
|
||||||
|
FamilyMembership.objects.create(family=family, member=self.standalone, role=FamilyMembership.FamilyRole.CHILD)
|
||||||
|
FamilyMembership.objects.create(family=family, member=parent, role=FamilyMembership.FamilyRole.PARENT)
|
||||||
|
|
||||||
|
response = self.club_get("member_detail", self.standalone.pk)
|
||||||
|
|
||||||
|
self.assertIn(parent, response.context["guardians"])
|
||||||
|
self.assertContains(response, "Pat Parent")
|
||||||
|
self.assertContains(response, "tel:+32 470 00 00 01")
|
||||||
|
self.assertContains(response, "tel:+32 470 00 00 02")
|
||||||
|
|
||||||
|
def test_a_parents_page_does_not_show_guardian_numbers(self):
|
||||||
|
# self.standalone has no CHILD membership anywhere -- the section must not appear.
|
||||||
|
response = self.club_get("member_detail", self.standalone.pk)
|
||||||
|
|
||||||
|
self.assertEqual(len(response.context["guardians"]), 0)
|
||||||
|
self.assertNotContains(response, "Parent/guardian contact")
|
||||||
|
|
||||||
|
def test_a_guardian_with_no_phone_numbers_gets_no_dial_buttons(self):
|
||||||
|
family = Family.objects.create()
|
||||||
|
parent = Member.objects.create(first_name="Pat", last_name="Parent")
|
||||||
|
FamilyMembership.objects.create(family=family, member=self.standalone, role=FamilyMembership.FamilyRole.CHILD)
|
||||||
|
FamilyMembership.objects.create(family=family, member=parent, role=FamilyMembership.FamilyRole.PARENT)
|
||||||
|
|
||||||
|
response = self.club_get("member_detail", self.standalone.pk)
|
||||||
|
|
||||||
|
self.assertContains(response, "Pat Parent")
|
||||||
|
self.assertNotContains(response, "tel:")
|
||||||
|
|
||||||
|
|
||||||
class MemberDeleteTests(ManagementTestBase):
|
class MemberDeleteTests(ManagementTestBase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|||||||
@@ -590,6 +590,10 @@ class MemberDetailView(ClubStaffRequiredMixin, DetailView):
|
|||||||
attach_to_family_form=AttachToFamilyForm(club=self.request.club, member=self.object),
|
attach_to_family_form=AttachToFamilyForm(club=self.request.club, member=self.object),
|
||||||
current_membership=ClubMembership.objects.filter(club=self.request.club, member=self.object, season=current_season(self.request.club)).first(),
|
current_membership=ClubMembership.objects.filter(club=self.request.club, member=self.object, season=current_season(self.request.club)).first(),
|
||||||
membership_history=ClubMembership.objects.filter(club=self.request.club, member=self.object).select_related("season").order_by("-season__start_date"),
|
membership_history=ClubMembership.objects.filter(club=self.request.club, member=self.object).select_related("season").order_by("-season__start_date"),
|
||||||
|
# Non-empty only when `member` is a CHILD in some family -- that's the same
|
||||||
|
# signal the Personal information card uses to decide whether to show parent
|
||||||
|
# contact numbers at all.
|
||||||
|
guardians=self.object.guardians,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user