Let parents register once and book for their children
A parent registers once and manages lessons for one or more children, who need no login of their own. A child is a real wp_users row with the student role but no usable login — so student_id keeps meaning "a WordPress user" on every table, and booking, credits, policies and enrolments work unchanged. A us_guardians link table maps guardian to child. The signup form gains a parent/guardian tick that reveals a block per child, with the account-signup questions asked per child rather than per guardian — they describe the student, not the account holder. Signup policies are recorded once per child with the guardian as the acceptor, which is the record that actually means something. A family that half-creates is rolled back entirely rather than leaving a guardian who cannot re-register. The booking and enrolment forms gain a "Who is this for?" picker listing children first, so the default selection is never the parent — booking for the wrong child is correctable, quietly billing a parent for their kid's lesson is not. POST /bookings and POST /enrollments take an optional student_id honoured only for that child's guardian; anything else is a 403. That check is the authorisation boundary of the feature. Payments and credits gain a payer: the charge names the child it was for and the guardian who owes it, so per-child reporting is unchanged while notices, receipts and the payment step reach the parent. Credit is held by the payer, so one child's cancellation can settle a sibling's charge, and the daily billing scan sends a guardian one notice covering every child. Closes #132 Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+17
-1
@@ -16,6 +16,11 @@
|
||||
const pinnedTypeId = Number(app.dataset.lessonType) || 0;
|
||||
const filterEnabled = app.dataset.typeFilter !== '0';
|
||||
|
||||
// Who this account may book for — children first, the account holder last,
|
||||
// so a guardian's default selection is a child rather than themselves. A
|
||||
// single-student account has one entry and gets no picker.
|
||||
const students = window.usGuardian.parseStudents(app.dataset.students);
|
||||
|
||||
function apiFetch(path, options = {}) {
|
||||
return fetch(restUrl + path, {
|
||||
...options,
|
||||
@@ -453,6 +458,7 @@
|
||||
<div class="us-register">
|
||||
<h3>${escHtml(dayLabel(dayKey(slot.start_dt)))} · ${escHtml(timeOf(slot.start_dt))}–${escHtml(timeOf(slot.end_dt))}</h3>
|
||||
<form id="us-register-form">
|
||||
${window.usGuardian.selectorHtml(students, 'us-booking-student')}
|
||||
${offeringFieldHtml(tied, tiedId, choices)}
|
||||
<div id="us-questions"></div>
|
||||
${policies.map(policyField).join('')}
|
||||
@@ -553,6 +559,7 @@
|
||||
body: JSON.stringify({
|
||||
slot_id: slot.id,
|
||||
offering_id: offeringId,
|
||||
student_id: window.usGuardian.selectedId('us-booking-student'),
|
||||
recurrence: weeklyEl && weeklyEl.checked ? 'weekly' : 'single',
|
||||
answers,
|
||||
accepted_policy_version_ids: accepted,
|
||||
@@ -579,6 +586,15 @@
|
||||
// How many upcoming lessons to show before the "Show all" reveal.
|
||||
const INITIAL_LESSON_COUNT = 5;
|
||||
|
||||
// Whose lesson this is. Only shown on an account that books for more than
|
||||
// one person — on a single-student account the name is on every row and says
|
||||
// nothing.
|
||||
function lessonWhoHtml(l) {
|
||||
if (students.length < 2 || !l.student_name) return '';
|
||||
|
||||
return ` <span class="us-my-lesson-who">— ${escHtml(String(l.student_name))}</span>`;
|
||||
}
|
||||
|
||||
function lessonRowHtml(l) {
|
||||
const title = l.offering_title ? escHtml(String(l.offering_title)) : 'Lesson';
|
||||
const duration = l.duration_minutes ? ` <span class="us-my-lesson-duration">(${escHtml(String(l.duration_minutes))} min)</span>` : '';
|
||||
@@ -588,7 +604,7 @@
|
||||
return `
|
||||
<div class="us-my-lesson">
|
||||
<div class="us-my-lesson-info">
|
||||
<strong class="us-my-lesson-title">${title}${duration}</strong>
|
||||
<strong class="us-my-lesson-title">${title}${duration}${lessonWhoHtml(l)}</strong>
|
||||
<span class="us-my-lesson-when">${escHtml(dayLabel(dayKey(l.start_dt)))} · ${escHtml(timeOf(l.start_dt))}–${escHtml(timeOf(l.end_dt))}</span>
|
||||
</div>
|
||||
<div class="us-my-lesson-actions">
|
||||
|
||||
Reference in New Issue
Block a user