Files
unsupervised-scheduler/templates/admin/student-detail.php
T
thatguygriffandClaude Opus 5 76caf178f0
CI / Tests (PHP 8.1) (pull_request) Successful in 41s
CI / Tests (PHP 8.2) (pull_request) Successful in 40s
CI / No Debug Code (pull_request) Successful in 2s
CI / Coding Standards (pull_request) Successful in 2m55s
CI / PHPStan (pull_request) Successful in 3m1s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m43s
CI / Build Plugin Zip (pull_request) Skipped
Say "student" and "profile" in the UI, not "child" and "family"
Sweep the translatable strings across the frontend templates, the admin
screens, the editor previews and the block inserter entry. Nothing else
moves: the database columns, request parameters, form field names, CSS
classes, the us_family shortcode and the us-scheduler/family block name are
contracts with existing installs and with post content people have already
saved, so renaming them would break sites for no user-visible gain.

Two strings are reworded rather than swapped, because the direct
substitution reads wrong:

- The students list said "Child of Jane" and now says "Managed by Jane".
  "Student of Jane" would read as a teacher's pupil, which is exactly the
  wrong idea in a music studio.
- A managed account is now "a managed student account" rather than "a
  student account", which would not distinguish it from the account holder.

The guardian feature doc gains a short section on the split, so the next
person to work on it does not read the mismatch as drift and "fix" it.

Closes #144

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-07-29 20:34:55 -03:00

