421 lines
27 KiB
PHP
421 lines
27 KiB
PHP
<?= $this->extend('layout/management_layout') ?>
|
|
<?= $this->section('content') ?>
|
|
|
|
<?php
|
|
$statusClasses = [
|
|
'draft' => 'bg-info text-dark',
|
|
'active' => 'bg-success',
|
|
'closing' => 'bg-warning text-dark',
|
|
'closed' => 'bg-secondary',
|
|
'archived' => 'bg-dark',
|
|
];
|
|
$formatDate = static fn ($value): string => $value ? esc((string) $value) : 'Not set';
|
|
$money = static fn ($value): string => '$' . number_format((float) $value, 2);
|
|
$activeId = (int) ($activeYear['id'] ?? 0);
|
|
$nextDraftId = (int) ($nextDraftYear['id'] ?? 0);
|
|
$closingPreviewUrl = $activeId > 0
|
|
? site_url('administrator/school-years/' . $activeId . '/closing/preview' . ($nextDraftId > 0 ? '?' . http_build_query(['target_school_year_id' => $nextDraftId]) : ''))
|
|
: '';
|
|
?>
|
|
|
|
<?php if (session()->getFlashdata('success')): ?>
|
|
<div class="alert alert-success"><?= esc(session()->getFlashdata('success')) ?></div>
|
|
<?php elseif (session()->getFlashdata('error')): ?>
|
|
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<div class="container-fluid py-3">
|
|
<div class="mb-3">
|
|
<div>
|
|
<h2 class="mb-1">School Years</h2>
|
|
<div class="text-muted">End the current school year after its checklist is clear, then start the next year.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="border rounded bg-white p-3 mb-4">
|
|
<div class="d-flex flex-wrap justify-content-between gap-3">
|
|
<div>
|
|
<h5 class="mb-2">End Current School Year</h5>
|
|
<div class="d-flex flex-wrap gap-2 mb-2">
|
|
<span class="badge <?= $activeYear ? 'bg-success' : 'bg-secondary' ?>">Current: <?= esc($activeYear['name'] ?? 'None') ?></span>
|
|
<span class="badge <?= $nextDraftYear ? 'bg-info text-dark' : 'bg-secondary' ?>">Next: <?= esc($nextDraftYear['name'] ?? 'Not created') ?></span>
|
|
<?php if ($closingYear): ?>
|
|
<span class="badge bg-warning text-dark">Ending: <?= esc($closingYear['name']) ?></span>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php if ($closingYear): ?>
|
|
<div class="text-muted">Finish the end-year checklist for <?= esc($closingYear['name']) ?> before starting another year.</div>
|
|
<?php elseif ($activeYear && $nextDraftYear): ?>
|
|
<div class="text-muted">Review promotions and balances for <?= esc($activeYear['name']) ?>. When all blockers are resolved, end it and start <?= esc($nextDraftYear['name']) ?>.</div>
|
|
<?php elseif ($activeYear): ?>
|
|
<div class="text-muted">Create the next school year first. It will stay as a draft until the current year is closed.</div>
|
|
<?php elseif ($nextDraftYear): ?>
|
|
<div class="text-muted">No year is active. Start <?= esc($nextDraftYear['name']) ?> when you are ready.</div>
|
|
<?php else: ?>
|
|
<div class="text-muted">Create a school year draft to begin.</div>
|
|
<?php endif; ?>
|
|
<ol class="small text-muted ps-3 mt-2 mb-0">
|
|
<li>Create the next year as a draft.</li>
|
|
<li>Review the end-year checklist for the current year.</li>
|
|
<li>End the current year, then start the next year.</li>
|
|
</ol>
|
|
</div>
|
|
<div class="d-flex align-items-start">
|
|
<?php if ($closingYear): ?>
|
|
<a class="btn btn-primary" href="<?= site_url('administrator/school-years/' . (int) $closingYear['id'] . '/closing/preview') ?>">Finish End-Year Checklist</a>
|
|
<?php elseif ($activeYear && $nextDraftYear): ?>
|
|
<a class="btn btn-primary" href="<?= esc($closingPreviewUrl, 'attr') ?>">Close Year</a>
|
|
<?php elseif ($activeYear): ?>
|
|
<button class="btn btn-success" type="button" data-bs-toggle="collapse" data-bs-target="#schoolYearCreateForm">Create Next Year</button>
|
|
<?php elseif ($nextDraftYear): ?>
|
|
<button class="btn btn-success" type="button" data-bs-toggle="modal" data-bs-target="#activateSchoolYearModal<?= $nextDraftId ?>">Start <?= esc($nextDraftYear['name']) ?></button>
|
|
<?php else: ?>
|
|
<button class="btn btn-success" type="button" data-bs-toggle="collapse" data-bs-target="#schoolYearCreateForm">Create School Year</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="collapse mb-4" id="schoolYearCreateForm">
|
|
<div class="border rounded bg-white p-3">
|
|
<form action="<?= site_url('administrator/school-years/store') ?>" method="post" class="row g-3 align-items-end">
|
|
<?= csrf_field() ?>
|
|
<div class="col-md-2">
|
|
<label class="form-label" for="new_school_year_name">School Year</label>
|
|
<input class="form-control" id="new_school_year_name" name="name" placeholder="2026-2027" required pattern="\d{4}-\d{4}">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label" for="new_previous_school_year_id">Previous Year</label>
|
|
<select class="form-select" id="new_previous_school_year_id" name="previous_school_year_id">
|
|
<option value="">None</option>
|
|
<?php foreach (($schoolYears ?? []) as $existingYear): ?>
|
|
<option value="<?= (int) ($existingYear['id'] ?? 0) ?>"><?= esc($existingYear['name'] ?? '') ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label" for="new_school_year_starts_on">Starts On</label>
|
|
<input class="form-control" id="new_school_year_starts_on" name="starts_on" type="date">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label" for="new_school_year_ends_on">Ends On</label>
|
|
<input class="form-control" id="new_school_year_ends_on" name="ends_on" type="date">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label" for="new_registration_starts_on">Registration Starts</label>
|
|
<input class="form-control" id="new_registration_starts_on" name="registration_starts_on" type="date">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label" for="new_registration_ends_on">Registration Ends</label>
|
|
<input class="form-control" id="new_registration_ends_on" name="registration_ends_on" type="date">
|
|
</div>
|
|
<div class="col-md-2 d-flex gap-2">
|
|
<button class="btn btn-primary" type="submit">Save Draft</button>
|
|
<button class="btn btn-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#schoolYearCreateForm">Cancel</button>
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label" for="new_school_year_description">Description</label>
|
|
<textarea class="form-control" id="new_school_year_description" name="description" rows="2"></textarea>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table id="schoolYearsTable" class="table table-striped table-hover align-middle">
|
|
<thead>
|
|
<tr>
|
|
<th>School Year</th>
|
|
<th>Dates</th>
|
|
<th>Status</th>
|
|
<th>Verify</th>
|
|
<th>Last Activity</th>
|
|
<th>Updated</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach (($schoolYears ?? []) as $year): ?>
|
|
<?php
|
|
$id = (int) ($year['id'] ?? 0);
|
|
$status = (string) ($year['status'] ?? 'draft');
|
|
$readonly = in_array($status, ['closed', 'archived'], true);
|
|
$transition = $latestTransitions[$id] ?? null;
|
|
$verify = $yearVerification[$id] ?? [];
|
|
$previousYearName = $schoolYearNamesById[(int) ($year['previous_school_year_id'] ?? 0)] ?? null;
|
|
?>
|
|
<tr>
|
|
<td class="fw-semibold"><?= esc($year['name'] ?? '') ?></td>
|
|
<td>
|
|
<div><?= $formatDate($year['starts_on'] ?? null) ?></div>
|
|
<div class="text-muted small"><?= $formatDate($year['ends_on'] ?? null) ?></div>
|
|
</td>
|
|
<td>
|
|
<span class="badge <?= esc($statusClasses[$status] ?? 'bg-secondary') ?>">
|
|
<?= esc(ucfirst($status)) ?>
|
|
</span>
|
|
</td>
|
|
<td class="small">
|
|
<div>Previous: <span class="fw-semibold"><?= esc($previousYearName ?? 'Not linked') ?></span></div>
|
|
<div>Students: <span class="fw-semibold"><?= esc((string) ($verify['students'] ?? 0)) ?></span></div>
|
|
<div>Promoted: <span class="fw-semibold"><?= esc((string) ($verify['promoted'] ?? 0)) ?></span> / Decisions: <?= esc((string) ($verify['decisions'] ?? 0)) ?></div>
|
|
<div>Queued: <span class="fw-semibold"><?= esc((string) ($verify['queued'] ?? 0)) ?></span></div>
|
|
<div>Carry-forward: <span class="fw-semibold"><?= esc($money($verify['carry_forward_balance'] ?? 0)) ?></span></div>
|
|
</td>
|
|
<td>
|
|
<?php if ($transition): ?>
|
|
<div><?= esc($transition['action'] ?? '') ?></div>
|
|
<div class="text-muted small"><?= esc($transition['created_at'] ?? '') ?></div>
|
|
<?php else: ?>
|
|
<span class="text-muted">-</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td><?= esc($year['updated_at'] ?? '') ?></td>
|
|
<td>
|
|
<div class="dropdown">
|
|
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
|
Actions
|
|
</button>
|
|
<ul class="dropdown-menu dropdown-menu-end">
|
|
<?php if (! $readonly && $status !== 'closing'): ?>
|
|
<li>
|
|
<button class="dropdown-item" type="button" data-bs-toggle="modal" data-bs-target="#editSchoolYearModal<?= $id ?>">
|
|
Edit metadata
|
|
</button>
|
|
</li>
|
|
<?php endif; ?>
|
|
<?php if ($status === 'draft'): ?>
|
|
<?php if (empty($activeYear) && empty($closingYear)): ?>
|
|
<li>
|
|
<button class="dropdown-item" type="button" data-bs-toggle="modal" data-bs-target="#activateSchoolYearModal<?= $id ?>">
|
|
Start this year
|
|
</button>
|
|
</li>
|
|
<?php endif; ?>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li>
|
|
<form action="<?= site_url('administrator/school-years/' . $id . '/delete-draft') ?>" method="post" onsubmit="return confirm('Delete this unused draft school year?');">
|
|
<?= csrf_field() ?>
|
|
<button class="dropdown-item text-danger" type="submit">Delete draft</button>
|
|
</form>
|
|
</li>
|
|
<?php elseif ($status === 'active'): ?>
|
|
<li>
|
|
<a class="dropdown-item" href="<?= site_url('administrator/school-years/' . $id . '/closing/preview' . ($nextDraftId > 0 ? '?' . http_build_query(['target_school_year_id' => $nextDraftId]) : '')) ?>">Close year</a>
|
|
</li>
|
|
<?php elseif ($status === 'closing'): ?>
|
|
<li>
|
|
<a class="dropdown-item" href="<?= site_url('administrator/school-years/' . $id . '/closing/preview') ?>">Finish end-year checklist</a>
|
|
</li>
|
|
<li>
|
|
<form action="<?= site_url('administrator/school-years/' . $id . '/closing/cancel') ?>" method="post" onsubmit="return confirm('Cancel the end-year process for this school year?');">
|
|
<?= csrf_field() ?>
|
|
<button class="dropdown-item" type="submit">Cancel end-year process</button>
|
|
</form>
|
|
</li>
|
|
<?php elseif ($status === 'closed'): ?>
|
|
<li>
|
|
<a class="dropdown-item" href="<?= site_url('administrator/school-years/' . $id . '/closing/preview') ?>">View end-year report</a>
|
|
</li>
|
|
<li>
|
|
<button class="dropdown-item" type="button" data-bs-toggle="modal" data-bs-target="#reopenSchoolYearModal<?= $id ?>">
|
|
Reopen
|
|
</button>
|
|
</li>
|
|
<li>
|
|
<form action="<?= site_url('administrator/school-years/' . $id . '/archive') ?>" method="post" onsubmit="return confirm('Archive this closed school year?');">
|
|
<?= csrf_field() ?>
|
|
<button class="dropdown-item" type="submit">Archive</button>
|
|
</form>
|
|
</li>
|
|
<?php else: ?>
|
|
<li>
|
|
<a class="dropdown-item" href="<?= site_url('administrator/school-years/' . $id . '/closing/preview') ?>">View audit trail</a>
|
|
</li>
|
|
<?php endif; ?>
|
|
</ul>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<?php foreach (($schoolYears ?? []) as $year): ?>
|
|
<?php
|
|
$id = (int) ($year['id'] ?? 0);
|
|
$status = (string) ($year['status'] ?? 'draft');
|
|
$readonly = in_array($status, ['closed', 'archived', 'closing'], true);
|
|
$verify = $yearVerification[$id] ?? [];
|
|
$previousYearName = $schoolYearNamesById[(int) ($year['previous_school_year_id'] ?? 0)] ?? null;
|
|
?>
|
|
<?php if (! $readonly): ?>
|
|
<div class="modal fade" id="editSchoolYearModal<?= $id ?>" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<form class="modal-content" action="<?= site_url('administrator/school-years/' . $id . '/update') ?>" method="post">
|
|
<?= csrf_field() ?>
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Edit School Year Metadata</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="border rounded bg-light p-3 mb-3">
|
|
<h6 class="mb-2">Current Verification Data</h6>
|
|
<div class="row g-2 small">
|
|
<div class="col-md-4">
|
|
<div class="text-muted">Previous year</div>
|
|
<div class="fw-semibold"><?= esc($previousYearName ?? 'Not linked') ?></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-muted">Students in year</div>
|
|
<div class="fw-semibold"><?= esc((string) ($verify['students'] ?? 0)) ?></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-muted">Promotion decisions</div>
|
|
<div class="fw-semibold"><?= esc((string) ($verify['decisions'] ?? 0)) ?></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-muted">Promoted students</div>
|
|
<div class="fw-semibold"><?= esc((string) ($verify['promoted'] ?? 0)) ?></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-muted">Queued for next year</div>
|
|
<div class="fw-semibold"><?= esc((string) ($verify['queued'] ?? 0)) ?></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-muted">Carry-forward balance</div>
|
|
<div class="fw-semibold"><?= esc($money($verify['carry_forward_balance'] ?? 0)) ?></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-muted">Positive balances</div>
|
|
<div class="fw-semibold"><?= esc($money($verify['positive_balance'] ?? 0)) ?></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-muted">Credits</div>
|
|
<div class="fw-semibold"><?= esc($money($verify['credit_balance'] ?? 0)) ?></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-muted">End-year status</div>
|
|
<div class="fw-semibold"><?= esc(($verify['closing_status'] ?? '') !== '' ? ucfirst((string) $verify['closing_status']) : 'Not started') ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label" for="school_year_name_<?= $id ?>">School Year</label>
|
|
<input class="form-control" id="school_year_name_<?= $id ?>" name="name" value="<?= esc($year['name'] ?? '') ?>" required pattern="\d{4}-\d{4}">
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label" for="school_year_starts_on_<?= $id ?>">Starts On</label>
|
|
<input class="form-control" id="school_year_starts_on_<?= $id ?>" name="starts_on" type="date" value="<?= esc($year['starts_on'] ?? '') ?>">
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label" for="school_year_ends_on_<?= $id ?>">Ends On</label>
|
|
<input class="form-control" id="school_year_ends_on_<?= $id ?>" name="ends_on" type="date" value="<?= esc($year['ends_on'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label" for="previous_school_year_id_<?= $id ?>">Previous Year</label>
|
|
<select class="form-select" id="previous_school_year_id_<?= $id ?>" name="previous_school_year_id">
|
|
<option value="">None</option>
|
|
<?php foreach (($schoolYears ?? []) as $existingYear): ?>
|
|
<?php if ((int) ($existingYear['id'] ?? 0) !== $id): ?>
|
|
<option value="<?= (int) ($existingYear['id'] ?? 0) ?>" <?= (int) ($year['previous_school_year_id'] ?? 0) === (int) ($existingYear['id'] ?? 0) ? 'selected' : '' ?>>
|
|
<?= esc($existingYear['name'] ?? '') ?>
|
|
</option>
|
|
<?php endif; ?>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label" for="registration_starts_on_<?= $id ?>">Registration Starts</label>
|
|
<input class="form-control" id="registration_starts_on_<?= $id ?>" name="registration_starts_on" type="date" value="<?= esc($year['registration_starts_on'] ?? '') ?>">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label" for="registration_ends_on_<?= $id ?>">Registration Ends</label>
|
|
<input class="form-control" id="registration_ends_on_<?= $id ?>" name="registration_ends_on" type="date" value="<?= esc($year['registration_ends_on'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
<label class="form-label" for="description_<?= $id ?>">Description</label>
|
|
<textarea class="form-control" id="description_<?= $id ?>" name="description" rows="3"><?= esc($year['description'] ?? '') ?></textarea>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($status === 'draft'): ?>
|
|
<div class="modal fade" id="activateSchoolYearModal<?= $id ?>" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<form class="modal-content" action="<?= site_url('administrator/school-years/' . $id . '/activate') ?>" method="post">
|
|
<?= csrf_field() ?>
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Start <?= esc($year['name'] ?? '') ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p class="mb-2">This school year will become the active year.</p>
|
|
<?php if (! empty($activeYear)): ?>
|
|
<p class="mb-3">Current active year <strong><?= esc($activeYear['name']) ?></strong> will move to <strong>end-year review</strong>.</p>
|
|
<?php endif; ?>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" value="1" name="confirm_activation" id="confirm_activation_<?= $id ?>" required>
|
|
<label class="form-check-label" for="confirm_activation_<?= $id ?>">
|
|
I understand this starts the selected year.
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-success">Start Year</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($status === 'closed'): ?>
|
|
<div class="modal fade" id="reopenSchoolYearModal<?= $id ?>" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<form class="modal-content" action="<?= site_url('administrator/school-years/' . $id . '/reopen') ?>" method="post">
|
|
<?= csrf_field() ?>
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Reopen <?= esc($year['name'] ?? '') ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<label class="form-label" for="reopen_reason_<?= $id ?>">Reason</label>
|
|
<textarea class="form-control" id="reopen_reason_<?= $id ?>" name="reason" rows="3" required></textarea>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-warning">Reopen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php endforeach; ?>
|
|
|
|
<?= $this->endSection() ?>
|
|
|
|
<?= $this->section('scripts') ?>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
if (window.jQuery && window.jQuery.fn && window.jQuery.fn.DataTable) {
|
|
window.jQuery('#schoolYearsTable').DataTable({
|
|
pageLength: 25,
|
|
order: [[0, 'desc']]
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
<?= $this->endSection() ?>
|