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 <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
|
||||
|
||||
|
||||
class ParentClaimForm(forms.Form):
|
||||
"""The public "link me to my child" form -- see members.models.ParentClaim.
|
||||
@@ -10,6 +12,13 @@ class ParentClaimForm(forms.Form):
|
||||
that confirmed whether a given child exists would turn it into a way to
|
||||
enumerate the club's children. The submitter types what they know and an
|
||||
admin matches it against the real record.
|
||||
|
||||
``lock_parent_fields`` drops the "about you" fields entirely rather than
|
||||
just pre-filling them: a signed-in parent claiming a second child is shown
|
||||
read-only text instead (see members.views.ParentClaimView), because an
|
||||
editable-but-pre-filled input still lets a mismatched email slip through.
|
||||
The view fills parent_first_name/parent_last_name/parent_email in from the
|
||||
authenticated Member afterwards -- never from anything the client submits.
|
||||
"""
|
||||
|
||||
parent_first_name = forms.CharField(label=_("Your first name"), max_length=150)
|
||||
@@ -20,6 +29,12 @@ class ParentClaimForm(forms.Form):
|
||||
child_last_name = forms.CharField(label=_("Child's last name"), max_length=150)
|
||||
child_date_of_birth = forms.DateField(label=_("Child's date of birth"), widget=forms.DateInput(attrs={"type": "date"}))
|
||||
|
||||
def __init__(self, *args, lock_parent_fields=False, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if lock_parent_fields:
|
||||
for name in ("parent_first_name", "parent_last_name", "parent_email"):
|
||||
del self.fields[name]
|
||||
|
||||
|
||||
class ClaimReviewForm(forms.Form):
|
||||
"""An admin approving one claim: which child it actually refers to.
|
||||
@@ -33,9 +48,31 @@ class ClaimReviewForm(forms.Form):
|
||||
# already builds the full class list (including the size modifier), and a
|
||||
# class baked into the widget attrs would render a second, conflicting
|
||||
# class="..." on the <select> alongside it rather than merging with it.
|
||||
child = forms.ModelChoiceField(queryset=None, label=_("Link to"), widget=forms.Select())
|
||||
# data-searchable matches every other member-picker in the app (e.g.
|
||||
# TeamMembershipForm.member) -- useful once a club has many candidates.
|
||||
child = forms.ModelChoiceField(
|
||||
queryset=None,
|
||||
label=_("Link to"),
|
||||
widget=forms.Select(attrs={"data-searchable": "true", "data-search-placeholder": _("Type a name to search...")}),
|
||||
)
|
||||
|
||||
def __init__(self, *args, candidates=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if candidates is None:
|
||||
candidates = Member.objects.none()
|
||||
self.fields["child"].queryset = candidates
|
||||
self.fields["child"].label_from_instance = lambda child: f"{child} ({child.date_of_birth:%d %b %Y})" if child.date_of_birth else str(child)
|
||||
# Pre-select the top match rather than leaving the empty placeholder as
|
||||
# the genuinely-selected option -- see management.views.ParentClaimListView.
|
||||
top_match = candidates.first()
|
||||
if top_match is not None:
|
||||
self.fields["child"].initial = top_match.pk
|
||||
|
||||
|
||||
class ClaimRejectForm(forms.Form):
|
||||
"""The optional reason recorded when an admin rejects a claim -- see
|
||||
members.services.claims.reject_claim. Shown in a modal rather than an
|
||||
inline row input so a real reason isn't fighting for space next to the
|
||||
Approve/Reject buttons."""
|
||||
|
||||
note = forms.CharField(label=_("Reason"), required=False, widget=forms.Textarea(attrs={"rows": 2}), help_text=_("Recorded in the club's own history -- not sent to the parent."))
|
||||
|
||||
Reference in New Issue
Block a user