364 lines
19 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
if (! defined('ABSPATH')) {
exit;
}
/**
* @var \WP_User $student
* @var list<array{id: int, start_dt: string, end_dt: string, offering: string, instructor: string, status: string}> $upcoming
* @var list<array{id: int, start_dt: string, end_dt: string, offering: string, instructor: string, status: string}> $past
* @var list<array{id: int, offering: string, status: string}> $enrolments
* @var list<array{policy: string, version: string, context: string, accepted_at: string}> $acceptances
* @var list<array{question: string, answer: string, required: bool}> $registrationInfo
* @var list<array{question: string, answer: string, context: string}> $intake
* @var list<array{created_at: string, context: string, method: string, status: string, amount: float, tax_amount: float, total: float, currency: string, receipt: string}> $payments
* @var list<array{created_at: string, amount: float, remaining: float, currency: string, reason: string, status: string}> $credits
* @var float $creditBalance Balance of the account that settles this student's charges — the guardian's for a child.
* @var string $creditCurrency
* @var array{id: int, name: string, email: string}|null $guardian The parent/guardian who books for this student, or null when they book for themselves.
* @var list<array{id: int, name: string, date_of_birth: string, relationship: string}> $children Children this student books for.
* @var array{id: int, name: string, email: string} $payer Who is billed for this student — themselves, or their guardian.
* @var string $pageSlug
* @var string $backUrl
* @var bool $canBilling
* @var string $billingOverride
* @var string $billingDefault
* @var string $notice
* @var string $error
*/
$renderLessons = static function (array $rows, bool $withActions = false): void {
if (empty($rows)) {
echo '<p>' . esc_html__('None.', 'unsupervised-schedular') . '</p>';
return;
}
?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th><?php esc_html_e('When', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Offering', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Instructor', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Status', 'unsupervised-schedular'); ?></th>
<?php if ($withActions) : ?>
<th><?php esc_html_e('Actions', 'unsupervised-schedular'); ?></th>
<?php endif; ?>
</tr>
</thead>
<tbody>
<?php foreach ($rows as $row) : ?>
<tr>
<td><?php echo esc_html($row['start_dt'] !== '' ? (string) mysql2date('M j, Y g:i A', $row['start_dt']) : '—'); ?></td>
<td><?php echo esc_html($row['offering']); ?></td>
<td><?php echo esc_html($row['instructor']); ?></td>
<td><?php echo esc_html($row['status']); ?></td>
<?php if ($withActions) : ?>
<td>
<?php if ($row['status'] !== 'cancelled') : ?>
<form method="post" style="display:inline">
<?php wp_nonce_field('usc_student_actions'); ?>
<input type="hidden" name="usc_action" value="cancel_lesson">
<input type="hidden" name="lesson_id" value="<?php echo esc_attr((string) $row['id']); ?>">
<button type="submit" class="button-link button-link-delete" onclick="return confirm('<?php echo esc_js(__('Cancel this lesson? The slot is freed and any pending payment is voided.', 'unsupervised-schedular')); ?>');">
<?php esc_html_e('Cancel lesson', 'unsupervised-schedular'); ?>
</button>
</form>
<?php endif; ?>
</td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
};
?>
<div class="wrap">
<h1>
<?php echo esc_html($student->display_name); ?>
<a href="<?php echo esc_url($backUrl); ?>" class="page-title-action"><?php esc_html_e('Back to students', 'unsupervised-schedular'); ?></a>
</h1>
<?php if ($notice !== '') : ?>
<div class="notice notice-success is-dismissible"><p><?php echo esc_html($notice); ?></p></div>
<?php endif; ?>
<?php if ($error !== '') : ?>
<div class="notice notice-error is-dismissible"><p><?php echo esc_html($error); ?></p></div>
<?php endif; ?>
<h2><?php esc_html_e('Account', 'unsupervised-schedular'); ?></h2>
<form method="post">
<?php wp_nonce_field('usc_student_actions'); ?>
<input type="hidden" name="usc_action" value="update_account">
<table class="form-table">
<tr>
<th><label for="usc-display-name"><?php esc_html_e('Display name', 'unsupervised-schedular'); ?></label></th>
<td><input type="text" id="usc-display-name" name="display_name" class="regular-text" value="<?php echo esc_attr($student->display_name); ?>" required></td>
</tr>
<tr>
<th><label for="usc-user-email"><?php esc_html_e('Email', 'unsupervised-schedular'); ?></label></th>
<td><input type="email" id="usc-user-email" name="user_email" class="regular-text" value="<?php echo esc_attr($student->user_email); ?>" required></td>
</tr>
<tr>
<th><?php esc_html_e('Registered', 'unsupervised-schedular'); ?></th>
<td><?php echo esc_html($student->user_registered); ?></td>
</tr>
</table>
<?php submit_button(esc_html__('Save account details', 'unsupervised-schedular'), 'secondary', 'submit', false); ?>
</form>
<?php if ($guardian !== null || ! empty($children)) : ?>
<h2><?php esc_html_e('Profile', 'unsupervised-schedular'); ?></h2>
<?php $detailUrl = static fn(int $id): string => add_query_arg(['page' => $pageSlug, 'student_id' => $id], admin_url('admin.php')); ?>
<?php if ($guardian !== null) : ?>
<p>
<?php
printf(
/* translators: 1: linked name of the parent/guardian, 2: their email address. */
esc_html__('Books and pays through %1$s (%2$s).', 'unsupervised-schedular'),
'<a href="' . esc_url($detailUrl($guardian['id'])) . '">' . esc_html($guardian['name']) . '</a>',
esc_html($guardian['email'])
);
?>
</p>
<p class="description"><?php esc_html_e('This is a managed student account: it has no login of its own, and its email address is a placeholder that cannot receive mail.', 'unsupervised-schedular'); ?></p>
<?php endif; ?>
<?php if (! empty($children)) : ?>
<p><?php esc_html_e('Books and pays for:', 'unsupervised-schedular'); ?></p>
<ul class="ul-disc">
<?php foreach ($children as $child) : ?>
<li>
<a href="<?php echo esc_url($detailUrl($child['id'])); ?>"><?php echo esc_html($child['name']); ?></a>
<?php if ($child['date_of_birth'] !== '') : ?>
<span class="description"><?php echo esc_html($child['date_of_birth']); ?></span>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php endif; ?>
<h2><?php esc_html_e('Registration Information', 'unsupervised-schedular'); ?></h2>
<?php if (empty($registrationInfo)) : ?>
<p><?php esc_html_e('No registration questions are configured.', 'unsupervised-schedular'); ?></p>
<?php else : ?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th><?php esc_html_e('Question', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Answer', 'unsupervised-schedular'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($registrationInfo as $row) : ?>
<tr>
<td>
<?php echo esc_html($row['question']); ?>
<?php if ($row['required']) : ?>
<span class="us-required" aria-hidden="true">*</span>
<?php endif; ?>
</td>
<td><?php echo esc_html($row['answer']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php if ($canBilling) : ?>
<h2><?php esc_html_e('Billing method', 'unsupervised-schedular'); ?></h2>
<form method="post">
<?php wp_nonce_field('usc_student_billing'); ?>
<input type="hidden" name="usc_action" value="set_billing">
<select name="payment_method">
<option value="">
<?php
/* translators: %s: the studio default billing method */
echo esc_html(sprintf(__('Studio default (%s)', 'unsupervised-schedular'), $billingDefault));
?>
</option>
<option value="card" <?php selected($billingOverride, 'card'); ?>><?php esc_html_e('Credit card', 'unsupervised-schedular'); ?></option>
<option value="etransfer" <?php selected($billingOverride, 'etransfer'); ?>><?php esc_html_e('E-transfer', 'unsupervised-schedular'); ?></option>
<option value="comp" <?php selected($billingOverride, 'comp'); ?>><?php esc_html_e('Comp (no charge)', 'unsupervised-schedular'); ?></option>
</select>
<?php submit_button(esc_html__('Save', 'unsupervised-schedular'), 'secondary', 'submit', false); ?>
</form>
<?php endif; ?>
<h2><?php esc_html_e('Upcoming lessons', 'unsupervised-schedular'); ?></h2>
<?php $renderLessons($upcoming, true); ?>
<h2><?php esc_html_e('Past lessons', 'unsupervised-schedular'); ?></h2>
<?php $renderLessons($past); ?>
<h2><?php esc_html_e('Group-class enrolments', 'unsupervised-schedular'); ?></h2>
<?php if (empty($enrolments)) : ?>
<p><?php esc_html_e('None.', 'unsupervised-schedular'); ?></p>
<?php else : ?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th><?php esc_html_e('Class', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Status', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Actions', 'unsupervised-schedular'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($enrolments as $enrolment) : ?>
<tr>
<td><?php echo esc_html($enrolment['offering']); ?></td>
<td><?php echo esc_html($enrolment['status']); ?></td>
<td>
<?php if ($enrolment['status'] === 'active') : ?>
<form method="post" style="display:inline">
<?php wp_nonce_field('usc_student_actions'); ?>
<input type="hidden" name="usc_action" value="withdraw_enrollment">
<input type="hidden" name="enrollment_id" value="<?php echo esc_attr((string) $enrolment['id']); ?>">
<button type="submit" class="button-link button-link-delete" onclick="return confirm('<?php echo esc_js(__('Withdraw this student from the class? The seat is freed and any pending payment is voided.', 'unsupervised-schedular')); ?>');">
<?php esc_html_e('Withdraw', 'unsupervised-schedular'); ?>
</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<h2><?php esc_html_e('Policy acceptances', 'unsupervised-schedular'); ?></h2>
<?php if (empty($acceptances)) : ?>
<p><?php esc_html_e('None.', 'unsupervised-schedular'); ?></p>
<?php else : ?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th><?php esc_html_e('Policy', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Version', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Context', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Accepted', 'unsupervised-schedular'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($acceptances as $acceptance) : ?>
<tr>
<td><?php echo esc_html($acceptance['policy']); ?></td>
<td><?php echo esc_html($acceptance['version']); ?></td>
<td><?php echo esc_html($acceptance['context']); ?></td>
<td><?php echo esc_html($acceptance['accepted_at'] !== '' ? (string) mysql2date('M j, Y g:i A', $acceptance['accepted_at']) : '—'); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<h2><?php esc_html_e('Intake answers', 'unsupervised-schedular'); ?></h2>
<?php if (empty($intake)) : ?>
<p><?php esc_html_e('None.', 'unsupervised-schedular'); ?></p>
<?php else : ?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th><?php esc_html_e('Question', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Answer', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Context', 'unsupervised-schedular'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($intake as $row) : ?>
<tr>
<td><?php echo esc_html($row['question']); ?></td>
<td><?php echo esc_html($row['answer']); ?></td>
<td><?php echo esc_html($row['context']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php if ($canBilling) : ?>
<h2><?php esc_html_e('Account credit', 'unsupervised-schedular'); ?></h2>
<p>
<?php
printf(
/* translators: %s: available credit balance, e.g. "45.00 CAD" */
esc_html__('Available balance: %s', 'unsupervised-schedular'),
'<strong>' . esc_html(number_format_i18n($creditBalance, 2) . ' ' . $creditCurrency) . '</strong>'
);
?>
<?php if ($payer['id'] !== (int) $student->ID) : ?>
<span class="description">
<?php
printf(
/* translators: %s: name of the parent/guardian whose account holds the balance. */
esc_html__('Held on %ss account — the profile shares one balance.', 'unsupervised-schedular'),
esc_html($payer['name'])
);
?>
</span>
<?php endif; ?>
<span class="description"><?php esc_html_e('Credit from cancelled paid lessons is applied automatically to upcoming scheduled billing.', 'unsupervised-schedular'); ?></span>
</p>
<?php if (! empty($credits)) : ?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th><?php esc_html_e('Date', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Reason', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Amount', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Remaining', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Status', 'unsupervised-schedular'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($credits as $credit) : ?>
<tr>
<td><?php echo esc_html($credit['created_at'] !== '' ? (string) mysql2date('M j, Y g:i A', $credit['created_at']) : '—'); ?></td>
<td><?php echo esc_html($credit['reason']); ?></td>
<td><?php echo esc_html(number_format_i18n($credit['amount'], 2) . ' ' . $credit['currency']); ?></td>
<td><?php echo esc_html(number_format_i18n($credit['remaining'], 2) . ' ' . $credit['currency']); ?></td>
<td><?php echo esc_html($credit['status']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<h2><?php esc_html_e('Payment history', 'unsupervised-schedular'); ?></h2>
<?php if (empty($payments)) : ?>
<p><?php esc_html_e('None.', 'unsupervised-schedular'); ?></p>
<?php else : ?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th><?php esc_html_e('Date', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Context', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Method', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Status', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Subtotal', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('HST', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Total', 'unsupervised-schedular'); ?></th>
<th><?php esc_html_e('Receipt', 'unsupervised-schedular'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($payments as $payment) : ?>
<tr>
<td><?php echo esc_html($payment['created_at'] !== '' ? (string) mysql2date('M j, Y g:i A', $payment['created_at']) : '—'); ?></td>
<td><?php echo esc_html($payment['context']); ?></td>
<td><?php echo esc_html($payment['method']); ?></td>
<td><?php echo esc_html($payment['status']); ?></td>
<td><?php echo esc_html(number_format_i18n($payment['amount'], 2)); ?></td>
<td><?php echo esc_html(number_format_i18n($payment['tax_amount'], 2)); ?></td>
<td><?php echo esc_html(number_format_i18n($payment['total'], 2) . ' ' . $payment['currency']); ?></td>
<td><?php echo esc_html($payment['receipt']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php endif; ?>
</div>