fix registration enrollment of students
Deploy to Shared Hosting / Shared hosting deploy (push) Failing after 48s
Tests / PHPUnit (push) Successful in 1m19s

This commit is contained in:
root
2026-08-26 21:36:03 -04:00
parent a354d78fa9
commit 9676261d65
7 changed files with 208 additions and 53 deletions
+47 -5
View File
@@ -380,6 +380,7 @@ class ParentController extends BaseController
} }
unset($student); unset($student);
$this->ensureStudentYearStatusRows($students, $selectedYear);
service('studentYearStatus')->attachToStudents($students, $selectedYear); service('studentYearStatus')->attachToStudents($students, $selectedYear);
foreach ($students as &$student) { foreach ($students as &$student) {
$studentId = (int) ($student['id'] ?? 0); $studentId = (int) ($student['id'] ?? 0);
@@ -528,7 +529,7 @@ class ParentController extends BaseController
} }
$studentName = trim((string) ($studentInfo['firstname'] ?? '') . ' ' . (string) ($studentInfo['lastname'] ?? '')) ?: 'Student ID ' . $studentId; $studentName = trim((string) ($studentInfo['firstname'] ?? '') . ' ' . (string) ($studentInfo['lastname'] ?? '')) ?: 'Student ID ' . $studentId;
if (empty($evaluation['can_enroll'])) { if (empty($evaluation['can_enroll']) && empty($evaluation['parent_enrollment_allowed'])) {
$transitionService->logEnrollmentBlock($evaluation, 'parent_enroll_submit', (int) $parentId, (int) $parentId); $transitionService->logEnrollmentBlock($evaluation, 'parent_enroll_submit', (int) $parentId, (int) $parentId);
$messages = array_values(array_filter(array_map('trim', array_map('strval', $evaluation['blockers'] ?? [])))); $messages = array_values(array_filter(array_map('trim', array_map('strval', $evaluation['blockers'] ?? []))));
$codes = array_values(array_filter(array_map('strval', array_merge($evaluation['blocking_rule_codes'] ?? [], $evaluation['review_rule_codes'] ?? [])))); $codes = array_values(array_filter(array_map('strval', array_merge($evaluation['blocking_rule_codes'] ?? [], $evaluation['review_rule_codes'] ?? []))));
@@ -1951,6 +1952,7 @@ class ParentController extends BaseController
if ($this->hasSettledParentEnrollmentStatus($existingStatus, $existingAdmissionStatus)) { if ($this->hasSettledParentEnrollmentStatus($existingStatus, $existingAdmissionStatus)) {
$payload[] = [ $payload[] = [
'student_id' => $studentId, 'student_id' => $studentId,
'student_name' => trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? '')) ?: 'Student ID ' . $studentId,
'can_enroll' => false, 'can_enroll' => false,
'primary_block_reason' => 'ALREADY_ENROLLED', 'primary_block_reason' => 'ALREADY_ENROLLED',
'primary_parent_message' => EnrollmentEligibility::alreadyEnrolledMessage($existingStatus), 'primary_parent_message' => EnrollmentEligibility::alreadyEnrolledMessage($existingStatus),
@@ -1969,7 +1971,10 @@ class ParentController extends BaseController
); );
$payload[] = [ $payload[] = [
'student_id' => $studentId, 'student_id' => $studentId,
'student_name' => trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? '')) ?: 'Student ID ' . $studentId,
'can_enroll' => (bool) ($evaluation['can_enroll'] ?? false), 'can_enroll' => (bool) ($evaluation['can_enroll'] ?? false),
'parent_enrollment_allowed' => (bool) ($evaluation['parent_enrollment_allowed'] ?? $evaluation['can_enroll'] ?? false),
'first_enrollment' => (bool) ($evaluation['first_enrollment'] ?? false),
'primary_block_reason' => $evaluation['primary_block_reason'] ?? null, 'primary_block_reason' => $evaluation['primary_block_reason'] ?? null,
'primary_parent_message' => $evaluation['primary_parent_message'] ?? null, 'primary_parent_message' => $evaluation['primary_parent_message'] ?? null,
'blocking_rule_codes' => array_values(array_map('strval', $evaluation['blocking_rule_codes'] ?? [])), 'blocking_rule_codes' => array_values(array_map('strval', $evaluation['blocking_rule_codes'] ?? [])),
@@ -2008,7 +2013,10 @@ class ParentController extends BaseController
foreach ($students as $student) { foreach ($students as $student) {
$evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : []; $evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : [];
if (($evaluation['can_enroll'] ?? false) !== true) { if (
($evaluation['can_enroll'] ?? false) !== true
&& ($evaluation['parent_enrollment_allowed'] ?? false) !== true
) {
continue; continue;
} }
@@ -2123,7 +2131,7 @@ class ParentController extends BaseController
'parent' 'parent'
); );
if (($evaluation['can_enroll'] ?? false) === true) { if (($evaluation['can_enroll'] ?? false) === true || ($evaluation['parent_enrollment_allowed'] ?? false) === true) {
return; return;
} }
@@ -2800,6 +2808,7 @@ class ParentController extends BaseController
} }
unset($kid); unset($kid);
$this->ensureStudentYearStatusRows($kids, $selectedSchoolYear);
service('studentYearStatus')->attachToStudents($kids, $selectedSchoolYear); service('studentYearStatus')->attachToStudents($kids, $selectedSchoolYear);
foreach ($kids as &$kid) { foreach ($kids as &$kid) {
@@ -2821,6 +2830,33 @@ class ParentController extends BaseController
]; ];
} }
/**
* @param list<array<string, mixed>> $students
*/
private function ensureStudentYearStatusRows(array $students, string $schoolYear): void
{
$schoolYear = trim($schoolYear);
if ($students === [] || ! preg_match('/^\d{4}-\d{4}$/', $schoolYear)) {
return;
}
$studentYearStatus = service('studentYearStatus');
foreach ($students as $student) {
$studentId = (int) ($student['id'] ?? $student['student_id'] ?? 0);
if ($studentId <= 0 || $studentYearStatus->hasStatus($studentId, $schoolYear)) {
continue;
}
$isNew = (int) ($student['is_new'] ?? 1) === 1;
if (! $studentYearStatus->upsert($studentId, $schoolYear, $isNew)) {
log_message('error', 'Unable to repair student_year_status for student_id={studentId}, school_year={schoolYear}', [
'studentId' => $studentId,
'schoolYear' => $schoolYear,
]);
}
}
}
private function validateAndSaveOrUpdateStudent($idx, $parentId, $semester, $schoolYear, $schoolIdService, $isNew = null, $studentId = null) private function validateAndSaveOrUpdateStudent($idx, $parentId, $semester, $schoolYear, $schoolIdService, $isNew = null, $studentId = null)
{ {
$firstName = $this->request->getPost('studentFirstName')[$idx] ?? null; $firstName = $this->request->getPost('studentFirstName')[$idx] ?? null;
@@ -2883,8 +2919,10 @@ class ParentController extends BaseController
'photo_consent' => $photoConsent, 'photo_consent' => $photoConsent,
'parent_id' => (int) $parentId, 'parent_id' => (int) $parentId,
'year_of_registration' => date('Y'), 'year_of_registration' => date('Y'),
'school_year' => $schoolYear,
]; ];
if ($this->db->fieldExists('school_year', 'students')) {
$studentData['school_year'] = $schoolYear;
}
if (!is_null($isNew)) { if (!is_null($isNew)) {
$studentData['is_new'] = $isNew ? 1 : 0; $studentData['is_new'] = $isNew ? 1 : 0;
@@ -2950,7 +2988,11 @@ class ParentController extends BaseController
} }
if (! is_null($isNew) && (int) $studentId > 0) { if (! is_null($isNew) && (int) $studentId > 0) {
service('studentYearStatus')->upsert((int) $studentId, (string) $schoolYear, (bool) $isNew); $studentYearStatus = service('studentYearStatus');
$statusSaved = $studentYearStatus->upsert((int) $studentId, (string) $schoolYear, (bool) $isNew);
if (! $statusSaved || ! $studentYearStatus->hasStatus((int) $studentId, (string) $schoolYear)) {
throw new \RuntimeException('Student year status could not be saved for student ID ' . (int) $studentId . ' and school year ' . (string) $schoolYear . '.');
}
} }
// ---------- SAVE MEDICAL CONDITIONS ---------- // ---------- SAVE MEDICAL CONDITIONS ----------
-1
View File
@@ -22,7 +22,6 @@ class StudentModel extends Model
'tuition_paid', 'tuition_paid',
'year_of_registration', 'year_of_registration',
'rfid_tag', 'rfid_tag',
'school_year',
'is_active' 'is_active'
]; ];
protected $useTimestamps = false; protected $useTimestamps = false;
@@ -302,7 +302,7 @@ final class EnrollmentRegistrationEmailService
return 'KG students may complete registration now. ' . $name . ' is eligible for re-enrollment even though no final deliberation decision is recorded.' . $placementText; return 'KG students may complete registration now. ' . $name . ' is eligible for re-enrollment even though no final deliberation decision is recorded.' . $placementText;
} }
if (($evaluation['can_enroll'] ?? false) === false && ! empty($evaluation['primary_parent_message'])) { if (! $this->canCompleteParentReEnrollment($evaluation) && ! empty($evaluation['primary_parent_message'])) {
return (string) $evaluation['primary_parent_message']; return (string) $evaluation['primary_parent_message'];
} }
@@ -655,7 +655,7 @@ final class EnrollmentRegistrationEmailService
return $placement; return $placement;
} }
return ($evaluation['can_enroll'] ?? false) === true ? 'Pending' : ''; return $this->canCompleteParentReEnrollment($evaluation) ? 'Pending' : '';
} }
private function requiredAction(array $evaluation, string $deadline, string $name = 'The student'): string private function requiredAction(array $evaluation, string $deadline, string $name = 'The student'): string
+29 -13
View File
@@ -707,7 +707,35 @@ final class EnrollmentTransitionService
} }
} }
return trim((string) ($student['school_year'] ?? '')) === $targetSchoolYear; if ($this->db->fieldExists('school_year', 'students')) {
return trim((string) ($student['school_year'] ?? '')) === $targetSchoolYear;
}
return ! $this->studentHasPriorSchoolHistory($studentId, $targetSchoolYear);
}
private function studentHasPriorSchoolHistory(int $studentId, string $targetSchoolYear): bool
{
if ($studentId <= 0) {
return false;
}
foreach (['student_class', 'enrollments', 'student_decisions'] as $table) {
if (! $this->db->tableExists($table) || ! $this->db->fieldExists('school_year', $table)) {
continue;
}
$count = $this->db->table($table)
->where('student_id', $studentId)
->where('school_year !=', $targetSchoolYear)
->countAllResults();
if ($count > 0) {
return true;
}
}
return false;
} }
private function schoolYearByName(string $schoolYear): ?array private function schoolYearByName(string $schoolYear): ?array
@@ -2009,19 +2037,7 @@ final class EnrollmentTransitionService
private function applyHouseholdLastNameRule(array &$evaluation, int $parentId, ?string $sourceSchoolYear = null): void private function applyHouseholdLastNameRule(array &$evaluation, int $parentId, ?string $sourceSchoolYear = null): void
{ {
if (! empty($evaluation['first_enrollment'])) {
$evaluation['family_name_check_ok'] = true;
return;
}
$students = $this->linkedStudentsForParent($parentId); $students = $this->linkedStudentsForParent($parentId);
$sourceSchoolYear = trim((string) $sourceSchoolYear);
if ($sourceSchoolYear !== '') {
$students = array_values(array_filter(
$students,
fn (array $student): bool => $this->sourceAssignment((int) ($student['id'] ?? 0), $sourceSchoolYear) !== null
));
}
if (count($students) <= 1) { if (count($students) <= 1) {
$evaluation['family_name_check_ok'] = true; $evaluation['family_name_check_ok'] = true;
+22 -6
View File
@@ -40,15 +40,15 @@ class StudentYearStatusService
return (int) ($row['is_new'] ?? 1) === 1; return (int) ($row['is_new'] ?? 1) === 1;
} }
public function upsert(int $studentId, string $schoolYear, bool $isNew): void public function upsert(int $studentId, string $schoolYear, bool $isNew): bool
{ {
if ($studentId <= 0) { if ($studentId <= 0) {
return; return false;
} }
$schoolYear = trim($schoolYear); $schoolYear = trim($schoolYear);
if (! preg_match('/^\d{4}-\d{4}$/', $schoolYear) || ! $this->db->tableExists('student_year_status')) { if (! preg_match('/^\d{4}-\d{4}$/', $schoolYear) || ! $this->db->tableExists('student_year_status')) {
return; return false;
} }
$flag = $isNew ? 1 : 0; $flag = $isNew ? 1 : 0;
@@ -59,19 +59,35 @@ class StudentYearStatusService
->first(); ->first();
if (is_array($existing) && isset($existing['id'])) { if (is_array($existing) && isset($existing['id'])) {
$this->yearStatusModel->update((int) $existing['id'], [ return (bool) $this->yearStatusModel->update((int) $existing['id'], [
'is_new' => $flag, 'is_new' => $flag,
]); ]);
return;
} }
$this->yearStatusModel->insert([ return (bool) $this->yearStatusModel->insert([
'student_id' => $studentId, 'student_id' => $studentId,
'school_year' => $schoolYear, 'school_year' => $schoolYear,
'is_new' => $flag, 'is_new' => $flag,
]); ]);
} }
public function hasStatus(int $studentId, string $schoolYear): bool
{
if ($studentId <= 0) {
return false;
}
$schoolYear = trim($schoolYear);
if (! preg_match('/^\d{4}-\d{4}$/', $schoolYear) || ! $this->db->tableExists('student_year_status')) {
return false;
}
return $this->yearStatusModel
->where('student_id', $studentId)
->where('school_year', $schoolYear)
->countAllResults() > 0;
}
/** /**
* Mark students who existed in a source school year as returning in the target year. * Mark students who existed in a source school year as returning in the target year.
* *
+97 -23
View File
@@ -281,7 +281,11 @@ $enrollableCount = 0;
$withdrawableCount = 0; $withdrawableCount = 0;
foreach (($students ?? []) as $student) { foreach (($students ?? []) as $student) {
$evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : []; $evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : [];
if (($evaluation['can_enroll'] ?? false) === true && !$deadlinePassed && $isEditable) { if (
(($evaluation['can_enroll'] ?? false) === true || ($evaluation['parent_enrollment_allowed'] ?? false) === true)
&& !$deadlinePassed
&& $isEditable
) {
$enrollableCount++; $enrollableCount++;
} }
if (($student['enrollment_status'] ?? '') === 'enrolled' && $isEditable) { if (($student['enrollment_status'] ?? '') === 'enrolled' && $isEditable) {
@@ -386,7 +390,7 @@ $studentCount = count($students ?? []);
$evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : []; $evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : [];
if ($hasSettledEnrollmentStatus($student['enrollment_status'] ?? '', $student['admission_status'] ?? '')) { if ($hasSettledEnrollmentStatus($student['enrollment_status'] ?? '', $student['admission_status'] ?? '')) {
echo '<span class="text-muted small">' . esc($alreadyEnrolledMessage((string) ($student['enrollment_status'] ?? ''))) . '</span>'; echo '<span class="text-muted small">' . esc($alreadyEnrolledMessage((string) ($student['enrollment_status'] ?? ''))) . '</span>';
} elseif (($evaluation['can_enroll'] ?? false) === true) { } elseif (($evaluation['can_enroll'] ?? false) === true || ($evaluation['parent_enrollment_allowed'] ?? false) === true) {
echo '<span class="text-success small">Eligible</span>'; echo '<span class="text-success small">Eligible</span>';
} elseif (! empty($evaluation['primary_parent_message'])) { } elseif (! empty($evaluation['primary_parent_message'])) {
echo '<span class="text-danger small">' . esc($evaluation['primary_parent_message']) . '</span>'; echo '<span class="text-danger small">' . esc($evaluation['primary_parent_message']) . '</span>';
@@ -510,13 +514,20 @@ $studentCount = count($students ?? []);
$evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : []; $evaluation = is_array($student['transition_evaluation'] ?? null) ? $student['transition_evaluation'] : [];
$studentName = trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? '')); $studentName = trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? ''));
$studentName = $studentName !== '' ? $studentName : 'Student'; $studentName = $studentName !== '' ? $studentName : 'Student';
$canEnroll = ($evaluation['can_enroll'] ?? false) === true $studentId = (int) ($student['id'] ?? 0);
$isPreselectedStudent = in_array($studentId, array_map('intval', $preselectedStudentIds ?? []), true);
$canEnroll = (
($evaluation['can_enroll'] ?? false) === true
|| ($evaluation['parent_enrollment_allowed'] ?? false) === true
)
&& !$deadlinePassed && !$deadlinePassed
&& $isEditable; && $isEditable;
$hasSettledEnrollment = $hasSettledEnrollmentStatus($student['enrollment_status'] ?? '', $student['admission_status'] ?? ''); $hasSettledEnrollment = $hasSettledEnrollmentStatus($student['enrollment_status'] ?? '', $student['admission_status'] ?? '');
$blockMessage = $hasSettledEnrollment $blockMessage = $canEnroll
? ''
: ($hasSettledEnrollment
? $alreadyEnrolledMessage((string) ($student['enrollment_status'] ?? '')) ? $alreadyEnrolledMessage((string) ($student['enrollment_status'] ?? ''))
: (string) ($evaluation['primary_parent_message'] ?? $eligibilityMessage['message'] ?? ''); : (string) ($evaluation['primary_parent_message'] ?? $eligibilityMessage['message'] ?? ''));
$blockTitle = $hasSettledEnrollment $blockTitle = $hasSettledEnrollment
? $alreadyEnrolledTitle((string) ($student['enrollment_status'] ?? '')) ? $alreadyEnrolledTitle((string) ($student['enrollment_status'] ?? ''))
: 'Enrollment Not Available'; : 'Enrollment Not Available';
@@ -547,6 +558,8 @@ $studentCount = count($students ?? []);
<div class="student-select-card <?= $canEnroll ? '' : 'is-disabled' ?>" <div class="student-select-card <?= $canEnroll ? '' : 'is-disabled' ?>"
data-student-card data-student-card
data-student-id="<?= esc($student['id']) ?>" data-student-id="<?= esc($student['id']) ?>"
data-student-name="<?= esc($studentName) ?>"
data-requested-student="<?= $isPreselectedStudent ? '1' : '0' ?>"
data-school-id="<?= esc($student['school_id'] ?? 'N/A') ?>" data-school-id="<?= esc($student['school_id'] ?? 'N/A') ?>"
data-current-grade="<?= esc($gradeLabel) ?>" data-current-grade="<?= esc($gradeLabel) ?>"
data-required-action="<?= esc($student['required_action_label'] ?? 'Contact administration') ?>" data-required-action="<?= esc($student['required_action_label'] ?? 'Contact administration') ?>"
@@ -935,6 +948,11 @@ $studentCount = count($students ?? []);
const selectedEnrollInputs = () => form ? Array.from(form.querySelectorAll("input[name='enroll[]']:checked")) : []; const selectedEnrollInputs = () => form ? Array.from(form.querySelectorAll("input[name='enroll[]']:checked")) : [];
const selectedWithdrawInputs = () => form ? Array.from(form.querySelectorAll("input[name='withdraw[]']:checked")) : []; const selectedWithdrawInputs = () => form ? Array.from(form.querySelectorAll("input[name='withdraw[]']:checked")) : [];
const studentCards = () => Array.from(document.querySelectorAll('[data-student-card]'));
const requestedStudentCards = () => {
const requested = studentCards().filter(card => card.dataset.requestedStudent === '1');
return requested.length ? requested : studentCards();
};
function setStep(step) { function setStep(step) {
currentStep = Math.max(0, Math.min(finalStep, step)); currentStep = Math.max(0, Math.min(finalStep, step));
@@ -1214,7 +1232,8 @@ $studentCount = count($students ?? []);
function selectStudentsForEnrollment(ids) { function selectStudentsForEnrollment(ids) {
const wanted = Array.isArray(ids) ? ids.map(Number).filter(id => id > 0) : []; const wanted = Array.isArray(ids) ? ids.map(Number).filter(id => id > 0) : [];
document.querySelectorAll('[data-student-card]').forEach(card => { const cards = wanted.length ? requestedStudentCards() : studentCards();
cards.forEach(card => {
if (card.dataset.selectable !== '1') { if (card.dataset.selectable !== '1') {
return; return;
} }
@@ -1340,7 +1359,7 @@ $studentCount = count($students ?? []);
return true; return true;
} }
document.querySelectorAll('[data-student-card]').forEach(card => { studentCards().forEach(card => {
card.querySelectorAll('[data-health-group]').forEach(group => { card.querySelectorAll('[data-health-group]').forEach(group => {
group.addEventListener('change', function(event) { group.addEventListener('change', function(event) {
const target = event.target; const target = event.target;
@@ -1372,17 +1391,41 @@ $studentCount = count($students ?? []);
function showBlockModal(message, options = {}) { function showBlockModal(message, options = {}) {
const alreadyEnrolled = options.alreadyEnrolled === true; const alreadyEnrolled = options.alreadyEnrolled === true;
const warning = options.warning === true;
if (blockModalTitle) { if (blockModalTitle) {
blockModalTitle.textContent = options.title || (alreadyEnrolled ? 'Already Enrolled' : 'Enrollment Not Available'); blockModalTitle.textContent = options.title || (alreadyEnrolled ? 'Already Enrolled' : 'Enrollment Not Available');
} }
if (blockModalHeader) { if (blockModalHeader) {
blockModalHeader.classList.toggle('bg-danger', !alreadyEnrolled); blockModalHeader.classList.toggle('bg-danger', !alreadyEnrolled && !warning);
blockModalHeader.classList.toggle('bg-success', alreadyEnrolled); blockModalHeader.classList.toggle('bg-success', alreadyEnrolled && !warning);
blockModalHeader.classList.toggle('bg-warning', warning);
blockModalHeader.classList.toggle('text-dark', warning);
blockModalHeader.classList.toggle('text-white', !warning);
} }
if (blockModalBody) { if (blockModalBody) {
blockModalBody.textContent = message || (alreadyEnrolled const messages = Array.isArray(options.messages) ? options.messages.filter(Boolean) : [];
? 'This student is already enrolled for the selected school year.' if (messages.length > 1) {
: 'Enrollment cannot continue at this time. Please contact school administration.'); const list = document.createElement('ul');
list.className = 'mb-0 ps-4';
messages.forEach(item => {
const li = document.createElement('li');
const separatorIndex = item.indexOf(': ');
if (separatorIndex > 0) {
const name = document.createElement('strong');
name.textContent = item.substring(0, separatorIndex);
li.appendChild(name);
li.appendChild(document.createTextNode(item.substring(separatorIndex)));
} else {
li.textContent = item;
}
list.appendChild(li);
});
blockModalBody.replaceChildren(list);
} else {
blockModalBody.textContent = messages[0] || message || (alreadyEnrolled
? 'This student is already enrolled for the selected school year.'
: 'Enrollment cannot continue at this time. Please contact school administration.');
}
} }
if (blockModal) { if (blockModal) {
blockModal.show(); blockModal.show();
@@ -1400,6 +1443,24 @@ $studentCount = count($students ?? []);
}; };
} }
function blockMessagesForCards(cards) {
return cards
.map(card => {
const studentId = String(card.dataset.studentId || '');
const row = latestEligibility[studentId] || {};
const message = row.primary_parent_message
|| (row.blocking_rule_codes || []).join(', ')
|| card.dataset.blockMessage
|| '';
if (!message) {
return '';
}
const name = row.student_name || card.dataset.studentName || 'Student';
return name + ': ' + message;
})
.filter(Boolean);
}
async function refreshEligibility() { async function refreshEligibility() {
const response = await fetch(eligibilityRefreshUrl, { const response = await fetch(eligibilityRefreshUrl, {
headers: { 'X-Requested-With': 'XMLHttpRequest' }, headers: { 'X-Requested-With': 'XMLHttpRequest' },
@@ -1426,7 +1487,7 @@ $studentCount = count($students ?? []);
function applyEligibilityToCards() { function applyEligibilityToCards() {
let shouldReload = false; let shouldReload = false;
document.querySelectorAll('[data-student-card]').forEach(card => { studentCards().forEach(card => {
const studentId = String(card.dataset.studentId || ''); const studentId = String(card.dataset.studentId || '');
const row = latestEligibility[studentId]; const row = latestEligibility[studentId];
if (!row) { if (!row) {
@@ -1434,7 +1495,7 @@ $studentCount = count($students ?? []);
} }
const wasBlocked = card.dataset.selectable !== '1'; const wasBlocked = card.dataset.selectable !== '1';
const canEnroll = row.can_enroll === true && !deadlinePassed; const canEnroll = (row.can_enroll === true || row.parent_enrollment_allowed === true) && !deadlinePassed;
card.dataset.selectable = canEnroll ? '1' : '0'; card.dataset.selectable = canEnroll ? '1' : '0';
card.classList.toggle('is-disabled', !canEnroll); card.classList.toggle('is-disabled', !canEnroll);
if (row.primary_parent_message) { if (row.primary_parent_message) {
@@ -1468,19 +1529,29 @@ $studentCount = count($students ?? []);
return; return;
} }
const eligibleCards = Array.from(document.querySelectorAll('[data-student-card]')).filter(card => { const cardsToConsider = requestedStudentCards();
const eligibleCards = cardsToConsider.filter(card => {
const studentId = String(card.dataset.studentId || ''); const studentId = String(card.dataset.studentId || '');
return latestEligibility[studentId]?.can_enroll === true; const row = latestEligibility[studentId] || {};
return row.can_enroll === true || row.parent_enrollment_allowed === true;
}); });
if (eligibleCards.length === 0) { if (eligibleCards.length === 0) {
const firstBlocked = Object.values(latestEligibility).find(row => row.primary_parent_message || (row.blocking_rule_codes || []).length > 0); const otherBlockedCards = studentCards().filter(card => !cardsToConsider.includes(card));
const blockedMessages = blockMessagesForCards(cardsToConsider)
.concat(blockMessagesForCards(otherBlockedCards));
const firstBlocked = cardsToConsider
.map(card => latestEligibility[String(card.dataset.studentId || '')])
.find(row => row && (row.primary_parent_message || (row.blocking_rule_codes || []).length > 0))
|| Object.values(latestEligibility).find(row => row.primary_parent_message || (row.blocking_rule_codes || []).length > 0);
const alreadyEnrolled = firstBlocked?.decision === 'ALREADY_ENROLLED'; const alreadyEnrolled = firstBlocked?.decision === 'ALREADY_ENROLLED';
const blockReason = firstBlocked?.primary_parent_message const blockReason = firstBlocked?.primary_parent_message
|| (firstBlocked?.blocking_rule_codes || []).join(', ') || (firstBlocked?.blocking_rule_codes || []).join(', ')
|| 'No students are currently eligible for enrollment.'; || 'No students are currently eligible for enrollment.';
showBlockModal(blockReason, { showBlockModal(blockReason, {
alreadyEnrolled, alreadyEnrolled,
messages: blockedMessages,
warning: alreadyEnrolled && blockedMessages.length > 1,
title: firstBlocked?.block_title || (alreadyEnrolled ? 'Already Enrolled' : 'Enrollment Not Available'), title: firstBlocked?.block_title || (alreadyEnrolled ? 'Already Enrolled' : 'Enrollment Not Available'),
}); });
return; return;
@@ -1491,7 +1562,7 @@ $studentCount = count($students ?? []);
}); });
} }
document.querySelectorAll('[data-student-card]').forEach(card => { studentCards().forEach(card => {
card.addEventListener('click', async function(event) { card.addEventListener('click', async function(event) {
if (event.target.closest('input, select, textarea, label')) { if (event.target.closest('input, select, textarea, label')) {
return; return;
@@ -1645,11 +1716,14 @@ $studentCount = count($students ?? []);
}); });
}); });
if (enrollmentStartStep >= 2 && startButton && !startButton.disabled && !<?= $flashError ? 'true' : 'false' ?>) { refreshEligibility().then(() => {
openEnrollmentAtStep(enrollmentStartStep - 1); if (enrollmentStartStep >= 2 && startButton && !startButton.disabled && !<?= $flashError ? 'true' : 'false' ?>) {
} openEnrollmentAtStep(enrollmentStartStep - 1);
}
refreshEligibility().catch(() => { }).catch(() => {
if (enrollmentStartStep >= 2 && startButton && !startButton.disabled && !<?= $flashError ? 'true' : 'false' ?>) {
openEnrollmentAtStep(enrollmentStartStep - 1);
}
// Keep server-rendered eligibility if refresh fails. // Keep server-rendered eligibility if refresh fails.
}); });
}); });
+11 -3
View File
@@ -100,14 +100,22 @@ $disableEmergencyBtn = count($emergencies) >= $maxEmergency;
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php <?php
// Check if at least one student is not enrolled
$hasUnenrolled = false; $hasUnenrolled = false;
$unenrolledStudentIds = [];
foreach ($existingKids as $kid) { foreach ($existingKids as $kid) {
if ($kid['enrollment'] == 0) { if ($kid['enrollment'] == 0) {
$hasUnenrolled = true; $hasUnenrolled = true;
break; $studentId = (int) ($kid['id'] ?? 0);
if ($studentId > 0) {
$unenrolledStudentIds[] = $studentId;
}
} }
} }
usort($unenrolledStudentIds, static fn (int $a, int $b): int => $b <=> $a);
$enrollmentUrl = base_url('/parent/enroll_classes?' . http_build_query([
'start' => 2,
'students' => implode(',', $unenrolledStudentIds),
]));
?> ?>
<?php if ($hasUnenrolled): ?> <?php if ($hasUnenrolled): ?>
@@ -118,7 +126,7 @@ $disableEmergencyBtn = count($emergencies) >= $maxEmergency;
<p class="mb-3"> <p class="mb-3">
Please complete their enrollment using the button below to ensure they are able to attend classes. Please complete their enrollment using the button below to ensure they are able to attend classes.
</p> </p>
<a href="<?= base_url('/parent/enroll_classes') ?>" <a href="<?= esc($enrollmentUrl) ?>"
class="btn btn-lg btn-success bi bi-pencil-square" class="btn btn-lg btn-success bi bi-pencil-square"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-placement="bottom"