CI / Tests (PHP 8.1) (pull_request) Successful in 43s
CI / No Debug Code (pull_request) Successful in 2s
CI / Tests (PHP 8.2) (pull_request) Successful in 50s
CI / PHPStan (pull_request) Successful in 2m55s
CI / Coding Standards (pull_request) Successful in 3m0s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m40s
CI / Build Plugin Zip (pull_request) Skipped
Signup and the profile page now ask for a four-digit year between 1900 and the current year. Anything else — a short year, a full date, a year in the future — is discarded rather than stored, so a typo cannot leave a nonsense age on the record. The year lives in a new us_birth_year user meta rather than reusing us_date_of_birth, which would have left one key holding two formats. The old key is not migrated in bulk. Instead GuardianService handles it in two halves: birthYear() falls back to the year of the old date when the new key is absent, so a student added before this change still shows one, and setBirthYear() deletes the old date on every save. That deletion is what makes the fallback safe rather than merely tidy. Without it, clearing the birth year on a student who predates the change would leave the old date behind for the fallback to read straight back, and the year could never be cleared at all. Stored in user meta, so no Schema.php change and no USC_VERSION bump. Closes #147 Co-Authored-By: Claude Opus 5 <[email protected]>
81 lines
3.5 KiB
PHP
81 lines
3.5 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
if (! defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* @var list<array{id: int, name: string, email: string, registered: string, upcoming: int, enrolments: int, guardian: array{id: int, name: string, email: string}|null, children: list<array{id: int, name: string, birth_year: string, relationship: string}>}> $students
|
|
* @var string $pageSlug
|
|
*/
|
|
|
|
/**
|
|
* The Family cell: for a child, the guardian who books for them; for a guardian,
|
|
* the children they book for. Both link to the other person's detail screen so
|
|
* an admin can move between a family without going back to the list.
|
|
*/
|
|
$familyCell = static function (array $student) use ($pageSlug): string {
|
|
$link = static fn(int $id, string $name): string => sprintf(
|
|
'<a href="%s">%s</a>',
|
|
esc_url(add_query_arg(['page' => $pageSlug, 'student_id' => $id], admin_url('admin.php'))),
|
|
esc_html($name)
|
|
);
|
|
|
|
if ($student['guardian'] !== null) {
|
|
return sprintf(
|
|
/* translators: %s: linked name of the parent/guardian who books for this student. */
|
|
esc_html__('Managed by %s', 'unsupervised-schedular'),
|
|
$link($student['guardian']['id'], $student['guardian']['name'])
|
|
);
|
|
}
|
|
|
|
if ($student['children'] === []) {
|
|
return '—';
|
|
}
|
|
|
|
return implode(', ', array_map(
|
|
static fn(array $child): string => $link($child['id'], $child['name']),
|
|
$student['children']
|
|
));
|
|
};
|
|
?>
|
|
<div class="wrap">
|
|
<h1><?php esc_html_e('Students', 'unsupervised-schedular'); ?></h1>
|
|
|
|
<?php if (empty($students)) : ?>
|
|
<p><?php esc_html_e('No students yet.', 'unsupervised-schedular'); ?></p>
|
|
<?php else : ?>
|
|
<table class="wp-list-table widefat fixed striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php esc_html_e('Name', 'unsupervised-schedular'); ?></th>
|
|
<th><?php esc_html_e('Email', 'unsupervised-schedular'); ?></th>
|
|
<th><?php esc_html_e('Profile', 'unsupervised-schedular'); ?></th>
|
|
<th><?php esc_html_e('Registered', 'unsupervised-schedular'); ?></th>
|
|
<th><?php esc_html_e('Upcoming lessons', 'unsupervised-schedular'); ?></th>
|
|
<th><?php esc_html_e('Active enrolments', 'unsupervised-schedular'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($students as $student) : ?>
|
|
<?php $detailUrl = add_query_arg(['page' => $pageSlug, 'student_id' => $student['id']], admin_url('admin.php')); ?>
|
|
<tr>
|
|
<td><a href="<?php echo esc_url($detailUrl); ?>"><?php echo esc_html($student['name']); ?></a></td>
|
|
<td><?php echo esc_html($student['email']); ?></td>
|
|
<td>
|
|
<?php
|
|
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $familyCell escapes every name and URL it interpolates.
|
|
echo $familyCell($student);
|
|
?>
|
|
</td>
|
|
<td><?php echo esc_html($student['registered']); ?></td>
|
|
<td><?php echo esc_html((string) $student['upcoming']); ?></td>
|
|
<td><?php echo esc_html((string) $student['enrolments']); ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php endif; ?>
|
|
</div>
|