fix enrollment logic, add financial aid, fix class distribution
Tests / PHPUnit (push) Failing after 1m6s
Tests / PHPUnit (push) Failing after 1m6s
This commit is contained in:
+508
-318
@@ -1,256 +1,418 @@
|
||||
<?= $this->extend('layout/main_layout') ?>
|
||||
<?= $this->section('content') ?>
|
||||
<div class="container my-5">
|
||||
<h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Enroll in Classes</h3>
|
||||
<?php if (!empty($selectedYear)): ?>
|
||||
<div class="text-center text-muted small">School Year: <?= esc($selectedYear) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?= $this->section('styles') ?>
|
||||
<style>
|
||||
.enrollment-start-panel {
|
||||
border: 1px solid #d8e7dc;
|
||||
border-radius: 8px;
|
||||
background: #f8fbf8;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.enrollment-stepper {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: .5rem;
|
||||
}
|
||||
|
||||
.enrollment-step {
|
||||
border-bottom: 3px solid #dee2e6;
|
||||
color: #6c757d;
|
||||
font-size: .85rem;
|
||||
padding: .35rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.enrollment-step.is-active {
|
||||
border-color: #198754;
|
||||
color: #198754;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.student-select-card {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
padding: .85rem;
|
||||
transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
|
||||
}
|
||||
|
||||
.student-select-card.is-selected {
|
||||
background: #eef8f0;
|
||||
border-color: #198754;
|
||||
box-shadow: 0 0 0 .15rem rgba(25, 135, 84, .14);
|
||||
}
|
||||
|
||||
.student-select-card.is-disabled {
|
||||
background: #f8f9fa;
|
||||
color: #6c757d;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.student-select-icon {
|
||||
align-items: center;
|
||||
border: 1px solid #adb5bd;
|
||||
border-radius: 50%;
|
||||
display: inline-flex;
|
||||
height: 1.65rem;
|
||||
justify-content: center;
|
||||
width: 1.65rem;
|
||||
}
|
||||
|
||||
.student-select-card.is-selected .student-select-icon {
|
||||
background: #198754;
|
||||
border-color: #198754;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.enrollment-policy-frame {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
height: min(54vh, 520px);
|
||||
min-height: 320px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.enrollment-modal-footer {
|
||||
gap: .5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
.enrollment-stepper {
|
||||
gap: .25rem;
|
||||
}
|
||||
|
||||
.enrollment-step {
|
||||
font-size: .72rem;
|
||||
}
|
||||
|
||||
.enrollment-modal-footer {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.enrollment-modal-footer .btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.student-select-card {
|
||||
padding: .75rem;
|
||||
}
|
||||
|
||||
.enrollment-status-table,
|
||||
.enrollment-status-table thead,
|
||||
.enrollment-status-table tbody,
|
||||
.enrollment-status-table tr,
|
||||
.enrollment-status-table th,
|
||||
.enrollment-status-table td {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.enrollment-status-table thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.enrollment-status-table {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.enrollment-status-table tr {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
margin-bottom: .85rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.enrollment-status-table td {
|
||||
align-items: flex-start;
|
||||
border-bottom: 1px solid #eef1f3;
|
||||
display: flex;
|
||||
gap: .75rem;
|
||||
justify-content: space-between;
|
||||
padding: .75rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.enrollment-status-table td:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.enrollment-status-table td::before {
|
||||
color: #6c757d;
|
||||
content: attr(data-label);
|
||||
flex: 0 0 42%;
|
||||
font-size: .8rem;
|
||||
font-weight: 700;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.enrollment-status-table td > * {
|
||||
max-width: 58%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
<?php
|
||||
// Put this near the very top of the file (before output), or right after the extend/section lines.
|
||||
$tz = (string) (config('School')->attendance['timezone'] ?? user_timezone());
|
||||
$deadlineObj = (new DateTime($lastDayOfRegistration, new DateTimeZone($tz)))->setTime(23, 59, 59);
|
||||
$nowObj = new DateTime('now', new DateTimeZone($tz));
|
||||
$nowObj = new DateTime('now', new DateTimeZone($tz));
|
||||
$deadlinePassed = $nowObj > $deadlineObj;
|
||||
$deadlineISO = $deadlineObj->format('Y-m-d\TH:i:sP'); // for JS
|
||||
$hasAcceptedSchoolPolicy = (bool) ($hasAcceptedSchoolPolicy ?? false);
|
||||
$familyFinancialSummary = is_array($familyFinancialSummary ?? null) ? $familyFinancialSummary : [];
|
||||
$money = static function ($amount) use ($familyFinancialSummary): string {
|
||||
$currency = (string) ($familyFinancialSummary['currency'] ?? '$');
|
||||
return $currency . number_format((float) $amount, 2);
|
||||
};
|
||||
$statusBadge = static function (?string $status): string {
|
||||
$status = strtolower(trim((string) $status));
|
||||
return match ($status) {
|
||||
'admission under review' => '<span class="badge bg-primary">admission under review</span>',
|
||||
'review & decision' => '<span class="badge bg-secondary">review & decision</span>',
|
||||
'payment pending' => '<span class="badge bg-warning text-dark">payment pending</span>',
|
||||
'enrolled' => '<span class="badge bg-success">enrolled</span>',
|
||||
'withdraw under review' => '<span class="badge bg-warning text-dark">withdraw under review</span>',
|
||||
'refund pending' => '<span class="badge bg-info text-dark">refund pending</span>',
|
||||
'withdrawn' => '<span class="badge bg-danger">withdrawn</span>',
|
||||
'waitlist' => '<span class="badge bg-secondary">waitlist</span>',
|
||||
'denied' => '<span class="badge bg-danger">denied</span>',
|
||||
'not enrolled' => '<span class="badge bg-light text-dark">not enrolled</span>',
|
||||
default => '<span class="badge bg-light text-dark">unknown</span>',
|
||||
};
|
||||
};
|
||||
$enrollableCount = 0;
|
||||
$withdrawableCount = 0;
|
||||
foreach (($students ?? []) as $student) {
|
||||
$eligibilityMessage = is_array($student['enrollment_eligibility_message'] ?? null) ? $student['enrollment_eligibility_message'] : ['blocking' => false];
|
||||
if (($student['enrollment_status'] ?? '') === 'not enrolled' && !$deadlinePassed && $isEditable && empty($eligibilityMessage['blocking'])) {
|
||||
$enrollableCount++;
|
||||
}
|
||||
if (($student['enrollment_status'] ?? '') === 'enrolled' && $isEditable) {
|
||||
$withdrawableCount++;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!-- Registration Info -->
|
||||
<div class="alert alert-info mb-3">
|
||||
<p>Enrollment is the process of officially signing up your child for the upcoming school year.</p>
|
||||
<ul>
|
||||
<li>Last Day for Enrollment is <strong><?= esc(local_date($lastDayOfRegistration, 'm-d-Y')) ?></strong>.</li>
|
||||
<li>Once you click "Save", the enrollment status will change to <strong>admission under review</strong>.</li>
|
||||
<li>Payments are processed on the first day of school: <strong><?= esc(local_date($schoolStartDate, 'm-d-Y')) ?></strong>.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php if ($familyFinancialSummary !== []): ?>
|
||||
<div class="border rounded p-3 mb-3 bg-light">
|
||||
<div class="fw-semibold mb-2">Family Account Information</div>
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<span class="text-muted">Previous-year carry-over balance:</span>
|
||||
<strong><?= esc($money($familyFinancialSummary['carry_over_balance'] ?? 0)) ?></strong>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<span class="text-muted">Registration fee:</span>
|
||||
<strong><?= esc($money($familyFinancialSummary['registration_fee'] ?? 0)) ?></strong>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<span class="text-muted">Tuition due now:</span>
|
||||
<strong><?= esc($money($familyFinancialSummary['tuition_due_at_registration'] ?? 0)) ?></strong>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<span class="text-muted">Mandatory fees:</span>
|
||||
<strong><?= esc($money($familyFinancialSummary['mandatory_fees'] ?? 0)) ?></strong>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<span class="text-muted">Current-year account balance:</span>
|
||||
<strong><?= esc($money($familyFinancialSummary['current_balance'] ?? 0)) ?></strong>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<span class="text-muted">Total currently due:</span>
|
||||
<strong><?= 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>
|
||||
<div class="container my-4">
|
||||
<div class="text-center mb-4">
|
||||
<h3 class="text-success" style="font-family: Arial, sans-serif;">Enroll in Classes</h3>
|
||||
<?php if (!empty($selectedYear)): ?>
|
||||
<div class="text-muted small">School Year: <?= esc($selectedYear) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (session()->getFlashdata('error')): ?>
|
||||
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (session()->getFlashdata('error')): ?>
|
||||
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php $hasAcceptedSchoolPolicy = (bool) ($hasAcceptedSchoolPolicy ?? false); ?>
|
||||
<?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>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>School ID</th>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th>Age</th>
|
||||
<th>Gender</th>
|
||||
<th>Grade</th>
|
||||
<th>Decision</th>
|
||||
<th>Required Action</th>
|
||||
<th>Enroll</th>
|
||||
<th>Withdraw</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($students as $index => $student): ?>
|
||||
<?php $eligibilityMessage = is_array($student['enrollment_eligibility_message'] ?? null) ? $student['enrollment_eligibility_message'] : ['message' => '', 'blocking' => false, 'level' => 'info']; ?>
|
||||
<div class="alert alert-info mb-3">
|
||||
<p>Enrollment is the process of officially signing up your child for the upcoming school year.</p>
|
||||
<ul class="mb-0">
|
||||
<li>Last Day for Enrollment is <strong><?= esc(local_date($lastDayOfRegistration, 'm-d-Y')) ?></strong>.</li>
|
||||
<li>After submit, enrollment status changes to <strong>admission under review</strong>.</li>
|
||||
<li>Payments are processed on the first day of school: <strong><?= esc(local_date($schoolStartDate, 'm-d-Y')) ?></strong>.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php if ($familyFinancialSummary !== []): ?>
|
||||
<div class="border rounded p-3 mb-3 bg-light">
|
||||
<div class="fw-semibold mb-2">Family Account Information</div>
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4"><span class="text-muted">Previous-year carry-over balance:</span> <strong><?= esc($money($familyFinancialSummary['carry_over_balance'] ?? 0)) ?></strong></div>
|
||||
<div class="col-md-4"><span class="text-muted">Registration fee:</span> <strong><?= esc($money($familyFinancialSummary['registration_fee'] ?? 0)) ?></strong></div>
|
||||
<div class="col-md-4"><span class="text-muted">Tuition due now:</span> <strong><?= esc($money($familyFinancialSummary['tuition_due_at_registration'] ?? 0)) ?></strong></div>
|
||||
<div class="col-md-4"><span class="text-muted">Mandatory fees:</span> <strong><?= esc($money($familyFinancialSummary['mandatory_fees'] ?? 0)) ?></strong></div>
|
||||
<div class="col-md-4"><span class="text-muted">Current-year account balance:</span> <strong><?= esc($money($familyFinancialSummary['current_balance'] ?? 0)) ?></strong></div>
|
||||
<div class="col-md-4"><span class="text-muted">Total currently due:</span> <strong><?= 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 class="small mt-2"><a href="<?= site_url('parent/financial-aid') ?>">Request financial aid</a></div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($students)): ?>
|
||||
<form action="<?= base_url('/parent/enroll_classes_handler') ?>" method="post" id="enrollmentFlowForm">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" id="accept_school_policy_input" name="accept_school_policy" value="<?= $hasAcceptedSchoolPolicy ? '1' : '0' ?>">
|
||||
|
||||
<div class="enrollment-start-panel mb-4">
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col-lg">
|
||||
<div class="fw-semibold">Enrollment process</div>
|
||||
<div class="text-muted small">
|
||||
Select students, review policy information, then submit enrollment.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-auto">
|
||||
<button type="button"
|
||||
class="btn btn-success btn-lg w-100"
|
||||
id="startEnrollmentButton"
|
||||
<?= (!$isEditable || $enrollableCount === 0 || $deadlinePassed) ? 'disabled' : '' ?>>
|
||||
Start Enrollment
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($deadlinePassed): ?>
|
||||
<div class="small text-danger mt-2">Enrollment closed on <?= esc($deadlineObj->format('m-d-Y')) ?>.</div>
|
||||
<?php elseif ($enrollableCount === 0): ?>
|
||||
<div class="small text-muted mt-2">No students are currently available for new enrollment.</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive mb-4">
|
||||
<table class="table table-striped table-bordered align-middle enrollment-status-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?= $index + 1 ?></td>
|
||||
<td><?= esc($student['school_id'] ?? 'N/A') ?></td>
|
||||
<td><?= esc($student['firstname'] ?? 'N/A') ?></td>
|
||||
<td><?= esc($student['lastname'] ?? 'N/A') ?></td>
|
||||
<td><?= esc($student['age'] ?? 'N/A') ?></td>
|
||||
<td><?= esc($student['gender'] ?? 'N/A') ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$section = $student['class_section'] ?? null;
|
||||
echo $section
|
||||
? esc(preg_replace_callback('/\byouth\b/i', fn($m) => ucfirst(strtolower($m[0])), $section))
|
||||
: 'Not Assigned';
|
||||
?>
|
||||
</td>
|
||||
<td><?= esc($student['transition_evaluation']['decision_label'] ?? 'Pending') ?></td>
|
||||
<td><?= esc($student['required_action_label'] ?? 'Contact the school administration.') ?></td>
|
||||
|
||||
<!-- Enroll Checkbox -->
|
||||
<td>
|
||||
<?php if ($student['enrollment_status'] === 'not enrolled'): ?>
|
||||
<?php $disableEnrollUI = $deadlinePassed || !$isEditable || (bool) ($eligibilityMessage['blocking'] ?? false); ?>
|
||||
<input type="checkbox"
|
||||
name="enroll[]"
|
||||
value="<?= esc($student['id']) ?>"
|
||||
data-decision-message="<?= esc($eligibilityMessage['message'] ?? '') ?>"
|
||||
data-decision-blocking="<?= !empty($eligibilityMessage['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', 'review & decision', 'payment pending', 'withdraw under review'])): ?>
|
||||
<input type="checkbox" checked disabled>
|
||||
<?php else: ?>
|
||||
<input type="checkbox" disabled>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
<!-- Withdraw Checkbox -->
|
||||
<td>
|
||||
<?php if ($student['enrollment_status'] === 'enrolled'): ?>
|
||||
<input type="checkbox" name="withdraw[]" value="<?= esc($student['id']) ?>" <?= !$isEditable ? 'disabled' : '' ?>>
|
||||
<?php elseif ($student['enrollment_status'] === 'withdrawn'): ?>
|
||||
<input type="checkbox" checked disabled>
|
||||
<?php else: ?>
|
||||
<input type="checkbox" disabled>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
<!-- Status -->
|
||||
<td>
|
||||
<?php
|
||||
$status = strtolower(trim($student['enrollment_status'] ?? ''));
|
||||
|
||||
switch ($status) {
|
||||
case 'admission under review':
|
||||
echo '<span class="badge bg-primary">admission under review</span>';
|
||||
break;
|
||||
case 'review & decision':
|
||||
echo '<span class="badge bg-secondary">review & decision</span>';
|
||||
break;
|
||||
case 'payment pending':
|
||||
echo '<span class="badge bg-warning text-dark">payment pending</span>';
|
||||
break;
|
||||
case 'enrolled':
|
||||
echo '<span class="badge bg-success">enrolled</span>';
|
||||
break;
|
||||
case 'withdraw under review':
|
||||
echo '<span class="badge bg-warning text-dark">withdraw under review</span>';
|
||||
break;
|
||||
case 'refund pending':
|
||||
echo '<span class="badge bg-info text-dark">refund pending</span>';
|
||||
break;
|
||||
case 'withdrawn':
|
||||
echo '<span class="badge bg-danger">withdrawn</span>';
|
||||
break;
|
||||
case 'waitlist':
|
||||
echo '<span class="badge bg-secondary">waitlist</span>'; // lighter and neutral
|
||||
break;
|
||||
case 'denied':
|
||||
echo '<span class="badge bg-danger">denied</span>'; // red stands out clearly
|
||||
break;
|
||||
case 'not enrolled':
|
||||
echo '<span class="badge bg-light text-dark">not enrolled</span>'; // very neutral
|
||||
break;
|
||||
default:
|
||||
echo '<span class="badge bg-light text-dark">unknown</span>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<th>Student</th>
|
||||
<th>Grade</th>
|
||||
<th>Decision</th>
|
||||
<th>Required Action</th>
|
||||
<th>Status</th>
|
||||
<th>Withdraw</th>
|
||||
</tr>
|
||||
<?php if (($eligibilityMessage['message'] ?? '') !== ''): ?>
|
||||
<tr id="enrollment-decision-message-<?= esc($student['id']) ?>" class="enrollment-decision-message-row">
|
||||
<td colspan="12">
|
||||
<div class="alert alert-<?= esc($eligibilityMessage['level'] ?? 'info') ?> mb-0">
|
||||
<?= esc($eligibilityMessage['message']) ?>
|
||||
</div>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($students as $student): ?>
|
||||
<?php
|
||||
$studentName = trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? ''));
|
||||
$studentName = $studentName !== '' ? $studentName : 'Student';
|
||||
$section = $student['class_section'] ?? null;
|
||||
$gradeLabel = $section
|
||||
? preg_replace_callback('/\byouth\b/i', fn($m) => ucfirst(strtolower($m[0])), (string) $section)
|
||||
: 'Not Assigned';
|
||||
?>
|
||||
<tr>
|
||||
<td data-label="Student">
|
||||
<div class="fw-semibold"><?= esc($studentName) ?></div>
|
||||
<div class="text-muted small">School ID: <?= esc($student['school_id'] ?? 'N/A') ?></div>
|
||||
</td>
|
||||
<td data-label="Grade"><?= esc($gradeLabel) ?></td>
|
||||
<td data-label="Decision"><?= esc($student['transition_evaluation']['decision_label'] ?? 'Pending') ?></td>
|
||||
<td data-label="Required Action"><?= esc($student['required_action_label'] ?? 'Contact administration') ?></td>
|
||||
<td data-label="Status"><?= $statusBadge($student['enrollment_status'] ?? '') ?></td>
|
||||
<td data-label="Withdraw">
|
||||
<?php if (($student['enrollment_status'] ?? '') === 'enrolled'): ?>
|
||||
<div class="form-check form-switch m-0">
|
||||
<input class="form-check-input" type="checkbox" name="withdraw[]" value="<?= esc($student['id']) ?>" id="withdraw-<?= esc($student['id']) ?>" <?= !$isEditable ? 'disabled' : '' ?>>
|
||||
<label class="form-check-label small" for="withdraw-<?= esc($student['id']) ?>">Request</label>
|
||||
</div>
|
||||
<?php elseif (($student['enrollment_status'] ?? '') === 'withdrawn'): ?>
|
||||
<span class="text-muted small">Withdrawn</span>
|
||||
<?php else: ?>
|
||||
<span class="text-muted small">Unavailable</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Save Button -->
|
||||
<?php
|
||||
$today = local_date(utc_now(), 'Y-m-d');
|
||||
$disableDueToDate = ($today >= $lastDayOfRegistration);
|
||||
// Allow submit for withdrawals even after deadline; keep editability guard only
|
||||
$disableSave = !$isEditable;
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-center">
|
||||
<button type="submit"
|
||||
class="btn btn-lg btn-success <?= $disableSave ? 'disabled' : '' ?>"
|
||||
<?= $disableSave ? 'disabled' : '' ?>
|
||||
title="<?php
|
||||
if (!$isEditable) {
|
||||
echo 'Editing is not allowed for this record.';
|
||||
}
|
||||
?>">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
</form>
|
||||
<?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>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</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>
|
||||
|
||||
<?php if ($withdrawableCount > 0): ?>
|
||||
<div class="d-flex justify-content-center mb-4">
|
||||
<button type="submit" class="btn btn-outline-danger" id="withdrawSubmitButton">Submit Withdrawal Request</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="modal fade" id="enrollmentFlowModal" tabindex="-1" aria-labelledby="enrollmentFlowModalLabel" aria-hidden="true" data-bs-backdrop="static">
|
||||
<div class="modal-dialog modal-xl modal-dialog-scrollable modal-fullscreen-sm-down">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-success text-white">
|
||||
<h5 class="modal-title" id="enrollmentFlowModalLabel">Enroll your childs</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="enrollment-stepper mb-3" aria-label="Enrollment steps">
|
||||
<div class="enrollment-step is-active" data-step-indicator="0">Students</div>
|
||||
<div class="enrollment-step" data-step-indicator="1">Policy</div>
|
||||
<div class="enrollment-step" data-step-indicator="2">Submit</div>
|
||||
</div>
|
||||
|
||||
<div data-step-panel="0">
|
||||
<h6 class="fw-semibold">Select student names</h6>
|
||||
<div class="text-muted small mb-3">Tap each student you want to enroll for <?= esc($selectedYear ?? 'the selected school year') ?>.</div>
|
||||
<div class="d-grid gap-2">
|
||||
<?php foreach ($students as $student): ?>
|
||||
<?php
|
||||
$eligibilityMessage = is_array($student['enrollment_eligibility_message'] ?? null) ? $student['enrollment_eligibility_message'] : ['message' => '', 'blocking' => false, 'level' => 'info'];
|
||||
$studentName = trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? ''));
|
||||
$studentName = $studentName !== '' ? $studentName : 'Student';
|
||||
$canEnroll = ($student['enrollment_status'] ?? '') === 'not enrolled'
|
||||
&& !$deadlinePassed
|
||||
&& $isEditable
|
||||
&& empty($eligibilityMessage['blocking']);
|
||||
$section = $student['class_section'] ?? null;
|
||||
$gradeLabel = $section
|
||||
? preg_replace_callback('/\byouth\b/i', fn($m) => ucfirst(strtolower($m[0])), (string) $section)
|
||||
: 'Not Assigned';
|
||||
?>
|
||||
<div class="student-select-card <?= $canEnroll ? '' : 'is-disabled' ?>"
|
||||
data-student-card
|
||||
data-student-id="<?= esc($student['id']) ?>"
|
||||
data-selectable="<?= $canEnroll ? '1' : '0' ?>">
|
||||
<div class="d-flex gap-3 align-items-start">
|
||||
<span class="student-select-icon" aria-hidden="true"><i class="bi bi-check2"></i></span>
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center">
|
||||
<div class="fw-semibold"><?= esc($studentName) ?></div>
|
||||
<?= $statusBadge($student['enrollment_status'] ?? '') ?>
|
||||
</div>
|
||||
<div class="small text-muted">Grade: <?= esc($gradeLabel) ?> · School ID: <?= esc($student['school_id'] ?? 'N/A') ?></div>
|
||||
<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']) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($canEnroll): ?>
|
||||
<input class="d-none" type="checkbox" name="enroll[]" value="<?= esc($student['id']) ?>" data-enroll-input>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-none" data-step-panel="1">
|
||||
<h6 class="fw-semibold">Policy info update</h6>
|
||||
<div class="text-muted small mb-3">Review the current school policies before submitting enrollment.</div>
|
||||
<iframe src="<?= base_url('policy/school_policy') ?>" class="enrollment-policy-frame" 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="d-none" data-step-panel="2">
|
||||
<h6 class="fw-semibold">Enroll submit</h6>
|
||||
<div class="text-muted small mb-3">Review the selected students, then submit enrollment.</div>
|
||||
<div id="enrollmentReviewList" class="d-grid gap-2"></div>
|
||||
<div class="alert alert-warning mt-3 mb-0 small">
|
||||
Submitting sends the selected enrollment(s) to admission review.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer enrollment-modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" id="enrollmentBackButton">Back</button>
|
||||
<button type="button" class="btn btn-success" id="enrollmentNextButton">Next</button>
|
||||
<button type="submit" class="btn btn-success d-none" id="enrollmentSubmitButton">Submit Enrollment</button>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<p>No students found for the selected school year. Please register your kids first.</p>
|
||||
<?php endif; ?>
|
||||
</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">
|
||||
<div class="modal-content">
|
||||
@@ -259,11 +421,8 @@ $money = static function ($amount) use ($familyFinancialSummary): string {
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Enrollment for the selected school year closed on
|
||||
<strong><?= esc($deadlineObj->format('m-d-Y')) ?></strong>.
|
||||
You can still request a withdrawal (if applicable), but new enrollments are no longer accepted.
|
||||
<br><br>
|
||||
If you believe this is an error, please contact the school office.
|
||||
Enrollment for the selected school year closed on <strong><?= esc($deadlineObj->format('m-d-Y')) ?></strong>.
|
||||
You can still request a withdrawal if applicable.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">OK</button>
|
||||
@@ -271,133 +430,164 @@ $money = static function ($amount) use ($familyFinancialSummary): string {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('scripts') ?>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// 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 form = document.getElementById('enrollmentFlowForm');
|
||||
const startButton = document.getElementById('startEnrollmentButton');
|
||||
const flowModalEl = document.getElementById('enrollmentFlowModal');
|
||||
const deadlineModalEl = document.getElementById('deadlineModal');
|
||||
const flowModal = flowModalEl ? new bootstrap.Modal(flowModalEl) : null;
|
||||
const deadlineModal = deadlineModalEl ? new bootstrap.Modal(deadlineModalEl) : 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 backButton = document.getElementById('enrollmentBackButton');
|
||||
const nextButton = document.getElementById('enrollmentNextButton');
|
||||
const submitButton = document.getElementById('enrollmentSubmitButton');
|
||||
const reviewList = document.getElementById('enrollmentReviewList');
|
||||
let currentStep = 0;
|
||||
let hasAcceptedSchoolPolicy = <?= $hasAcceptedSchoolPolicy ? 'true' : 'false' ?>;
|
||||
|
||||
const selectedEnrollInputs = () => form ? Array.from(form.querySelectorAll("input[name='enroll[]']:checked")) : [];
|
||||
const selectedWithdrawInputs = () => form ? Array.from(form.querySelectorAll("input[name='withdraw[]']:checked")) : [];
|
||||
|
||||
function setStep(step) {
|
||||
currentStep = Math.max(0, Math.min(2, step));
|
||||
document.querySelectorAll('[data-step-panel]').forEach(panel => {
|
||||
panel.classList.toggle('d-none', Number(panel.dataset.stepPanel) !== currentStep);
|
||||
});
|
||||
document.querySelectorAll('[data-step-indicator]').forEach(indicator => {
|
||||
indicator.classList.toggle('is-active', Number(indicator.dataset.stepIndicator) === currentStep);
|
||||
});
|
||||
backButton.classList.toggle('d-none', currentStep === 0);
|
||||
nextButton.classList.toggle('d-none', currentStep === 2);
|
||||
submitButton.classList.toggle('d-none', currentStep !== 2);
|
||||
if (currentStep === 2) {
|
||||
renderReview();
|
||||
}
|
||||
};
|
||||
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;
|
||||
function renderReview() {
|
||||
const cards = selectedEnrollInputs().map(input => {
|
||||
const card = input.closest('[data-student-card]');
|
||||
const name = card ? card.querySelector('.fw-semibold')?.textContent?.trim() : 'Student';
|
||||
const meta = card ? card.querySelector('.small.text-muted')?.textContent?.trim() : '';
|
||||
return '<div class="border rounded p-3"><div class="fw-semibold">' + escapeHtml(name || 'Student') + '</div><div class="small text-muted">' + escapeHtml(meta || '') + '</div></div>';
|
||||
});
|
||||
reviewList.innerHTML = cards.length ? cards.join('') : '<div class="alert alert-warning mb-0">No students selected.</div>';
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value).replace(/[&<>"']/g, function(char) {
|
||||
return ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[char];
|
||||
});
|
||||
}
|
||||
|
||||
if (acceptPolicyButton) {
|
||||
acceptPolicyButton.addEventListener('click', function() {
|
||||
if (policyAcceptedCheckbox && !policyAcceptedCheckbox.checked) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasAcceptedSchoolPolicy = true;
|
||||
if (policyAcceptedInput) {
|
||||
policyAcceptedInput.value = '1';
|
||||
}
|
||||
if (policyModal) {
|
||||
policyModal.hide();
|
||||
}
|
||||
});
|
||||
function syncPolicyAccepted() {
|
||||
hasAcceptedSchoolPolicy = !!(policyAcceptedCheckbox && policyAcceptedCheckbox.checked);
|
||||
if (policyAcceptedInput) {
|
||||
policyAcceptedInput.value = hasAcceptedSchoolPolicy ? '1' : '0';
|
||||
}
|
||||
}
|
||||
|
||||
// The enrollment form
|
||||
const form = document.querySelector("form[action*='enroll_classes_handler']");
|
||||
|
||||
// 1) Block clicking "enroll" checkboxes after deadline
|
||||
document.querySelectorAll("input[name='enroll[]']").forEach(cb => {
|
||||
cb.addEventListener("click", function(e) {
|
||||
if (startButton && flowModal) {
|
||||
startButton.addEventListener('click', function() {
|
||||
if (deadlinePassed) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
this.checked = false;
|
||||
showDeadlineModal();
|
||||
if (deadlineModal) deadlineModal.show();
|
||||
return;
|
||||
}
|
||||
setStep(0);
|
||||
flowModal.show();
|
||||
});
|
||||
}
|
||||
|
||||
if (!hasAcceptedSchoolPolicy) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
this.checked = false;
|
||||
showPolicyModal();
|
||||
document.querySelectorAll('[data-student-card]').forEach(card => {
|
||||
card.addEventListener('click', function() {
|
||||
if (this.dataset.selectable !== '1') {
|
||||
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);
|
||||
const input = this.querySelector('[data-enroll-input]');
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
input.checked = !input.checked;
|
||||
this.classList.toggle('is-selected', input.checked);
|
||||
});
|
||||
});
|
||||
|
||||
// 2) Prevent submission if any enroll[] is checked after deadline
|
||||
if (policyAcceptedCheckbox) {
|
||||
policyAcceptedCheckbox.addEventListener('change', syncPolicyAccepted);
|
||||
}
|
||||
|
||||
if (backButton) {
|
||||
backButton.addEventListener('click', function() {
|
||||
setStep(currentStep - 1);
|
||||
});
|
||||
}
|
||||
|
||||
if (nextButton) {
|
||||
nextButton.addEventListener('click', function() {
|
||||
if (currentStep === 0 && selectedEnrollInputs().length === 0) {
|
||||
alert('Please select at least one student to enroll.');
|
||||
return;
|
||||
}
|
||||
if (currentStep === 1) {
|
||||
syncPolicyAccepted();
|
||||
if (!hasAcceptedSchoolPolicy) {
|
||||
alert('Please read and accept the school policies before continuing.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
setStep(currentStep + 1);
|
||||
});
|
||||
}
|
||||
|
||||
if (form) {
|
||||
form.addEventListener("submit", function(e) {
|
||||
const anyBoxesChecked = form.querySelectorAll("input[name='enroll[]']:checked").length > 0;
|
||||
const anyWithdrawChecked = form.querySelectorAll("input[name='withdraw[]']:checked").length > 0;
|
||||
form.addEventListener('submit', function(e) {
|
||||
if (e.submitter && e.submitter.id === 'withdrawSubmitButton') {
|
||||
selectedEnrollInputs().forEach(input => {
|
||||
input.checked = false;
|
||||
input.closest('[data-student-card]')?.classList.remove('is-selected');
|
||||
});
|
||||
}
|
||||
|
||||
if (!anyBoxesChecked && !anyWithdrawChecked) {
|
||||
if (e.submitter && e.submitter.id === 'enrollmentSubmitButton') {
|
||||
selectedWithdrawInputs().forEach(input => {
|
||||
input.checked = false;
|
||||
});
|
||||
}
|
||||
|
||||
const anyEnroll = selectedEnrollInputs().length > 0;
|
||||
const anyWithdraw = selectedWithdrawInputs().length > 0;
|
||||
|
||||
syncPolicyAccepted();
|
||||
|
||||
if (!anyEnroll && !anyWithdraw) {
|
||||
e.preventDefault();
|
||||
alert("Please select at least one student to enroll or withdraw before saving.");
|
||||
alert('Please select at least one student to enroll or withdraw before submitting.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasAcceptedSchoolPolicy && anyBoxesChecked) {
|
||||
if (deadlinePassed && anyEnroll) {
|
||||
e.preventDefault();
|
||||
showPolicyModal();
|
||||
if (deadlineModal) deadlineModal.show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (deadlinePassed && anyBoxesChecked) {
|
||||
if (anyEnroll && !hasAcceptedSchoolPolicy) {
|
||||
e.preventDefault();
|
||||
showDeadlineModal();
|
||||
setStep(1);
|
||||
if (flowModal) flowModal.show();
|
||||
return;
|
||||
}
|
||||
|
||||
// 3) If only withdrawals are selected, ask for a quick confirmation
|
||||
if (!anyBoxesChecked && anyWithdrawChecked) {
|
||||
if (!anyEnroll && anyWithdraw) {
|
||||
const ok = confirm('Confirm withdrawal request for the selected student(s)?');
|
||||
if (!ok) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user