fix school year and related issues
Tests / PHPUnit (push) Failing after 34s

This commit is contained in:
root
2026-07-30 00:48:54 -04:00
parent f8f00c70c8
commit 81a72a4c59
27 changed files with 1512 additions and 52 deletions
+191
View File
@@ -25,9 +25,88 @@ $deadlineISO = $deadlineObj->format('Y-m-d\TH:i:sP'); // for JS
</ul>
</div>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<?php $hasAcceptedSchoolPolicy = (bool) ($hasAcceptedSchoolPolicy ?? false); ?>
<?php
$fallMakeupExamLabel = !empty($fallMakeupExamOn) ? local_date($fallMakeupExamOn, 'm-d-Y') : null;
$decisionMessageForStudent = static function (array $student) use ($fallMakeupExamLabel): array {
$decisionRow = is_array($student['previous_year_decision'] ?? null) ? $student['previous_year_decision'] : [];
$decision = trim((string) ($decisionRow['decision'] ?? ''));
$source = strtolower(trim((string) ($decisionRow['source'] ?? '')));
$decisionKey = strtolower($decision);
$name = trim((string) ($student['firstname'] ?? 'Your child') . ' ' . (string) ($student['lastname'] ?? ''));
$name = $name !== '' ? $name : 'Your child';
$previousClass = trim((string) ($decisionRow['class_section_name'] ?? ''));
$previousClass = $previousClass !== '' ? $previousClass : 'the same class';
if ($decisionKey === 'pass' || ($decisionKey === '' && $source !== 'pending')) {
return ['message' => '', 'blocking' => false, 'level' => 'info'];
}
if ($decisionKey === 'make-up exam in fall') {
$dateText = $fallMakeupExamLabel !== null ? ' on ' . $fallMakeupExamLabel : '';
return [
'message' => $name . ' has a make-up exam decision. Enrollment can be completed only after the fall make-up exam' . $dateText . ' and after the result is finalized.',
'blocking' => true,
'level' => 'warning',
];
}
if ($decisionKey === 'deferred decision') {
return [
'message' => $name . ' has a deferred decision. Enrollment can be completed only after you visit the administration to discuss your child\'s situation.',
'blocking' => true,
'level' => 'warning',
];
}
if ($decisionKey === 'repeat class') {
return [
'message' => $name . ' has a repeat class decision and is accepted only in ' . $previousClass . '.',
'blocking' => false,
'level' => 'info',
];
}
if ($decisionKey === 'expel') {
return [
'message' => $name . ' has an expel decision. Enrollment cannot be completed online; please contact the administration.',
'blocking' => true,
'level' => 'danger',
];
}
if ($decisionKey === 'withdrawn') {
return [
'message' => $name . ' was marked withdrawn in the final decision. Please contact the administration before requesting enrollment.',
'blocking' => true,
'level' => 'warning',
];
}
if ($decisionKey === '' || $source === 'pending') {
return [
'message' => $name . ' does not have a final promotion decision yet. Enrollment can be completed only after the administration finalizes the decision.',
'blocking' => true,
'level' => 'warning',
];
}
return [
'message' => $name . ' has a "' . $decision . '" decision. Please contact the administration before requesting enrollment.',
'blocking' => true,
'level' => 'warning',
];
};
?>
<?php if (!empty($students)): ?>
<form action="<?= base_url('/parent/enroll_classes_handler') ?>" method="post">
<?= csrf_field() ?>
<input type="hidden" id="accept_school_policy_input" name="accept_school_policy" value="<?= $hasAcceptedSchoolPolicy ? '1' : '0' ?>">
<div class="table-responsive">
<table class="table table-striped table-bordered align-middle">
<thead>
@@ -46,6 +125,7 @@ $deadlineISO = $deadlineObj->format('Y-m-d\TH:i:sP'); // for JS
</thead>
<tbody>
<?php foreach ($students as $index => $student): ?>
<?php $decisionMessage = $decisionMessageForStudent($student); ?>
<tr>
<td><?= $index + 1 ?></td>
<td><?= esc($student['school_id'] ?? 'N/A') ?></td>
@@ -69,6 +149,9 @@ $deadlineISO = $deadlineObj->format('Y-m-d\TH:i:sP'); // for JS
<input type="checkbox"
name="enroll[]"
value="<?= esc($student['id']) ?>"
data-decision-message="<?= esc($decisionMessage['message']) ?>"
data-decision-blocking="<?= $decisionMessage['blocking'] ? '1' : '0' ?>"
data-decision-message-target="enrollment-decision-message-<?= esc($student['id']) ?>"
<?= $disableEnrollUI ? 'disabled' : '' ?>>
<?php elseif (in_array($student['enrollment_status'], ['enrolled', 'admission under review', 'payment pending', 'withdraw under review'])): ?>
<input type="checkbox" checked disabled>
@@ -127,6 +210,15 @@ $deadlineISO = $deadlineObj->format('Y-m-d\TH:i:sP'); // for JS
?>
</td>
</tr>
<?php if ($decisionMessage['message'] !== ''): ?>
<tr id="enrollment-decision-message-<?= esc($student['id']) ?>" class="enrollment-decision-message-row d-none">
<td colspan="10">
<div class="alert alert-<?= esc($decisionMessage['level']) ?> mb-0">
<?= esc($decisionMessage['message']) ?>
</div>
</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
</table>
@@ -158,6 +250,32 @@ $deadlineISO = $deadlineObj->format('Y-m-d\TH:i:sP'); // for JS
<?php else: ?>
<p>No students found for the selected school year. Please register your kids first.</p>
<?php endif; ?>
<!-- School Policy Modal -->
<div class="modal fade" id="schoolPolicyModal" tabindex="-1" aria-labelledby="schoolPolicyModalLabel" aria-hidden="true" data-bs-backdrop="static">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-success text-white">
<h5 class="modal-title" id="schoolPolicyModalLabel">School Policies</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<iframe src="<?= base_url('policy/school_policy') ?>" width="100%" height="520" frameborder="0" title="School Policies"></iframe>
<div class="form-check mt-3">
<input class="form-check-input" type="checkbox" value="1" id="schoolPolicyAcceptedCheckbox" <?= $hasAcceptedSchoolPolicy ? 'checked disabled' : '' ?>>
<label class="form-check-label" for="schoolPolicyAcceptedCheckbox">
I have read and accept all school policies.
</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-success" id="acceptSchoolPolicyButton" <?= $hasAcceptedSchoolPolicy ? '' : 'disabled' ?>>
Accept Policy
</button>
</div>
</div>
</div>
</div>
<!-- Enrollment Deadline Modal -->
<div class="modal fade" id="deadlineModal" tabindex="-1" aria-labelledby="deadlineModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
@@ -188,13 +306,56 @@ $deadlineISO = $deadlineObj->format('Y-m-d\TH:i:sP'); // for JS
// Values from PHP
const deadlinePassed = <?= $deadlinePassed ? 'true' : 'false' ?>;
const enrollmentDeadline = new Date("<?= esc($deadlineISO) ?>");
let hasAcceptedSchoolPolicy = <?= $hasAcceptedSchoolPolicy ? 'true' : 'false' ?>;
// Bootstrap Modal
const modalEl = document.getElementById('deadlineModal');
const deadlineModal = modalEl ? new bootstrap.Modal(modalEl) : null;
const policyModalEl = document.getElementById('schoolPolicyModal');
const policyModal = policyModalEl ? new bootstrap.Modal(policyModalEl) : null;
const policyAcceptedInput = document.getElementById('accept_school_policy_input');
const policyAcceptedCheckbox = document.getElementById('schoolPolicyAcceptedCheckbox');
const acceptPolicyButton = document.getElementById('acceptSchoolPolicyButton');
const showDeadlineModal = () => {
if (deadlineModal) deadlineModal.show();
};
const showPolicyModal = () => {
if (policyModal) policyModal.show();
};
const setDecisionMessageVisible = (checkbox, visible) => {
const targetId = checkbox.dataset.decisionMessageTarget || '';
const target = targetId ? document.getElementById(targetId) : null;
if (target) {
target.classList.toggle('d-none', !visible);
}
};
const hideAllDecisionMessages = () => {
document.querySelectorAll('.enrollment-decision-message-row').forEach(row => {
row.classList.add('d-none');
});
};
if (policyAcceptedCheckbox && acceptPolicyButton) {
policyAcceptedCheckbox.addEventListener('change', function() {
acceptPolicyButton.disabled = !this.checked;
});
}
if (acceptPolicyButton) {
acceptPolicyButton.addEventListener('click', function() {
if (policyAcceptedCheckbox && !policyAcceptedCheckbox.checked) {
return;
}
hasAcceptedSchoolPolicy = true;
if (policyAcceptedInput) {
policyAcceptedInput.value = '1';
}
if (policyModal) {
policyModal.hide();
}
});
}
// The enrollment form
const form = document.querySelector("form[action*='enroll_classes_handler']");
@@ -202,11 +363,35 @@ $deadlineISO = $deadlineObj->format('Y-m-d\TH:i:sP'); // for JS
// 1) Block clicking "enroll" checkboxes after deadline
document.querySelectorAll("input[name='enroll[]']").forEach(cb => {
cb.addEventListener("click", function(e) {
hideAllDecisionMessages();
if (deadlinePassed) {
e.preventDefault();
e.stopImmediatePropagation();
this.checked = false;
showDeadlineModal();
return;
}
if (!hasAcceptedSchoolPolicy) {
e.preventDefault();
e.stopImmediatePropagation();
this.checked = false;
showPolicyModal();
return;
}
if (this.checked && this.dataset.decisionMessage) {
setDecisionMessageVisible(this, true);
if (this.dataset.decisionBlocking === '1') {
e.preventDefault();
e.stopImmediatePropagation();
this.checked = false;
return;
}
} else {
setDecisionMessageVisible(this, false);
}
});
});
@@ -223,6 +408,12 @@ $deadlineISO = $deadlineObj->format('Y-m-d\TH:i:sP'); // for JS
return;
}
if (!hasAcceptedSchoolPolicy && anyBoxesChecked) {
e.preventDefault();
showPolicyModal();
return;
}
if (deadlinePassed && anyBoxesChecked) {
e.preventDefault();
showDeadlineModal();