fix close year and ignore not active students
Deploy to Shared Hosting / Shared hosting deploy (push) Failing after 48s
Tests / PHPUnit (push) Successful in 1m17s

This commit is contained in:
root
2026-08-20 17:27:28 -04:00
parent 2b0206e7f2
commit 127098b87c
4 changed files with 346 additions and 98 deletions
@@ -540,7 +540,7 @@ class AdministratorController extends BaseController
public function teacherSubmissionsReport()
{
$semester = (string) (getSemester() ?? $this->semester ?? '');
$semester = (string) ($this->semester !== '' ? $this->semester : (getSemester() ?? $this->semester ?? ''));
$schoolYear = trim((string) ($this->request->getGet('school_year') ?? ''));
if ($schoolYear === '') {
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
@@ -561,7 +561,7 @@ class AdministratorController extends BaseController
{
$result = service('teacherSubmissionReport')->sendNotifications(
(array) $this->request->getPost(),
(string) (getSemester() ?? $this->semester ?? ''),
(string) ($this->semester !== '' ? $this->semester : (getSemester() ?? '')),
(int) (session()->get('user_id') ?? 0)
);
+74 -48
View File
@@ -304,6 +304,12 @@ class ParentController extends BaseController
foreach ($students as &$student) {
$studentId = $student['id'];
$student['age'] = $this->calculateAgeAsOfSchoolYearStartYear($student['dob'] ?? null, $selectedYear);
$student['allergies'] = $this->allergyModel
->where('student_id', (int) $studentId)
->findColumn('allergy') ?? [];
$student['medical_conditions'] = $this->medicalConditionModel
->where('student_id', (int) $studentId)
->findColumn('condition_name') ?? [];
// Get class section info (can be multiple sections like Grade + Arabic)
$classSections = $this->studentClassModel->getClassSectionsByStudentId($studentId, $selectedYear, true);
@@ -816,66 +822,38 @@ class ParentController extends BaseController
continue;
}
$firstName = $this->normalizeEnrollmentStudentName((string) ($fields['firstname'] ?? ''));
$lastName = $this->normalizeEnrollmentStudentName((string) ($fields['lastname'] ?? ''));
$dob = trim((string) ($fields['dob'] ?? ''));
$gender = trim((string) ($fields['gender'] ?? ''));
$registrationGrade = trim((string) ($fields['registration_grade'] ?? ''));
$studentLabel = trim((string) ($existing['firstname'] ?? '') . ' ' . (string) ($existing['lastname'] ?? ''))
?: 'Student ID ' . $studentId;
$photoConsent = (string) ($fields['photo_consent'] ?? '');
$studentLabel = trim($firstName . ' ' . $lastName) ?: 'Student ID ' . $studentId;
try {
$this->validateNames($firstName);
$this->validateNames($lastName);
} catch (InvalidArgumentException $e) {
$errors[] = $studentLabel . ': ' . $e->getMessage();
continue;
}
if (! in_array($gender, ['Male', 'Female'], true)) {
$errors[] = $studentLabel . ': gender is required.';
continue;
}
if ($photoConsent !== '0' && $photoConsent !== '1') {
$errors[] = $studentLabel . ': photo consent is required.';
continue;
}
if ($registrationGrade === '' || mb_strlen($registrationGrade) > 50) {
$errors[] = $studentLabel . ': registration grade is required.';
continue;
}
$dobObj = \DateTimeImmutable::createFromFormat('!Y-m-d', $dob, new \DateTimeZone('UTC'));
$dobErrors = \DateTimeImmutable::getLastErrors();
$dobWarningCount = is_array($dobErrors) ? (int) ($dobErrors['warning_count'] ?? 0) : 0;
$dobErrorCount = is_array($dobErrors) ? (int) ($dobErrors['error_count'] ?? 0) : 0;
if ($dobObj === false || $dobWarningCount > 0 || $dobErrorCount > 0) {
$errors[] = $studentLabel . ': date of birth must use YYYY-MM-DD format.';
continue;
}
$validation = $this->validateDobAge(
$dob,
$this->registrationMinimumAgeDeadline($schoolYear),
5,
18,
$this->schoolYearAgeDeadline($schoolYear)
$medicalConditions = $this->normalizeEnrollmentHealthSelections(
$fields['medical_conditions'] ?? [],
(string) ($fields['medical_condition_other'] ?? '')
);
if (! $validation['isValid']) {
$errors[] = $studentLabel . ': ' . $validation['message'] . '.';
$allergies = $this->normalizeEnrollmentHealthSelections(
$fields['allergies'] ?? [],
(string) ($fields['allergy_other'] ?? '')
);
if ($medicalConditions === []) {
$errors[] = $studentLabel . ': medical conditions are required.';
continue;
}
if ($allergies === []) {
$errors[] = $studentLabel . ': allergies are required.';
continue;
}
$updates[$studentId] = [
'firstname' => $firstName,
'lastname' => $lastName,
'dob' => $dobObj->format('Y-m-d'),
'age' => $this->calculateAgeAsOfSchoolYearStartYear($dobObj->format('Y-m-d'), $schoolYear),
'gender' => $gender,
'registration_grade' => $registrationGrade,
'photo_consent' => (int) $photoConsent,
'medical_conditions' => $medicalConditions,
'allergies' => $allergies,
];
}
@@ -884,14 +862,62 @@ class ParentController extends BaseController
}
foreach ($updates as $studentId => $payload) {
if (! $this->studentModel->update($studentId, $payload)) {
if (! $this->studentModel->update($studentId, ['photo_consent' => $payload['photo_consent']])) {
$errors[] = 'Student ID ' . $studentId . ': student information could not be updated.';
continue;
}
$this->medicalConditionModel->where('student_id', $studentId)->delete();
foreach ($payload['medical_conditions'] as $condition) {
$this->medicalConditionModel->insert([
'student_id' => $studentId,
'condition_name' => $condition,
]);
}
$this->allergyModel->where('student_id', $studentId)->delete();
foreach ($payload['allergies'] as $allergy) {
$this->allergyModel->insert([
'student_id' => $studentId,
'allergy' => $allergy,
]);
}
}
return $errors;
}
/**
* @param mixed $selected
* @return list<string>
*/
private function normalizeEnrollmentHealthSelections($selected, string $otherText): array
{
$values = [];
foreach ((array) $selected as $value) {
$value = trim((string) $value);
if ($value === '') {
continue;
}
$values[] = $value;
}
$otherText = trim($otherText);
if (in_array('Other', $values, true)) {
$values = array_values(array_filter($values, static fn(string $value): bool => $value !== 'Other'));
if ($otherText !== '') {
$values[] = mb_substr($otherText, 0, 100);
}
}
$unique = [];
foreach ($values as $value) {
$unique[$value] = $value;
}
return array_values($unique);
}
private function normalizeEnrollmentStudentName(string $name): string
{
$name = trim(preg_replace('/\s+/', ' ', $name) ?? '');
+24
View File
@@ -587,6 +587,9 @@ final class SchoolYearClosingService
$hasEventOnly = $this->db->fieldExists('is_event_only', 'student_class');
$hasActive = $this->db->fieldExists('is_active', 'students');
$hasEnrollments = $this->db->tableExists('enrollments');
$hasEnrollmentStatus = $hasEnrollments && $this->db->fieldExists('enrollment_status', 'enrollments');
$hasEnrollmentWithdrawn = $hasEnrollments && $this->db->fieldExists('is_withdrawn', 'enrollments');
$hasDob = $this->db->fieldExists('dob', 'students');
$hasRegistrationGrade = $this->db->fieldExists('registration_grade', 'students');
@@ -600,6 +603,14 @@ final class SchoolYearClosingService
->where('sc.school_year', $schoolYear)
->where('sc.class_section_id IS NOT NULL', null, false);
if ($hasEnrollments && ($hasEnrollmentStatus || $hasEnrollmentWithdrawn)) {
$builder->join(
'enrollments e',
'e.student_id = sc.student_id AND e.school_year = ' . $this->db->escape($schoolYear),
'left'
);
}
if ($hasDob) {
$builder->select('s.dob');
}
@@ -619,6 +630,19 @@ final class SchoolYearClosingService
$builder->where('s.is_active', 1);
}
if ($hasEnrollmentStatus) {
$inactiveStatuses = implode(',', array_map([$this->db, 'escape'], EnrollmentStatusService::INACTIVE_STATUSES));
$builder->where(
'(e.enrollment_status IS NULL OR LOWER(TRIM(e.enrollment_status)) NOT IN (' . $inactiveStatuses . '))',
null,
false
);
}
if ($hasEnrollmentWithdrawn) {
$builder->where('(e.is_withdrawn IS NULL OR e.is_withdrawn != 1)', null, false);
}
$assignmentRows = $builder
->orderBy('sc.student_id', 'ASC')
->orderBy('sc.updated_at', 'DESC')
+246 -48
View File
@@ -345,7 +345,70 @@ foreach (($students ?? []) as $student) {
<div data-step-panel="0">
<h6 class="fw-semibold">Review student information</h6>
<div class="text-muted small mb-3">Select each student you want to enroll for <?= esc($selectedYear ?? 'the selected school year') ?> and update their information if needed.</div>
<div class="text-muted small mb-3">Select each student you want to enroll for <?= esc($selectedYear ?? 'the selected school year') ?>. Photo consent, medical conditions, and allergies can be updated below.</div>
<?php
$enrollmentMedicalOptions = [
'None',
'ADHD (Attention-Deficit/Hyperactivity Disorder)',
'Anxiety or Emotional Disorders',
'Asthma',
'Autism Spectrum Disorder (ASD)',
'Behavioral or Conduct Disorders',
'Blindness / Vision Impairment',
'Celiac Disease (Gluten Intolerance)',
'Cerebral Palsy',
'Cystic Fibrosis',
'Depression',
'Diabetes (Type 1 or Type 2)',
'Down Syndrome',
'Dyslexia or Learning Disabilities',
'Eating Disorders',
'Eczema / Severe Skin Conditions',
'Epilepsy / Seizure Disorders',
'Hearing Impairments / Deafness',
'Heart Conditions (congenital or acquired)',
'Hemophilia / Bleeding Disorders',
'Kidney Disease',
'Migraines / Chronic Headaches',
'Obsessive-Compulsive Disorder (OCD)',
'Physical Disabilities / Mobility Impairments',
'PTSD (Post-Traumatic Stress Disorder)',
'Sickle Cell Anemia',
'Speech and Language Disorders',
'Thyroid Disorders',
'Tourette Syndrome',
'Traumatic Brain Injury (TBI)',
'Rheumatic diseases',
'Ulcerative Colitis / Crohns Disease',
'Other',
];
$enrollmentAllergyOptions = [
'None',
'Animal Dander (cats, dogs, etc.)',
'Antibiotics',
'Bee stings',
'Cockroach',
'Corn',
'Dust Mites',
'Egg',
'Fire ant stings',
'Fish',
'Fragrances / Perfumes',
'Latex',
'Milk / Dairy',
'Mold',
'Mosquito bites',
'Peanut',
'Pollen (grass, tree, weed)',
'Sesame',
'Shellfish (shrimp, crab, lobster, etc.)',
'Soy',
'Tree Nuts (almond, cashew, walnut, etc.)',
'Wasp stings',
'Wheat / Gluten',
'Other',
];
?>
<div class="d-grid gap-2">
<?php foreach ($students as $student): ?>
<?php
@@ -360,6 +423,25 @@ foreach (($students ?? []) as $student) {
$gradeLabel = $section
? preg_replace_callback('/\byouth\b/i', fn($m) => ucfirst(strtolower($m[0])), (string) $section)
: 'Not Assigned';
$medicalSelected = is_array($student['medical_conditions'] ?? null)
? array_values(array_filter(array_map('trim', $student['medical_conditions'])))
: array_values(array_filter(array_map('trim', preg_split('/[,\n;]+/', (string) ($student['medical_conditions'] ?? ''), -1, PREG_SPLIT_NO_EMPTY) ?: [])));
$allergySelected = is_array($student['allergies'] ?? null)
? array_values(array_filter(array_map('trim', $student['allergies'])))
: array_values(array_filter(array_map('trim', preg_split('/[,\n;]+/', (string) ($student['allergies'] ?? ''), -1, PREG_SPLIT_NO_EMPTY) ?: [])));
$medicalOther = implode(', ', array_diff($medicalSelected, $enrollmentMedicalOptions));
$allergyOther = implode(', ', array_diff($allergySelected, $enrollmentAllergyOptions));
if ($medicalOther !== '') {
$medicalSelected[] = 'Other';
}
if ($allergyOther !== '') {
$allergySelected[] = 'Other';
}
$dobDisplay = !empty($student['dob']) ? local_date($student['dob'], 'm-d-Y') : 'N/A';
$photoConsentValue = (string) ($student['photo_consent'] ?? '');
if ($photoConsentValue !== '0' && $photoConsentValue !== '1') {
$photoConsentValue = !empty($student['photo_consent']) ? '1' : '';
}
?>
<div class="student-select-card <?= $canEnroll ? '' : 'is-disabled' ?>"
data-student-card
@@ -376,56 +458,65 @@ foreach (($students ?? []) as $student) {
<div class="fw-semibold"><?= esc($studentName) ?></div>
<?= $statusBadge($student['enrollment_status'] ?? '') ?>
</div>
<div class="small text-muted">Current grade: <?= esc($gradeLabel) ?> &middot; School ID: <?= esc($student['school_id'] ?? 'N/A') ?></div>
<div class="small text-muted mt-1">
<div>School ID: <?= esc($student['school_id'] ?? 'N/A') ?></div>
<div>Date of birth: <?= esc($dobDisplay) ?><?php if (isset($student['age'])): ?> &middot; Age: <?= esc($student['age']) ?><?php endif; ?></div>
<div>Gender: <?= esc($student['gender'] ?? 'N/A') ?></div>
<div>Registration grade: <?= esc($student['registration_grade'] ?? 'N/A') ?></div>
<div>Current grade: <?= esc($gradeLabel) ?></div>
<div>Expected placement: <?= esc($student['expected_placement_label'] ?? $gradeLabel) ?></div>
<div>Required action: <?= esc($student['required_action_label'] ?? 'Contact administration') ?></div>
</div>
<?php if ($canEnroll): ?>
<div class="row g-2 mt-2" data-student-edit-fields>
<div class="col-md-6">
<label class="form-label small fw-semibold" for="student-firstname-<?= esc($student['id']) ?>">First name</label>
<input class="form-control form-control-sm" type="text" id="student-firstname-<?= esc($student['id']) ?>" name="student_info[<?= esc($student['id']) ?>][firstname]" value="<?= esc($student['firstname'] ?? '') ?>" required maxlength="30" disabled data-student-edit-input>
</div>
<div class="col-md-6">
<label class="form-label small fw-semibold" for="student-lastname-<?= esc($student['id']) ?>">Last name</label>
<input class="form-control form-control-sm" type="text" id="student-lastname-<?= esc($student['id']) ?>" name="student_info[<?= esc($student['id']) ?>][lastname]" value="<?= esc($student['lastname'] ?? '') ?>" required maxlength="30" disabled data-student-edit-input>
<input type="hidden" name="student_info[<?= esc($student['id']) ?>][firstname]" value="<?= esc($student['firstname'] ?? '') ?>">
<input type="hidden" name="student_info[<?= esc($student['id']) ?>][lastname]" value="<?= esc($student['lastname'] ?? '') ?>">
<input type="hidden" name="student_info[<?= esc($student['id']) ?>][dob]" value="<?= esc($student['dob'] ?? '') ?>">
<input type="hidden" name="student_info[<?= esc($student['id']) ?>][gender]" value="<?= esc($student['gender'] ?? '') ?>">
<input type="hidden" name="student_info[<?= esc($student['id']) ?>][registration_grade]" value="<?= esc($student['registration_grade'] ?? '') ?>">
<div class="row g-2 mt-3" data-student-edit-fields>
<div class="col-12">
<div class="fw-semibold small mb-1">Editable health information</div>
</div>
<div class="col-md-4">
<label class="form-label small fw-semibold" for="student-dob-<?= esc($student['id']) ?>">Date of birth</label>
<input class="form-control form-control-sm" type="date" id="student-dob-<?= esc($student['id']) ?>" name="student_info[<?= esc($student['id']) ?>][dob]" value="<?= esc($student['dob'] ?? '') ?>" required disabled data-student-edit-input>
</div>
<div class="col-md-4">
<label class="form-label small fw-semibold" for="student-gender-<?= esc($student['id']) ?>">Gender</label>
<select class="form-select form-select-sm" id="student-gender-<?= esc($student['id']) ?>" name="student_info[<?= esc($student['id']) ?>][gender]" required disabled data-student-edit-input>
<option value="">Select</option>
<option value="Male" <?= ($student['gender'] ?? '') === 'Male' ? 'selected' : '' ?>>Male</option>
<option value="Female" <?= ($student['gender'] ?? '') === 'Female' ? 'selected' : '' ?>>Female</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label small fw-semibold" for="student-grade-<?= esc($student['id']) ?>">Registration grade</label>
<input class="form-control form-control-sm" type="text" id="student-grade-<?= esc($student['id']) ?>" name="student_info[<?= esc($student['id']) ?>][registration_grade]" value="<?= esc($student['registration_grade'] ?? '') ?>" required maxlength="50" disabled data-student-edit-input>
</div>
<div class="col-md-6">
<label class="form-label small fw-semibold" for="student-photo-consent-<?= esc($student['id']) ?>">Photo consent</label>
<select class="form-select form-select-sm" id="student-photo-consent-<?= esc($student['id']) ?>" name="student_info[<?= esc($student['id']) ?>][photo_consent]" required disabled data-student-edit-input>
<option value="">Select</option>
<option value="1" <?= (string) ($student['photo_consent'] ?? '') === '1' ? 'selected' : '' ?>>Yes</option>
<option value="0" <?= (string) ($student['photo_consent'] ?? '') === '0' ? 'selected' : '' ?>>No</option>
<option value="1" <?= $photoConsentValue === '1' ? 'selected' : '' ?>>Yes</option>
<option value="0" <?= $photoConsentValue === '0' ? 'selected' : '' ?>>No</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label small fw-semibold">Expected placement</label>
<div class="form-control form-control-sm bg-light"><?= esc($student['expected_placement_label'] ?? $gradeLabel) ?></div>
<div class="col-md-4">
<label class="form-label small fw-semibold">Medical conditions</label>
<div class="border rounded p-2 bg-white small" style="max-height: 180px; overflow-y: auto;" data-health-group="medical_conditions">
<?php foreach ($enrollmentMedicalOptions as $opt): ?>
<?php $optId = 'student-medical-' . $student['id'] . '-' . md5($opt); ?>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="<?= esc($optId) ?>" name="student_info[<?= esc($student['id']) ?>][medical_conditions][]" value="<?= esc($opt) ?>" <?= in_array($opt, $medicalSelected, true) ? 'checked' : '' ?> disabled data-student-edit-input data-health-option="<?= $opt === 'Other' ? 'other' : ($opt === 'None' ? 'none' : 'item') ?>">
<label class="form-check-label" for="<?= esc($optId) ?>"><?= esc($opt) ?></label>
</div>
<?php endforeach; ?>
</div>
<input type="text" class="form-control form-control-sm mt-2 <?= $medicalOther === '' ? 'd-none' : '' ?>" name="student_info[<?= esc($student['id']) ?>][medical_condition_other]" value="<?= esc($medicalOther) ?>" placeholder="Please specify if Other is selected" disabled data-student-edit-input data-other-input="medical_conditions" maxlength="100">
</div>
<div class="col-md-4">
<label class="form-label small fw-semibold">Allergies</label>
<div class="border rounded p-2 bg-white small" style="max-height: 180px; overflow-y: auto;" data-health-group="allergies">
<?php foreach ($enrollmentAllergyOptions as $opt): ?>
<?php $optId = 'student-allergy-' . $student['id'] . '-' . md5($opt); ?>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="<?= esc($optId) ?>" name="student_info[<?= esc($student['id']) ?>][allergies][]" value="<?= esc($opt) ?>" <?= in_array($opt, $allergySelected, true) ? 'checked' : '' ?> disabled data-student-edit-input data-health-option="<?= $opt === 'Other' ? 'other' : ($opt === 'None' ? 'none' : 'item') ?>">
<label class="form-check-label" for="<?= esc($optId) ?>"><?= esc($opt) ?></label>
</div>
<?php endforeach; ?>
</div>
<input type="text" class="form-control form-control-sm mt-2 <?= $allergyOther === '' ? 'd-none' : '' ?>" name="student_info[<?= esc($student['id']) ?>][allergy_other]" value="<?= esc($allergyOther) ?>" placeholder="Please specify if Other is selected" disabled data-student-edit-input data-other-input="allergies" maxlength="100">
</div>
</div>
<?php else: ?>
<div class="small text-muted">
Date of birth: <?= esc(!empty($student['dob']) ? local_date($student['dob'], 'm-d-Y') : 'N/A') ?>
<?php if (isset($student['age'])): ?>
&middot; Age: <?= esc($student['age']) ?>
<?php endif; ?>
</div>
<div class="small text-muted">Expected placement: <?= esc($student['expected_placement_label'] ?? $gradeLabel) ?></div>
<?php endif; ?>
<div class="small">Required action: <?= esc($student['required_action_label'] ?? 'Contact administration') ?></div>
<?php if (($eligibilityMessage['message'] ?? '') !== ''): ?>
<div class="alert alert-<?= esc($eligibilityMessage['level'] ?? 'info') ?> py-2 px-3 mt-2 mb-0 small">
<?= esc($eligibilityMessage['message']) ?>
@@ -462,9 +553,6 @@ foreach (($students ?? []) as $student) {
<div class="col-md-4"><span class="text-muted">Previous-year carry-over balance:</span> <strong data-financial-value="carry_over_balance"><?= esc($money($familyFinancialSummary['carry_over_balance'] ?? 0)) ?></strong></div>
<div class="col-md-4"><span class="text-muted">Total currently due:</span> <strong data-financial-value="amount_due"><?= esc($money($familyFinancialSummary['amount_due'] ?? 0)) ?></strong></div>
</div>
<?php if (!empty($familyFinancialSummary['policy_message'])): ?>
<div class="small text-muted mt-2"><?= esc($familyFinancialSummary['policy_message']) ?></div>
<?php endif; ?>
</div>
<?php else: ?>
<div class="alert alert-warning mb-0">Family account information is not available. Please contact the administration if you have questions about tuition or balance.</div>
@@ -476,7 +564,7 @@ foreach (($students ?? []) as $student) {
<div class="text-muted small mb-3">Review the enrollment summary before submitting.</div>
<div class="mb-3">
<div class="fw-semibold mb-2">Fees by student</div>
<div class="fw-semibold mb-2">Summary</div>
<div id="enrollmentFeeReviewList" class="d-grid gap-2"></div>
</div>
@@ -509,11 +597,8 @@ foreach (($students ?? []) as $student) {
<div class="alert alert-warning mb-0 small">
<div class="fw-semibold mb-1">Important information</div>
<ul class="mb-0 ps-3">
<li>Submitting sends the selected enrollment(s) to admission review.</li>
<li>For newly registered students, enrollment status changes to admission under review after submitting the request.</li>
<li>Payments are processed on the first day of school.</li>
<?php if (!empty($familyFinancialSummary['policy_message'])): ?>
<li><?= esc($familyFinancialSummary['policy_message']) ?></li>
<?php endif; ?>
<?php if (!empty($fallMakeupExamOn)): ?>
<li>Students with a make-up exam decision may initially remain in the same grade until the make-up exam result is confirmed.</li>
<?php endif; ?>
@@ -721,8 +806,118 @@ foreach (($students ?? []) as $student) {
card.querySelectorAll('[data-student-edit-input]').forEach(field => {
field.disabled = !enabled;
});
syncHealthGroup(card, 'medical_conditions');
syncHealthGroup(card, 'allergies');
}
function syncHealthGroup(card, groupName) {
const selected = !!card.querySelector('[data-enroll-input]')?.checked;
const group = card.querySelector('[data-health-group="' + groupName + '"]');
const otherInput = card.querySelector('[data-other-input="' + groupName + '"]');
if (!group) {
return;
}
const noneChecked = !!group.querySelector('[data-health-option="none"]:checked');
const otherChecked = !!group.querySelector('[data-health-option="other"]:checked');
group.querySelectorAll('input[type="checkbox"]').forEach(input => {
if (!selected) {
input.disabled = true;
return;
}
if (input.dataset.healthOption === 'none') {
input.disabled = false;
return;
}
if (noneChecked) {
input.checked = false;
input.disabled = true;
return;
}
input.disabled = false;
});
if (otherInput) {
otherInput.classList.toggle('d-none', !otherChecked);
otherInput.disabled = !selected || !otherChecked;
if (!otherChecked) {
otherInput.value = '';
}
}
}
function validateSelectedStudentHealth() {
for (const input of selectedEnrollInputs()) {
const card = input.closest('[data-student-card]');
if (!card) {
continue;
}
const name = card.querySelector('.fw-semibold')?.textContent?.trim() || 'Student';
const photo = card.querySelector("select[name$='[photo_consent]']");
if (!photo || photo.value === '') {
alert('Please select photo consent for ' + name + '.');
return false;
}
const medicalChecked = card.querySelectorAll('[data-health-group="medical_conditions"] input[type="checkbox"]:checked');
if (!medicalChecked.length) {
alert('Please select medical conditions for ' + name + '.');
return false;
}
const medicalOther = card.querySelector('[data-other-input="medical_conditions"]');
if (card.querySelector('[data-health-group="medical_conditions"] [data-health-option="other"]:checked') && (!medicalOther || !medicalOther.value.trim())) {
alert('Please specify the other medical condition for ' + name + '.');
return false;
}
const allergyChecked = card.querySelectorAll('[data-health-group="allergies"] input[type="checkbox"]:checked');
if (!allergyChecked.length) {
alert('Please select allergies for ' + name + '.');
return false;
}
const allergyOther = card.querySelector('[data-other-input="allergies"]');
if (card.querySelector('[data-health-group="allergies"] [data-health-option="other"]:checked') && (!allergyOther || !allergyOther.value.trim())) {
alert('Please specify the other allergy for ' + name + '.');
return false;
}
}
return true;
}
document.querySelectorAll('[data-student-card]').forEach(card => {
card.querySelectorAll('[data-health-group]').forEach(group => {
group.addEventListener('change', function(event) {
const target = event.target;
if (!(target instanceof HTMLInputElement) || target.type !== 'checkbox') {
return;
}
const groupName = group.getAttribute('data-health-group');
if (!groupName) {
return;
}
if (target.dataset.healthOption === 'none' && target.checked) {
group.querySelectorAll('[data-health-option="item"], [data-health-option="other"]').forEach(input => {
input.checked = false;
});
}
if ((target.dataset.healthOption === 'item' || target.dataset.healthOption === 'other') && target.checked) {
const noneInput = group.querySelector('[data-health-option="none"]');
if (noneInput) {
noneInput.checked = false;
}
}
syncHealthGroup(card, groupName);
});
});
});
if (startButton && flowModal) {
startButton.addEventListener('click', function() {
if (deadlinePassed) {
@@ -768,6 +963,9 @@ foreach (($students ?? []) as $student) {
alert('Please select at least one student to enroll.');
return;
}
if (currentStep === 0 && !validateSelectedStudentHealth()) {
return;
}
if (currentStep === 1) {
syncPolicyAccepted();
if (!hasAcceptedSchoolPolicy) {