From c4f0ec71c1fe869d88367099716d558f5f335043 Mon Sep 17 00:00:00 2001
From: Bernard Siebens
Date: Tue, 11 Aug 2026 23:52:50 +0200
Subject: [PATCH] Season-scope claim history, self-service second-child claims,
redesign approval screen
Signed-in parents get their details locked and pre-filled on the claim
form instead of retyped; approving links to their existing user and
merges into their existing family instead of creating a duplicate.
The approval screen is now a card grid with a searchable, pre-selected
child dropdown and a reason modal for rejection. The "already dealt
with" history is scoped to the current season.
Co-Authored-By: Claude Sonnet 5
---
.../management/parent_claim_list.html | 104 +++++++------
management/tests.py | 145 ++++++++++++++++++
management/views.py | 14 +-
members/forms.py | 39 ++++-
.../0006_parentclaim_submitted_by_user.py | 21 +++
members/models.py | 9 ++
members/services/claims.py | 45 +++++-
members/services/family.py | 18 ++-
members/templates/members/parent_claim.html | 16 +-
members/tests.py | 69 +++++++++
members/views.py | 31 +++-
11 files changed, 445 insertions(+), 66 deletions(-)
create mode 100644 members/migrations/0006_parentclaim_submitted_by_user.py
diff --git a/management/templates/management/parent_claim_list.html b/management/templates/management/parent_claim_list.html
index 96819ba..db5719e 100644
--- a/management/templates/management/parent_claim_list.html
+++ b/management/templates/management/parent_claim_list.html
@@ -1,5 +1,5 @@
{% extends "management/base.html" %}
-{% load i18n lucide ui %}
+{% load i18n lucide static ui %}
{% block heading %}{% trans "Parent claims" %}{% endblock heading %}
{% block subheading %}{% trans "Parents asking to be linked to a child the club already has on file." %}{% endblock subheading %}
@@ -12,56 +12,64 @@
{% blocktrans %}Check each request against your own records before approving. The shortlist only ever contains children who have nobody on file, so approving can never re-parent a child who already has one. An approved parent is added as a guardian: they get the login, but owe no fee and are not counted as a member.{% endblocktrans %}
- {% for claim in pending %}
-
-
-
-
{% trans "Parent says they are" %}
-
{{ claim.parent_name }}
-
{{ claim.parent_email }}
-
-
-
{% trans "Parent of" %}
-
{{ claim.claimed_child_name }}
-
{{ claim.child_date_of_birth|date:"j F Y" }}
+
+ {% for claim in pending %}
+
+
+
+
{% trans "Parent says they are" %}
+
{{ claim.parent_name }}
+
{{ claim.parent_email }}
+
+
+
{% trans "Parent of" %}
+
{{ claim.claimed_child_name }}
+
{{ claim.child_date_of_birth|date:"j F Y" }}
+
+
+ {% comment %}
+ size="small" + show_label=False on the dropdown: {% form_field %}
+ defaults to daisyUI's medium control height, taller than the btn-sm
+ used by Approve/Reject below -- without it the dropdown stands out
+ against the buttons either side of it.
+
+ justify-between (rather than everything in one flex-wrap run) keeps
+ Approve and Reject apart even as the row wraps on a narrow card --
+ Approve stays pinned left, Reject right, so the two are never one
+ stray click apart the way adjacent buttons would be.
+ {% endcomment %}
+
+ {% if claim.has_candidates %}
+
+ {% else %}
+
{% trans "No child without a parent matches this. Check the spelling and the date of birth with them before rejecting." %}
+ {% endif %}
+
+
+
-
- {% comment %}
- size="small" + show_label=False on the dropdown: {% form_field %}
- defaults to daisyUI's medium control height, taller than the btn-sm/
- input-sm used by everything else in this row -- without it the
- dropdown stands out against the buttons either side of it.
-
- justify-between (rather than everything in one flex-wrap run) keeps
- Approve and Reject apart even as the row wraps on a narrow screen --
- Approve stays pinned left, Reject right, so the two are never one
- stray click apart the way adjacent buttons would be.
- {% endcomment %}
-
- {% if claim.has_candidates %}
-
- {% else %}
-
{% trans "No child without a parent matches this. Check the spelling and the date of birth with them before rejecting." %}
- {% endif %}
-
-
-
-
- {% empty %}
-
{% trans "Nothing waiting." %}
- {% endfor %}
+ {% empty %}
+
{% trans "Nothing waiting." %}
+ {% endfor %}
+
+ {% trans "Reject claim" as reject_claim_title %}
+ {% trans "Reject" as reject_claim_submit_label %}
+ {% for claim in pending %}
+ {% url 'management:parent_claim_reject' claim.pk as reject_claim_url %}
+ {% blocktrans with name=claim.parent_name asvar reject_claim_blurb %}Why is {{ name }}'s claim being rejected? Recorded in the club's own history, not sent to them.{% endblocktrans %}
+ {% include "controlpanel/_modal_form.html" with modal_id=claim.pk|dom_id:"claim_reject_modal" title=reject_claim_title form=claim.reject_form action_url=reject_claim_url submit_label=reject_claim_submit_label submit_icon="x" blurb=reject_claim_blurb %}
+ {% endfor %}
+
{% lucide "user-search" size=18 %} {% trans "Children with nobody on file" %}