recreate project

This commit is contained in:
root
2026-02-10 22:11:06 -05:00
commit 663c0cdbda
10149 changed files with 1379710 additions and 0 deletions
@@ -0,0 +1,161 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-xxl py-4">
<div class="container">
<div class="text-center mx-auto mb-4" style="max-width:700px;">
<h4 class="text-dark" style="font-family:Arial,sans-serif;">TimeOff Request</h4>
<div class="text-muted">Logged in as: <strong><?= esc($admin_name ?? 'Administrator') ?></strong></div>
<?php if (!empty($semester) && !empty($schoolYear)): ?>
<div class="small text-muted">Term: <?= esc($semester) ?> — <?= esc($schoolYear) ?></div>
<?php endif; ?>
</div>
<?php if (session()->getFlashdata('message')): ?>
<?php $status = session()->getFlashdata('status') ?? 'info';
$cls = $status === 'success' ? 'alert-success' : ($status === 'error' ? 'alert-danger' : 'alert-info'); ?>
<div class="alert <?= $cls ?> text-center" role="alert">
<?= esc(session()->getFlashdata('message')) ?>
</div>
<?php endif; ?>
<div class="card shadow-sm mb-4">
<div class="card-header bg-light"><strong>Submit Upcoming Absences</strong></div>
<div class="card-body">
<form action="<?= site_url('/administrator/absence') ?>" method="post" id="absenceForm">
<?= csrf_field() ?>
<?php $hasDates = !empty($availableDates) && is_array($availableDates); ?>
<div class="mb-3">
<label class="form-label">Select Sunday Date(s)</label>
<div id="datesContainer">
<div class="d-flex align-items-center gap-2 mb-2 date-row">
<select name="dates[]" class="form-select" style="max-width:260px;" <?= $hasDates ? '' : 'disabled' ?> required>
<option value="" disabled selected>Select Sunday date</option>
<?php foreach (($availableDates ?? []) as $d): ?>
<option value="<?= esc($d) ?>"><?= esc($d) ?></option>
<?php endforeach; ?>
</select>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="addDateRow()" <?= $hasDates ? '' : 'disabled' ?>>
<i class="bi bi-plus-lg"></i> Add another
</button>
</div>
</div>
<div class="form-text">Future Sundays from September to May are available only.</div>
<?php if (!$hasDates): ?>
<div class="alert alert-info mt-2">No future Sundays are available in the current term.</div>
<?php endif; ?>
</div>
<div class="row g-3 mb-3">
<div class="col-sm-6">
<label class="form-label" for="reason_type">Reason Type</label>
<select id="reason_type" name="reason_type" class="form-select">
<option value="vacation">Vacation</option>
<option value="sick">Sick</option>
<option value="personal">Personal</option>
<option value="other">Other</option>
</select>
</div>
<div class="col-sm-6">
<label class="form-label" for="reason">Reason <span class="text-danger">*</span></label>
<input type="text" id="reason" name="reason" class="form-control" placeholder="Provide a reason" required>
</div>
</div>
<div class="d-flex justify-content-center">
<button type="submit" class="btn btn-primary px-5" <?= $hasDates ? '' : 'disabled' ?> >
Submit Absence
</button>
</div>
</form>
</div>
</div>
<div class="card shadow-sm">
<div class="card-header bg-light"><strong>Your Reported Status (This Term)</strong></div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-bordered table-striped m-0 align-middle no-mgmt-sticky wrap-headers">
<thead class="table-light">
<tr>
<th class="text-center" style="width:80px;">#</th>
<th>Date</th>
<th>Status</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
<?php if (!empty($existing)): ?>
<?php $i=1; foreach ($existing as $row): ?>
<tr>
<td class="text-center"><?= $i++ ?></td>
<td><?= esc(!empty($row['date']) ? local_date($row['date'], 'm-d-Y') : '') ?></td>
<td>
<?php $st = strtolower((string)($row['status'] ?? ''));
$badge = $st==='absent'?'danger':($st==='late'?'warning text-dark':'success'); ?>
<span class="badge bg-<?= $badge ?>"><?= ucfirst($st ?: 'n/a') ?></span>
</td>
<td><?= esc($row['reason'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="4" class="text-center">No reported entries yet.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('styles') ?>
<style>
/* Allow header labels to wrap if needed on smaller screens */
.wrap-headers thead th { white-space: normal !important; }
</style>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
const AVAILABLE_DATES = <?= json_encode(array_values($availableDates ?? [])) ?>;
function addDateRow() {
if (!AVAILABLE_DATES || AVAILABLE_DATES.length === 0) return;
const container = document.getElementById('datesContainer');
const row = document.createElement('div');
row.className = 'd-flex align-items-center gap-2 mb-2 date-row';
const select = document.createElement('select');
select.name = 'dates[]';
select.className = 'form-select';
select.style.maxWidth = '260px';
select.required = true;
const def = document.createElement('option');
def.value = '';
def.disabled = true;
def.selected = true;
def.textContent = 'Select Sunday date';
select.appendChild(def);
AVAILABLE_DATES.forEach(function(d) {
const opt = document.createElement('option');
opt.value = d;
opt.textContent = d;
select.appendChild(opt);
});
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'btn btn-outline-danger btn-sm';
btn.innerHTML = '<i class="bi bi-x-lg"></i> Remove';
btn.addEventListener('click', function(){ row.remove(); });
row.appendChild(select);
row.appendChild(btn);
container.appendChild(row);
}
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,45 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
< class="wrapper">
<div class="content"></div>
<h1>Academic Performance</h1>
<?= $this->include('partials/academic_filter') ?>
<div class="table-responsive">
<table class="table table-bordered">
<thead></thead>
<tr>
<th>Student ID</th>
<th>Name</th>
<th>Subject</th>
<th>Grade</th>
<th>Term</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$academicPerformance = [
['student_id' => 'S001', 'name' => 'John Doe', 'subject' => 'Mathematics', 'grade' => 'A', 'term' => '2024 Q1'],
['student_id' => 'S002', 'name' => 'Jane Smith', 'subject' => 'English', 'grade' => 'B+', 'term' => '2024 Q1'],
['student_id' => 'S003', 'name' => 'Michael Johnson', 'subject' => 'Science', 'grade' => 'A-', 'term' => '2024 Q1'],
// Add more records as needed
];
foreach ($academicPerformance as $performance) {
echo "<tr>
<td>{$performance['student_id']}</td>
<td>{$performance['name']}</td>
<td>{$performance['subject']}</td>
<td>{$performance['grade']}</td>
<td>{$performance['term']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,42 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>administrator Attendance Records</h1>
<table class="table table-striped">
<thead>
<tr>
<th>administrator Name</th>
<th>Date</th>
<th>Time In</th>
<th>Time Out</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in real scenario, this will be fetched from the database
$attendanceRecords = [
['name' => 'John Doe', 'date' => '2024-06-01', 'time_in' => '09:00 AM', 'time_out' => '05:00 PM', 'status' => 'Present'],
['name' => 'Jane Smith', 'date' => '2024-06-01', 'time_in' => '09:15 AM', 'time_out' => '05:15 PM', 'status' => 'Present'],
['name' => 'Alice Johnson', 'date' => '2024-06-01', 'time_in' => '09:30 AM', 'time_out' => '04:45 PM', 'status' => 'Late'],
// Add more records as needed
];
foreach ($attendanceRecords as $record) {
$dateDisp = !empty($record['date']) ? local_date($record['date'], 'm-d-Y') : '';
echo "<tr>
<td>{$record['name']}</td>
<td>" . esc($dateDisp) . "</td>
<td>{$record['time_in']}</td>
<td>{$record['time_out']}</td>
<td>{$record['status']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,44 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>administrator Profiles</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Role</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$administratorProfiles = [
['name' => 'John Doe', 'email' => 'john.doe@example.com', 'role' => 'Super administrator', 'status' => 'Active'],
['name' => 'Jane Smith', 'email' => 'jane.smith@example.com', 'role' => 'administrator', 'status' => 'Inactive'],
['name' => 'Emily Johnson', 'email' => 'emily.johnson@example.com', 'role' => 'administrator', 'status' => 'Active'],
// Add more profiles as needed
];
foreach ($administratorProfiles as $profile) {
echo "<tr>
<td>{$profile['name']}</td>
<td>{$profile['email']}</td>
<td>{$profile['role']}</td>
<td>{$profile['status']}</td>
<td>
<a href='/administrator/administrator_profiles/edit/{$profile['email']}' class='btn btn-primary btn-sm'>Edit</a>
<a href='/administrator/administrator_profiles/delete/{$profile['email']}' class='btn btn-danger btn-sm'>Delete</a>
</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,706 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<?php
helper('url');
$dashboardEndpoint = $dashboardEndpoint ?? site_url('api/administrator/dashboard');
?>
<style>
/* --- Circular Stats --- */
.stat-circle {
position: relative;
width: 320px;
height: 320px;
border-radius: 50%;
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #dff0cfff;
font-weight: 700;
box-shadow: 0 6px 18px rgba(0, 0, 0, .15);
transition: transform .2s ease, box-shadow .2s ease;
}
.stat-circle:hover {
transform: scale(1.05);
box-shadow: 0 10px 28px rgba(0, 0, 0, .2);
}
.stat-value {
font-size: 2rem;
line-height: 1;
}
.stat-icon {
font-size: 2.5rem;
margin-bottom: .25rem;
opacity: .85;
}
.stat-title {
font-size: 1.6rem;
margin-top: .25rem;
font-weight: 500;
opacity: .9;
text-align: center;
line-height: 1.2;
word-break: break-word;
}
.circle-primary {
background: radial-gradient(circle at 30% 30%, #0d6efd, #0044aa);
}
.circle-success {
background: radial-gradient(circle at 30% 30%, #198754, #0c4f2c);
}
.circle-warning {
background: radial-gradient(circle at 30% 30%, #daa544ff, #a66f00);
}
.circle-info {
background: radial-gradient(circle at 30% 30%, #0dcaf0, #047e9c);
}
.circle-light {
background: radial-gradient(circle at 30% 30%, #63af4cff, #00eb7dff);
}
.stats-row {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
justify-content: center;
}
/* Search results */
.sr-badge {
font-size: .825rem;
}
.small-muted {
font-size: .875rem;
color: #6c757d;
}
.admin-dashboard table.mgmt-sticky > thead > tr > th,
.admin-dashboard table.mgmt-sticky > thead > tr > td,
.admin-dashboard table thead th,
.admin-dashboard table thead td {
position: static !important;
top: auto !important;
z-index: auto !important;
box-shadow: none !important;
}
</style>
<div class="container-fluid admin-dashboard" data-dashboard-endpoint="<?= esc($dashboardEndpoint) ?>">
<div class="wrapper">
<div class="content"></div>
<main>
<!-- === Welcome Section === -->
<section class="mb-4">
<h2>Welcome, <?= esc((string)session()->get('firstname')) . ' ' . esc((string)session()->get('lastname')); ?></h2>
<p>Here is an overview of your site's activity.</p>
</section>
<!-- === Search Section === -->
<section class="mb-5">
<h2 class="mb-3">Search</h2>
<div id="dashboardAlert" class="alert alert-danger d-none" role="alert"></div>
<form action="<?= site_url('administrator/administratordashboard') ?>" method="get" class="mb-3">
<div class="input-group">
<input type="text"
class="form-control"
placeholder="Search name, email, phone, school ID, RFID…"
name="query"
value="<?= esc((string)($query ?? '')) ?>"
required>
<button class="btn btn-outline-secondary" type="submit">Search</button>
</div>
<div class="form-text">Supports Users, Parents, Staff, Students, and Emergency Contacts.</div>
</form>
<?php
// Convenience flags
$hasResults = !empty($results);
$isMerged = $hasResults && array_is_list($results); // new bundle format
$isLegacy = $hasResults && !$isMerged; // old flat arrays
?>
<?php if (!empty($query)): ?>
<div class="mb-2 small-muted">
Showing results for: <strong><?= esc($query) ?></strong>
</div>
<?php endif; ?>
<?php if ($hasResults): ?>
<?php if ($isMerged): ?>
<!-- ===== New merged bundle format ===== -->
<div class="accordion" id="searchResultsAccordion">
<?php foreach ($results as $idx => $bundle): ?>
<?php
$u = $bundle['user'] ?? null;
$fullName = trim(($u['firstname'] ?? '') . ' ' . ($u['lastname'] ?? ''));
$title = $fullName !== '' ? $fullName : 'User ID ' . (int)($u['id'] ?? 0);
$badgeCount =
count($bundle['students'] ?? []) +
count($bundle['emergency_contacts'] ?? []) +
count($bundle['parents'] ?? []) +
count($bundle['staff'] ?? []);
$headerId = 'sr-head-' . $idx;
$bodyId = 'sr-body-' . $idx;
?>
<div class="accordion-item mb-2">
<h2 class="accordion-header" id="<?= esc($headerId) ?>">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
data-bs-target="#<?= esc($bodyId) ?>" aria-expanded="false"
aria-controls="<?= esc($bodyId) ?>">
<div class="d-flex flex-column w-100">
<div class="d-flex align-items-center justify-content-between w-100">
<span>
<i class="bi bi-person-circle me-2"></i>
<?= esc($title) ?>
</span>
<span class="badge bg-primary sr-badge"><?= (int)$badgeCount ?></span>
</div>
<div class="small text-muted">
<?php if ($u): ?>
<?= esc($u['email'] ?? '') ?>
<?php if (!empty($u['cellphone'])): ?>
· <?= esc($u['cellphone']) ?>
<?php endif; ?>
<?php if (!empty($u['city']) || !empty($u['state'])): ?>
· <?= esc(trim(($u['city'] ?? '') . ', ' . ($u['state'] ?? ''), ', ')) ?>
<?php endif; ?>
<?php else: ?>
<em>No user profile row; matched via family/students.</em>
<?php endif; ?>
</div>
</div>
</button>
</h2>
<div id="<?= esc($bodyId) ?>" class="accordion-collapse collapse" aria-labelledby="<?= esc($headerId) ?>" data-bs-parent="#searchResultsAccordion">
<div class="accordion-body">
<!-- Parents / Family -->
<?php if (!empty($bundle['parents'])): ?>
<h6 class="mb-2"><i class="bi bi-people me-1"></i> Family</h6>
<div class="table-responsive mb-3">
<table class="table table-sm align-middle">
<thead>
<tr>
<th>#</th>
<th>First Parent ID</th>
<th>Second Parent</th>
<th>Email</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<?php foreach ($bundle['parents'] as $i => $p): ?>
<tr>
<td><?= $i + 1 ?></td>
<td><?= esc($p['firstparent_id']) ?></td>
<td><?= esc(trim(($p['secondparent_firstname'] ?? '') . ' ' . ($p['secondparent_lastname'] ?? ''))) ?></td>
<td><?= esc($p['secondparent_email'] ?? '') ?></td>
<td><?= esc($p['secondparent_phone'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Students -->
<?php if (!empty($bundle['students'])): ?>
<h6 class="mb-2"><i class="bi bi-mortarboard me-1"></i> Students</h6>
<div class="table-responsive mb-3">
<table class="table table-sm align-middle">
<thead>
<tr>
<th>#</th>
<th>School ID</th>
<th>Name</th>
<th>Gender</th>
<th>DOB</th>
<th>RFID</th>
</tr>
</thead>
<tbody>
<?php foreach ($bundle['students'] as $i => $s): ?>
<tr>
<td><?= $i + 1 ?></td>
<td><?= esc($s['school_id']) ?></td>
<td><?= esc(trim(($s['firstname'] ?? '') . ' ' . ($s['lastname'] ?? ''))) ?></td>
<td><?= esc($s['gender'] ?? '') ?></td>
<td><?= esc($s['dob'] ?? '') ?></td>
<td><?= esc($s['rfid_tag'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Emergency Contacts -->
<?php if (!empty($bundle['emergency_contacts'])): ?>
<h6 class="mb-2"><i class="bi bi-telephone-outbound me-1"></i> Emergency Contacts</h6>
<div class="table-responsive mb-3">
<table class="table table-sm align-middle">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Relation</th>
<th>Phone</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<?php foreach ($bundle['emergency_contacts'] as $i => $e): ?>
<tr>
<td><?= $i + 1 ?></td>
<td><?= esc($e['emergency_contact_name'] ?? '') ?></td>
<td><?= esc($e['relation'] ?? '') ?></td>
<td><?= esc($e['cellphone'] ?? '') ?></td>
<td><?= esc($e['email'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Staff roles -->
<?php if (!empty($bundle['staff'])): ?>
<h6 class="mb-2"><i class="bi bi-person-badge me-1"></i> Staff</h6>
<div class="table-responsive">
<table class="table table-sm align-middle">
<thead>
<tr>
<th>#</th>
<th>Role</th>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($bundle['staff'] as $i => $st): ?>
<tr>
<td><?= $i + 1 ?></td>
<td><?= esc($st['role_name'] ?? '') ?></td>
<td><?= esc(trim(($st['firstname'] ?? '') . ' ' . ($st['lastname'] ?? ''))) ?></td>
<td><?= esc($st['email'] ?? '') ?></td>
<td><?= esc($st['phone'] ?? '') ?></td>
<td><?= esc($st['active_role'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if (empty($bundle['parents']) && empty($bundle['students']) && empty($bundle['emergency_contacts']) && empty($bundle['staff'])): ?>
<div class="text-muted">No related records.</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php elseif ($isLegacy): ?>
<!-- ===== Legacy flat format fallback ===== -->
<div class="row g-3">
<div class="col-12 col-lg-6">
<div class="card">
<div class="card-header"><i class="bi bi-people"></i> Users</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<?php if (!empty($results['users'])): ?>
<?php foreach ($results['users'] as $i => $r): ?>
<tr>
<td><?= $i + 1 ?></td>
<td><?= esc(trim(($r['firstname'] ?? '') . ' ' . ($r['lastname'] ?? ''))) ?></td>
<td><?= esc($r['email'] ?? '') ?></td>
<td><?= esc($r['cellphone'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="4" class="text-muted">No users found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6">
<div class="card">
<div class="card-header"><i class="bi bi-mortarboard"></i> Students</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead>
<tr>
<th>#</th>
<th>School ID</th>
<th>Name</th>
<th>DOB</th>
</tr>
</thead>
<tbody>
<?php if (!empty($results['students'])): ?>
<?php foreach ($results['students'] as $i => $s): ?>
<tr>
<td><?= $i + 1 ?></td>
<td><?= esc($s['school_id'] ?? '') ?></td>
<td><?= esc(trim(($s['firstname'] ?? '') . ' ' . ($s['lastname'] ?? ''))) ?></td>
<td><?= esc($s['dob'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="4" class="text-muted">No students found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6">
<div class="card">
<div class="card-header"><i class="bi bi-people"></i> Parents</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead>
<tr>
<th>#</th>
<th>First Parent ID</th>
<th>Second Parent</th>
<th>Email</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<?php if (!empty($results['parents'])): ?>
<?php foreach ($results['parents'] as $i => $p): ?>
<tr>
<td><?= $i + 1 ?></td>
<td><?= esc($p['firstparent_id'] ?? '') ?></td>
<td><?= esc(trim(($p['secondparent_firstname'] ?? '') . ' ' . ($p['secondparent_lastname'] ?? ''))) ?></td>
<td><?= esc($p['secondparent_email'] ?? '') ?></td>
<td><?= esc($p['secondparent_phone'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5" class="text-muted">No parents found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6">
<div class="card">
<div class="card-header"><i class="bi bi-person-badge"></i> Staff</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead>
<tr>
<th>#</th>
<th>Role</th>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php if (!empty($results['staff'])): ?>
<?php foreach ($results['staff'] as $i => $st): ?>
<tr>
<td><?= $i + 1 ?></td>
<td><?= esc($st['role_name'] ?? '') ?></td>
<td><?= esc(trim(($st['firstname'] ?? '') . ' ' . ($st['lastname'] ?? ''))) ?></td>
<td><?= esc($st['email'] ?? '') ?></td>
<td><?= esc($st['phone'] ?? '') ?></td>
<td><?= esc($st['active_role'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="6" class="text-muted">No staff found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-header"><i class="bi bi-telephone-outbound"></i> Emergency Contacts</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Relation</th>
<th>Phone</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<?php if (!empty($results['emergency_contacts'])): ?>
<?php foreach ($results['emergency_contacts'] as $i => $e): ?>
<tr>
<td><?= $i + 1 ?></td>
<td><?= esc($e['emergency_contact_name'] ?? '') ?></td>
<td><?= esc($e['relation'] ?? '') ?></td>
<td><?= esc($e['cellphone'] ?? '') ?></td>
<td><?= esc($e['email'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5" class="text-muted">No emergency contacts found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<?php elseif (!empty($query)): ?>
<div class="alert alert-warning mb-0">No matches found.</div>
<?php endif; ?>
</section>
<!-- === Statistics Section (circular design) === -->
<section class="mb-5">
<h2 class="mb-4 text-center">Statistics</h2>
<div class="stats-row">
<!-- Students -->
<div class="stat-circle circle-primary">
<div class="stat-icon"><i class="bi bi-people-fill"></i></div>
<div class="stat-value" data-stat="students">—</div>
<div class="stat-title">Students</div>
</div>
<!-- Teachers -->
<div class="stat-circle circle-info">
<div class="stat-icon"><i class="bi bi-person-video2"></i></div>
<div class="stat-value" data-stat="teachers">—</div>
<div class="stat-title">Teachers</div>
</div>
<!-- Teacher Assistants -->
<div class="stat-circle circle-success">
<div class="stat-icon"><i class="bi bi-person-badge-fill"></i></div>
<div class="stat-value" data-stat="teacherAssistants">—</div>
<div class="stat-title">Teachers' Assistants</div>
</div>
<!-- Admins -->
<div class="stat-circle circle-warning">
<div class="stat-icon"><i class="bi bi-shield-lock-fill"></i></div>
<div class="stat-value" data-stat="admins">—</div>
<div class="stat-title">Admins</div>
</div>
<!-- Parents -->
<div class="stat-circle circle-light">
<div class="stat-icon"><i class="bi bi-people"></i></div>
<div class="stat-value" data-stat="parents">—</div>
<div class="stat-title">Parents</div>
</div>
</div>
</section>
<!-- === Recent Activities Section === -->
<section>
<h2 class="mb-3">Recent Activities</h2>
<table class="table mt-2" data-no-mgmt-sticky>
<thead>
<tr>
<th>Date</th>
<th>Activity</th>
<th>User</th>
</tr>
</thead>
<tbody data-activities-body>
<tr data-activities-placeholder>
<td colspan="3" class="text-muted">Loading recent activity…</td>
</tr>
</tbody>
</table>
</section>
</main>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
const container = document.querySelector('[data-dashboard-endpoint]');
if (!container) {
return;
}
const endpoint = container.dataset.dashboardEndpoint;
const alertBox = document.getElementById('dashboardAlert');
const numberFormatter = new Intl.NumberFormat();
const statElements = container.querySelectorAll('[data-stat]');
const activitiesBody = container.querySelector('[data-activities-body]');
const showError = (message) => {
if (!alertBox) {
return;
}
alertBox.textContent = message;
alertBox.classList.remove('d-none');
};
fetch(endpoint, {
headers: {
'Accept': 'application/json',
},
credentials: 'same-origin',
})
.then((response) => {
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}
return response.json();
})
.then((payload) => {
const counts = payload && typeof payload === 'object' && payload.counts
? payload.counts
: {};
statElements.forEach((element) => {
const key = element.dataset.stat;
if (!key) {
return;
}
const value = counts[key];
element.textContent = Number.isFinite(value)
? numberFormatter.format(value)
: '—';
});
if (!activitiesBody) {
return;
}
activitiesBody.innerHTML = '';
const activities = payload && Array.isArray(payload.recentActivities)
? payload.recentActivities
: [];
if (activities.length === 0) {
const row = document.createElement('tr');
const cell = document.createElement('td');
cell.colSpan = 3;
cell.classList.add('text-muted');
cell.textContent = 'No recent activities found.';
row.appendChild(cell);
activitiesBody.appendChild(row);
return;
}
const formatDateTime = (value) => {
if (!value) {
return '—';
}
const date = new Date(value);
if (Number.isNaN(date.valueOf())) {
return String(value);
}
const pad = (num) => String(num).padStart(2, '0');
const datePart = `${pad(date.getMonth() + 1)}-${pad(date.getDate())}-${date.getFullYear()}`;
const timePart = `${pad(date.getHours())}:${pad(date.getMinutes())}`;
return `${datePart} ${timePart}`;
};
activities.forEach((activity) => {
const row = document.createElement('tr');
const toDisplayString = (value) => {
if (value === null || value === undefined || value === '') {
return '—';
}
return String(value);
};
const formattedTime = formatDateTime(activity && activity.login_time);
const timeCell = document.createElement('td');
timeCell.textContent = formattedTime;
const activityCell = document.createElement('td');
activityCell.textContent = 'Logged in';
const userCell = document.createElement('td');
userCell.textContent = toDisplayString(activity && activity.email);
row.appendChild(timeCell);
row.appendChild(activityCell);
row.appendChild(userCell);
activitiesBody.appendChild(row);
});
})
.catch((error) => {
if (activitiesBody) {
activitiesBody.innerHTML = '';
const row = document.createElement('tr');
const cell = document.createElement('td');
cell.colSpan = 3;
cell.classList.add('text-danger');
cell.textContent = 'Failed to load recent activities.';
row.appendChild(cell);
activitiesBody.appendChild(row);
}
showError(error.message || 'Unable to load dashboard metrics.');
});
});
</script>
<?= $this->endSection() ?>
+37
View File
@@ -0,0 +1,37 @@
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Attendance</h1>
<a href="/administrator/attendance/create" class="btn btn-primary">Add New Attendance Record</a>
</div>
<table class="table table-striped mt-3">
<thead>
<tr>
<th>ID</th>
<th>Student Name</th>
<th>Date</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (!empty($attendances)): ?>
<?php foreach ($attendances as $attendance): ?>
<tr>
<td><?php echo $attendance['id']; ?></td>
<td><?php echo $attendance['student_name']; ?></td>
<td><?= !empty($attendance['date']) ? esc(local_date($attendance['date'], 'm-d-Y')) : '' ?></td>
<td><?php echo $attendance['status']; ?></td>
<td>
<a href="/administrator/attendance/edit/<?php echo $attendance['id']; ?>" class="btn btn-warning">Edit</a>
<a href="/administrator/attendance/delete/<?php echo $attendance['id']; ?>" class="btn btn-danger" onclick="return confirm('Are you sure you want to delete this attendance record?');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5">No attendance records found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</main>
@@ -0,0 +1,45 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Attendance Records</h1>
<?= $this->include('partials/academic_filter') ?>
<table class="table table-striped">
<thead>
<tr>
<th>Student ID</th>
<th>Name</th>
<th>Date</th>
<th>Status</th>
<th>Remarks</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$attendanceRecords = [
['student_id' => 'S001', 'name' => 'John Doe', 'date' => '2024-07-01', 'status' => 'Present', 'remarks' => ''],
['student_id' => 'S002', 'name' => 'Jane Smith', 'date' => '2024-07-01', 'status' => 'Absent', 'remarks' => 'Sick'],
['student_id' => 'S003', 'name' => 'Michael Johnson', 'date' => '2024-07-01', 'status' => 'Present', 'remarks' => ''],
// Add more records as needed
];
foreach ($attendanceRecords as $record) {
$dateDisp = !empty($record['date']) ? local_date($record['date'], 'm-d-Y') : '';
echo "<tr>
<td>{$record['student_id']}</td>
<td>{$record['name']}</td>
<td>" . esc($dateDisp) . "</td>
<td>{$record['status']}</td>
<td>{$record['remarks']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,45 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Behavior Reports</h1>
<?= $this->include('partials/academic_filter') ?>
<table class="table table-striped">
<thead>
<tr>
<th>Report ID</th>
<th>Student ID</th>
<th>Name</th>
<th>Incident</th>
<th>Date</th>
<th>Action Taken</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$behaviorReports = [
['report_id' => 'B001', 'student_id' => 'S001', 'name' => 'John Doe', 'incident' => 'Disruptive in class', 'date' => '2024-07-01', 'action' => 'Parent meeting'],
['report_id' => 'B002', 'student_id' => 'S002', 'name' => 'Jane Smith', 'incident' => 'Fighting', 'date' => '2024-07-02', 'action' => 'Detention'],
['report_id' => 'B003', 'student_id' => 'S003', 'name' => 'Michael Johnson', 'incident' => 'Bullying', 'date' => '2024-07-03', 'action' => 'Suspension'],
// Add more records as needed
];
foreach ($behaviorReports as $report) {
$dateDisp = !empty($report['date']) ? local_date($report['date'], 'm-d-Y') : '';
echo "<tr>
<td>{$report['report_id']}</td>
<td>{$report['student_id']}</td>
<td>{$report['name']}</td>
<td>{$report['incident']}</td>
<td>" . esc($dateDisp) . "</td>
<td>{$report['action']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
<?= $this->endSection() ?>
+255
View File
@@ -0,0 +1,255 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container mt-4">
<h2 class="text-center mb-3">Broadcast Email to Parents</h2>
<?php if ($msg = session()->getFlashdata('message')): ?>
<div class="alert alert-success"><?= esc($msg) ?></div>
<?php endif; ?>
<?php if ($err = session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= esc($err) ?></div>
<?php endif; ?>
<form method="post" action="<?= site_url('admin/broadcast-email/send') ?>" id="broadcastForm">
<?= csrf_field() ?>
<div class="card mb-3">
<div class="card-header">Email Details</div>
<div class="card-body row g-3">
<div class="col-md-6">
<label class="form-label">From (configured sender)</label>
<select class="form-select" name="from_key" required>
<?php foreach (($fromOptions ?? []) as $opt): ?>
<option value="<?= esc($opt['key']) ?>"><?= esc($opt['label']) ?></option>
<?php endforeach; ?>
</select>
<small class="text-muted">Senders come from MAIL_SENDERS (.env).</small>
</div>
<div class="col-md-6">
<label class="form-label">Send Mode</label>
<select name="mode" class="form-select" id="modeSelect">
<option value="personalized">Personalized (replaces {{name}})</option>
<option value="standard">Standard (no personalization)</option>
</select>
</div>
<div class="col-md-8">
<label class="form-label">Subject</label>
<input type="text" name="subject" class="form-control" placeholder="Subject..." required>
</div>
<div class="col-md-4">
<div class="form-check mt-4">
<input class="form-check-input" type="checkbox" value="1" id="wrap_layout" name="wrap_layout" checked>
<label class="form-check-label" for="wrap_layout">
Wrap with email layout
</label>
</div>
</div>
<div class="col-12">
<label class="form-label">Message (HTML). Use {{name}} in Personalized mode.</label>
<textarea id="body_html" name="body_html" class="form-control" rows="10" placeholder="Dear {{name}}, ..."></textarea>
</div>
<!-- Optional fields your layout can use -->
<div class="col-md-6">
<label class="form-label">Preheader (optional)</label>
<input type="text" name="preheader" class="form-control" placeholder="Short preview text shown by mail clients">
</div>
<div class="col-md-3">
<label class="form-label">CTA Text (optional)</label>
<input type="text" name="cta_text" class="form-control" placeholder="e.g., View Details">
</div>
<div class="col-md-3">
<label class="form-label">CTA URL (optional)</label>
<input type="url" name="cta_url" class="form-control" placeholder="https://...">
</div>
</div>
<div class="card-footer d-flex gap-2 align-items-center flex-wrap">
<div class="input-group" style="max-width: 420px;">
<span class="input-group-text">Test email</span>
<input type="email" name="test_email" class="form-control" placeholder="you@example.com">
<button class="btn btn-outline-primary" name="send_test_only" value="1" type="submit">Send Test Only</button>
</div>
<span class="ms-auto fw-semibold">Selected: <span id="selectedCount">0</span></span>
<button type="submit" class="btn btn-primary">Send Broadcast</button>
</div>
</div>
<div class="card">
<div class="card-header d-flex justify-content-between">
<span>Parents</span>
<div class="d-flex gap-2">
<button type="button" class="btn btn-sm btn-outline-secondary" id="selectAll">Select All</button>
<button type="button" class="btn btn-sm btn-outline-secondary" id="clearAll">Clear</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table id="parentsTable" class="table table-striped table-hover align-middle w-100">
<thead class="table-dark">
<tr>
<th style="width:40px;">#</th>
<th>Name</th>
<th>Email</th>
<th>School ID</th>
</tr>
</thead>
<tbody>
<?php foreach (($parents ?? []) as $p): ?>
<tr>
<td><input type="checkbox" class="row-check" value="<?= (int)$p['id'] ?>"></td>
<td><?= esc(($p['firstname'] ?? '') . ' ' . ($p['lastname'] ?? '')) ?></td>
<td><?= esc($p['email'] ?? '') ?></td>
<td><?= esc($p['school_id'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<input type="hidden" id="selectedIdsCarrier" name="parent_ids[]" value="">
</div>
</div>
</form>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.8/css/dataTables.bootstrap5.min.css">
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.datatables.net/1.13.8/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.8/js/dataTables.bootstrap5.min.js"></script>
<!-- Summernote Lite (no Bootstrap dep) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/summernote@0.8.20/dist/summernote-lite.min.css">
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.20/dist/summernote-lite.min.js"></script>
<script>
(function() {
const editor = $('#body_html');
const csrfName = '<?= csrf_token() ?>'; // e.g. "csrf_test_name"
let csrfHash = '<?= csrf_hash() ?>';
const CSRF_COOKIE_NAME = <?= json_encode(config('Security')->csrfCookieName ?? (config('Security')->cookieName ?? 'csrf_cookie_name')) ?>;
function getCookie(n){
return document.cookie.split(';').map(v=>v.trim()).find(v=>v.startsWith(n+'='))?.split('=')[1] || '';
}
function uploadImageFile(file) {
if (!file || !file.type || !file.type.startsWith('image/')) return;
if (file.size > 5 * 1024 * 1024) { alert('Image too large (max 5MB).'); return; }
const fd = new FormData();
fd.append('image', file);
fd.append(csrfName, csrfHash); // 👈 CSRF in body
fetch('<?= site_url('admin/broadcast-email/upload-image') ?>', {
method: 'POST',
body: fd,
credentials: 'same-origin', // 👈 send cookies
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': csrfHash, // 👈 CSRF in header (CodeIgniter accepts this too)
}
})
.then(async (r) => {
// Grab fresh token if CI rotated it
const newHash = r.headers.get('X-CSRF-HASH');
if (newHash) csrfHash = newHash; else {
// Fallback to cookie (CI rotates token after POST)
try {
const c = decodeURIComponent(getCookie(CSRF_COOKIE_NAME) || '');
if (c) csrfHash = c;
} catch(_) {}
}
// If server returned HTML error (CSRF page), show it
const text = await r.text();
try { return JSON.parse(text); } catch { throw new Error(text || 'Upload failed'); }
})
.then(json => {
if (!json.success) throw new Error(json.error || 'Upload failed');
if (json.csrf_hash) csrfHash = json.csrf_hash; // another place to refresh
editor.summernote('insertImage', json.url, file.name || 'image');
})
.catch(err => {
console.error(err);
alert('Image upload failed: ' + err.message);
});
}
editor.summernote({
height: 260,
toolbar: [
['style', ['bold','italic','underline','clear']],
['para', ['ul','ol','paragraph']],
['insert',['link','table','hr']],
['font', ['superscript','subscript']],
['view', ['undo','redo','codeview']]
],
callbacks: {
onImageUpload: function(files) { for (let f of files) uploadImageFile(f); },
onPaste: function(e) {
const cd = (e.originalEvent || e).clipboardData;
if (!cd || !cd.items) return;
for (let it of cd.items) {
if (it.kind === 'file' && it.type.startsWith('image/')) {
e.preventDefault();
uploadImageFile(it.getAsFile());
}
}
}
}
});
})();
</script>
<script>
(function() {
const selected = new Set();
const table = $('#parentsTable').DataTable({
pageLength: 100,
lengthMenu: [10, 25, 50, 100, 250],
order: [[1, 'asc']]
});
function refreshSelected() {
$('#selectedCount').text(selected.size);
document.getElementById('selectedIdsCarrier').value = Array.from(selected).join(',');
}
$('#parentsTable').on('draw.dt', function() {
document.querySelectorAll('#parentsTable .row-check').forEach(chk => {
const id = parseInt(chk.value, 10);
chk.checked = selected.has(id);
});
});
$('#parentsTable').on('change', '.row-check', function() {
const id = parseInt(this.value, 10);
if (this.checked) selected.add(id); else selected.delete(id);
refreshSelected();
});
document.getElementById('selectAll').addEventListener('click', function() {
table.rows({page:'current'}).nodes().to$().find('.row-check').each(function() {
this.checked = true;
selected.add(parseInt(this.value, 10));
});
refreshSelected();
});
document.getElementById('clearAll').addEventListener('click', function() {
table.rows({page:'current'}).nodes().to$().find('.row-check').each(function() {
this.checked = false;
selected.delete(parseInt(this.value, 10));
});
refreshSelected();
});
})();
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,44 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Budget Reports</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Report ID</th>
<th>Period</th>
<th>Total Income</th>
<th>Total Expenses</th>
<th>Net Balance</th>
<th>Date Generated</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$budgetReports = [
['report_id' => 'BR001', 'period' => 'Q1 2024', 'total_income' => '$5000', 'total_expenses' => '$3000', 'net_balance' => '$2000', 'date_generated' => '2024-07-01'],
['report_id' => 'BR002', 'period' => 'Q2 2024', 'total_income' => '$7000', 'total_expenses' => '$4000', 'net_balance' => '$3000', 'date_generated' => '2024-07-02'],
['report_id' => 'BR003', 'period' => 'Q3 2024', 'total_income' => '$8000', 'total_expenses' => '$5000', 'net_balance' => '$3000', 'date_generated' => '2024-07-03'],
// Add more records as needed
];
foreach ($budgetReports as $report) {
$dateDisp = !empty($report['date_generated']) ? local_date($report['date_generated'], 'm-d-Y') : '';
echo "<tr>
<td>{$report['report_id']}</td>
<td>{$report['period']}</td>
<td>{$report['total_income']}</td>
<td>{$report['total_expenses']}</td>
<td>{$report['net_balance']}</td>
<td>" . esc($dateDisp) . "</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
<?= $this->endSection() ?>
+69
View File
@@ -0,0 +1,69 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-xxl py-5">
<div class="container">
<div class="main-content">
<div id="calendar-container">
<div id="calendar"></div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
const calendarEl = document.getElementById('calendar');
const isMobile = window.innerWidth <= 576;
const calendar = new FullCalendar.Calendar(calendarEl, {
initialView: isMobile ? 'listWeek' : 'dayGridMonth',
height: 'auto',
contentHeight: 'auto',
expandRows: true,
events: <?= json_encode($events) ?>,
eventDidMount: function(info) {
if (info.event.extendedProps.description && window.bootstrap && bootstrap.Tooltip) {
try {
new bootstrap.Tooltip(info.el, {
title: info.event.extendedProps.description,
placement: 'top',
trigger: 'hover',
container: 'body'
});
} catch (e) { /* no-op */ }
}
},
eventContent: function(arg) {
const event = arg.event;
const bg = event.extendedProps.backgroundColor || '#3788d8';
return {
html: `
<div style="
background-color: ${bg};
padding: 4px;
border-radius: 4px;
color: #fff;
font-size: 0.8rem;
text-align: center;">
${event.title}
</div>`
};
},
windowResize: function(view) {
if (window.innerWidth <= 576 && calendar.view.type !== 'listWeek') {
calendar.changeView('listWeek');
} else if (window.innerWidth > 576 && calendar.view.type !== 'dayGridMonth') {
calendar.changeView('dayGridMonth');
}
calendar.updateSize();
}
});
calendar.render();
window.calendar = calendar;
});
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,127 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h2 class="text-center mt-4 mb-3">Add Event</h2>
<?php if (isset($validation)): ?>
<div class="alert alert-danger">
<?= $validation->listErrors(); ?>
</div>
<?php endif; ?>
<?php
$request = service('request');
$prefilledSchoolYear = set_value('school_year');
if ($prefilledSchoolYear === '') {
$prefilledSchoolYear = $request->getGet('school_year') ?: (function_exists('getSchoolYear') ? (string) getSchoolYear() : '');
}
$prefilledSemester = set_value('semester');
if ($prefilledSemester === '') {
$prefilledSemester = $request->getGet('semester') ?: (function_exists('getSemester') ? (string) getSemester() : '');
}
?>
<form action="/administrator/calendar_add_event" method="post">
<?= csrf_field() ?>
<div class="form-group mb-3">
<div class="form-check form-switch mb-2">
<input class="form-check-input" type="checkbox" id="no_school" name="no_school" value="1"
<?= set_checkbox('no_school', '1') ?>>
<label class="form-check-label" for="no_school">No School Day</label>
</div>
<label for="title">Title<span class="text-danger">*</span></label>
<input type="text" id="title" name="title" class="form-control"
value="<?= set_value('title'); ?>" required>
</div>
<div class="form-group mb-3">
<label for="description">Description:</label>
<textarea id="description" name="description" class="form-control"><?= set_value('description'); ?></textarea>
</div>
<div class="form-group mb-3">
<label for="event_type">Event Type:</label>
<select id="event_type" name="event_type" class="form-select"
<?= empty($eventTypes) ? 'disabled' : '' ?>>
<option value="">— Select event type —</option>
<?php foreach ($eventTypes ?? [] as $type): ?>
<option value="<?= esc($type) ?>" <?= set_select('event_type', $type) ?>>
<?= esc($type) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group mb-3">
<label for="start">Event Date <span class="text-danger">*</span></label>
<input type="date" id="start" name="start" class="form-control"
value="<?= set_value('start'); ?>" required>
</div>
<div class="form-group mt-3">
<label>Calendar Display:</label><br>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="notify_parent" name="notify_parent" value="1"
<?= set_checkbox('notify_parent', '1') ?>>
<label class="form-check-label" for="notify_parent">Parents</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="notify_teacher" name="notify_teacher" value="1"
<?= set_checkbox('notify_teacher', '1') ?>>
<label class="form-check-label" for="notify_teacher">Teachers</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="notify_admin" name="notify_admin" value="1"
<?= set_checkbox('notify_admin', '1') ?>>
<label class="form-check-label" for="notify_admin">Admins</label>
</div>
</div>
<div class="form-group mt-3">
<label>Email notifications (optional):</label><br>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="send_email_parent" name="send_email_parent" value="1"
<?= set_checkbox('send_email_parent', '1') ?>>
<label class="form-check-label" for="send_email_parent">Send to Parents</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="send_email_teacher" name="send_email_teacher" value="1"
<?= set_checkbox('send_email_teacher', '1') ?>>
<label class="form-check-label" for="send_email_teacher">Send to Teachers</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="send_email_admin" name="send_email_admin" value="1"
<?= set_checkbox('send_email_admin', '1') ?>>
<label class="form-check-label" for="send_email_admin">Send to Admins</label>
</div>
<small class="form-text text-muted">Emails are dispatched immediately upon saving the event.</small>
</div>
<div class="row g-3 mt-2">
<div class="col-md-6">
<label for="school_year" class="form-label">School Year</label>
<input type="text" id="school_year" name="school_year" class="form-control" value="<?= esc($prefilledSchoolYear) ?>" placeholder="e.g. 2025-2026">
</div>
<div class="col-md-6">
<label for="semester" class="form-label">Semester</label>
<select id="semester" name="semester" class="form-select">
<option value="">—</option>
<option value="Fall" <?= (strcasecmp($prefilledSemester, 'Fall') === 0 ? 'selected' : '') ?>>Fall</option>
<option value="Spring" <?= (strcasecmp($prefilledSemester, 'Spring') === 0 ? 'selected' : '') ?>>Spring</option>
</select>
</div>
</div>
<br>
<div class="d-flex gap-2 mb-3 justify-content-end">
<button type="submit" class="btn btn-primary">Add Event</button>
<a href="/administrator/calendar_view" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
</div>
</div>
<?= $this->endSection() ?>
+113
View File
@@ -0,0 +1,113 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h2 class="text-center mt-4 mb-3">Edit Event</h2>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger">
<?= session()->getFlashdata('error'); ?>
</div>
<?php endif; ?>
<form action="/administrator/update/<?= $event['id']; ?>" method="post">
<?= csrf_field(); ?>
<div class="form-group mb-3">
<label>Calendar Display:</label><br>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="notify_parent" value="1"
<?= $event['notify_parent'] ? 'checked' : '' ?>>
<label class="form-check-label">Parents</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="notify_teacher" value="1"
<?= $event['notify_teacher'] ? 'checked' : '' ?>>
<label class="form-check-label">Teachers</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="notify_admin" value="1"
<?= $event['notify_admin'] ? 'checked' : '' ?>>
<label class="form-check-label">Admins</label>
</div>
</div>
<div class="form-group mb-3">
<label>Email notifications (optional):</label><br>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="send_email_parent" name="send_email_parent" value="1"
<?= set_checkbox('send_email_parent', '1') ?>>
<label class="form-check-label" for="send_email_parent">Send to Parents</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="send_email_teacher" name="send_email_teacher" value="1"
<?= set_checkbox('send_email_teacher', '1') ?>>
<label class="form-check-label" for="send_email_teacher">Send to Teachers</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="send_email_admin" name="send_email_admin" value="1"
<?= set_checkbox('send_email_admin', '1') ?>>
<label class="form-check-label" for="send_email_admin">Send to Admins</label>
</div>
<small class="form-text text-muted">Emails are dispatched immediately upon saving the event.</small>
</div>
<div class="form-group mb-3">
<div class="form-check form-switch mb-2">
<input class="form-check-input" type="checkbox" id="no_school" name="no_school" value="1"
<?= !empty($event['no_school']) ? 'checked' : '' ?>>
<label class="form-check-label" for="no_school">No School Day</label>
</div>
<label for="title">Title:</label>
<input type="text" id="title" name="title" class="form-control"
value="<?= esc($event['title']); ?>" required>
</div>
<div class="form-group mb-3">
<label for="description">Description:</label>
<textarea id="description" name="description" class="form-control"><?= esc($event['description']); ?></textarea>
</div>
<div class="form-group mb-3">
<label for="event_type">Event Type:</label>
<?php $currentEventType = (string)($event['event_type'] ?? ''); ?>
<select id="event_type" name="event_type" class="form-select">
<option value="">— Select event type —</option>
<?php foreach ($eventTypes ?? [] as $type): ?>
<option value="<?= esc($type) ?>" <?= $currentEventType === $type ? 'selected' : '' ?>>
<?= esc($type) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group mb-3">
<label for="start">Event Date:</label>
<input type="date" id="start" name="start" class="form-control"
value="<?= esc($event['date']); ?>" required>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<label for="school_year" class="form-label">School Year</label>
<input type="text" id="school_year" name="school_year" class="form-control" value="<?= esc($event['school_year'] ?? (function_exists('getSchoolYear')?getSchoolYear():'')) ?>" placeholder="e.g. 2025-2026">
</div>
<div class="col-md-6">
<label for="semester" class="form-label">Semester</label>
<?php $semVal = (string)($event['semester'] ?? (function_exists('getSemester')?getSemester():'')); ?>
<select id="semester" name="semester" class="form-select">
<option value="">—</option>
<option value="Fall" <?= (strcasecmp($semVal,'Fall')===0?'selected':'') ?>>Fall</option>
<option value="Spring" <?= (strcasecmp($semVal,'Spring')===0?'selected':'') ?>>Spring</option>
</select>
</div>
</div>
<div class="d-flex gap-2 mb-3 justify-content-end">
<button type="submit" class="btn btn-success">Update Event</button>
<a href="/administrator/calendar_view" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
</div>
<?= $this->endSection() ?>
+146
View File
@@ -0,0 +1,146 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content">
<br>
<h2 class="text-center mt-4 mb-3">School Calendar</h2>
<?php
$addParams = array_filter([
'school_year' => $schoolYear ?? '',
'semester' => $semester ?? '',
], fn($value) => $value !== '');
$addQuery = $addParams ? ('?' . http_build_query($addParams)) : '';
?>
<div class="d-flex gap-2 mb-3 justify-content-end">
<a href="/administrator/calendar_add_event<?= $addQuery ?>" class="btn btn-success">Add New Event</a>
</div>
<br>
<!-- School Year / Semester Filter Form -->
<form method="get" action="<?= base_url('administrator/calendar_view') ?>" class="mb-3 text-center d-flex gap-2 justify-content-center align-items-end flex-wrap">
<div>
<label for="school_year" class="form-label">School Year:</label>
<select name="school_year" id="school_year" class="form-select w-auto d-inline-block ms-2">
<?php foreach (range(date('Y') + 1, 2023) as $y):
$sy = ($y - 1) . '-' . $y; ?>
<option value="<?= $sy ?>" <?= $sy === ($schoolYear ?? '') ? 'selected' : '' ?>>
<?= $sy ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<label for="semester" class="form-label">Semester:</label>
<?php $semVal = (string)($semester ?? ($_GET['semester'] ?? '')); ?>
<select name="semester" id="semester" class="form-select w-auto d-inline-block ms-2">
<option value="">—</option>
<option value="Fall" <?= (strcasecmp($semVal,'Fall')===0?'selected':'') ?>>Fall</option>
<option value="Spring" <?= (strcasecmp($semVal,'Spring')===0?'selected':'') ?>>Spring</option>
</select>
</div>
<div>
<button type="submit" class="btn btn-secondary">Apply</button>
<a href="<?= base_url('administrator/calendar_view') ?>" class="btn btn-outline-secondary">Reset</a>
</div>
</form>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success">
<?= session()->getFlashdata('success') ?>
</div>
<?php endif; ?>
<?php
$emailStatus = session()->getFlashdata('email_status');
$emailStatusClass = session()->getFlashdata('email_status_class') ?? 'info';
?>
<?php if ($emailStatus): ?>
<div class="alert alert-<?= esc($emailStatusClass) ?>">
<?= esc($emailStatus) ?>
</div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger">
<?= session()->getFlashdata('error') ?>
</div>
<?php endif; ?>
<div class="table-responsive mb-3">
<table id="myTable" class="display">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Type</th>
<th>Description</th>
<th>Date</th>
<th>Notify Parent</th>
<th>Notify Teacher</th>
<th>Notify Admin</th>
<th>No School</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (!empty($events)): ?>
<?php if ($emailStatus): ?>
<tr class="table-warning">
<td colspan="10" class="text-center small">
<?= esc($emailStatus) ?>
</td>
</tr>
<?php endif; ?>
<?php foreach ($events as $event): ?>
<tr>
<td><?= esc($event['id']) ?></td>
<td><?= esc($event['title']) ?></td>
<td><?= esc($event['event_type'] ?? '—') ?></td>
<td><?= esc($event['description']) ?></td>
<td><?= esc(!empty($event['date']) ? date('m-d-Y', strtotime($event['date'])) : '') ?></td>
<td>
<input type="checkbox" disabled <?= $event['notify_parent'] ? 'checked' : '' ?>>
</td>
<td>
<input type="checkbox" disabled <?= $event['notify_teacher'] ? 'checked' : '' ?>>
</td>
<td>
<input type="checkbox" disabled <?= $event['notify_admin'] ? 'checked' : '' ?>>
</td>
<td>
<input type="checkbox" disabled <?= !empty($event['no_school']) ? 'checked' : '' ?>>
</td>
<td>
<a href="/administrator/calendar_edit/<?= $event['id'] ?>" class="btn btn-primary btn-sm">Edit</a>
<form action="/administrator/calendar_delete/<?= $event['id'] ?>" method="post" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-danger btn-sm"
onclick="return confirm('Are you sure you want to delete this event?')">Delete</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="10" class="text-center">No events found</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
$(document).ready(function() {
$('#myTable').DataTable();
});
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,159 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h2 class="text-center mt-4 mb-3">Classes Lists</h2>
<form method="get" class="row g-2 align-items-center justify-content-center mb-3">
<div class="col-auto">
<label class="form-label mb-0">School year</label>
</div>
<div class="col-auto">
<select name="school_year" class="form-select form-select-sm" style="min-width: 200px;">
<?php if (!empty($schoolYears)): ?>
<?php foreach ($schoolYears as $yearOption): ?>
<option value="<?= esc($yearOption) ?>" <?= ($selectedYear === $yearOption ? 'selected' : '') ?>>
<?= esc($yearOption) ?>
</option>
<?php endforeach; ?>
<?php else: ?>
<option value="<?= esc($schoolYear ?? '') ?>" <?= ($selectedYear === ($schoolYear ?? '') ? 'selected' : '') ?>>
<?= esc($schoolYear ?? 'Current') ?>
</option>
<?php endif; ?>
</select>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-secondary btn-sm">Apply</button>
<a class="btn btn-outline-secondary btn-sm" href="<?= base_url('administrator/class_assignment') ?>">Reset</a>
</div>
</form>
<?php if (session()->getFlashdata('message')): ?>
<div class="alert alert-success">
<?= session()->getFlashdata('message') ?>
</div>
<?php endif; ?>
<?php if (!empty($classSections)): ?>
<div id="classAccordion">
<?php foreach ($classSections as $index => $classSection): ?>
<div class="card">
<div class="card-header" id="heading<?= $index ?>">
<h2 class="mb-0">
<button class="btn btn-link w-100 text-start" type="button"
data-bs-toggle="collapse"
data-bs-target="#collapse<?= $index ?>"
aria-expanded="<?= $index === 0 ? 'true' : 'false' ?>"
aria-controls="collapse<?= $index ?>">
Grade : <?= esc($classSection['class_section_name']) ?>
</button>
</h2>
</div>
<div id="collapse<?= $index ?>" class="collapse" aria-labelledby="heading<?= $index ?>" data-bs-parent="#classAccordion">
<div class="card-body">
<h5>Main Teacher:
<?= !empty($classSection['main_teachers'])
? esc(implode(', ', $classSection['main_teachers']))
: 'None' ?>
</h5>
<h5>Teacher-Assistant:
<?= !empty($classSection['teacher_assistants'])
? esc(implode(', ', $classSection['teacher_assistants']))
: 'None' ?>
</h5>
<?php $displaySemester = $selectedSemester ?? ''; ?>
<?php if ($displaySemester === '' && isset($classSection['semester'])): ?>
<?php $displaySemester = $classSection['semester']; ?>
<?php endif; ?>
<h5>Semester: <?= esc($displaySemester ?: 'N/A') ?></h5>
<h5>School Year: <?= esc($classSection['school_year']) ?></h5>
<p><?= esc($classSection['description']) ?></p>
<?php if (!empty($classSection['students'])): ?>
<table id="myTable<?= $index ?>" class="display">
<thead>
<tr>
<th>School ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Age</th>
<th>Gender</th>
<th>Photo Consent</th>
<th>Tuition Paid</th>
</tr>
</thead>
<tbody>
<?php foreach ($classSection['students'] as $student): ?>
<tr>
<td><?= esc($student['school_id']) ?></td>
<td>
<?php $sid = (int)($student['id'] ?? 0); ?>
<?php if ($sid): ?>
<a href="#" class="text-decoration-none" data-family-student-id="<?= $sid ?>">
<?= esc($student['firstname']) ?>
</a>
<?php else: ?>
<?= esc($student['firstname']) ?>
<?php endif; ?>
</td>
<td>
<?php if (!empty($sid)): ?>
<a href="#" class="text-decoration-none" data-family-student-id="<?= $sid ?>">
<?= esc($student['lastname']) ?>
</a>
<?php else: ?>
<?= esc($student['lastname']) ?>
<?php endif; ?>
</td>
<td><?= esc($student['age']) ?></td>
<td><?= esc($student['gender']) ?></td>
<td><?= esc($student['photo_consent']) ?></td>
<td><?= esc($student['tuition_paid']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<p>No students registered in this class section.</p>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<p>No class sections available.</p>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
$(document).ready(function() {
<?php foreach ($classSections as $index => $section): ?>
$('#myTable<?= $index ?>').DataTable({
"paging": true, // enable pagination
"lengthChange": true, // allow changing number of rows per page
"pageLength": 50, // default rows per page
"info": true, // show "Showing X to Y of Z entries"
"language": {
"info": "Showing _START_ to _END_ of _TOTAL_ entries (Page _PAGE_ of _PAGES_)",
"infoEmpty": "No entries to show",
"infoFiltered": "(filtered from _MAX_ total entries)",
"lengthMenu": "Show _MENU_ entries per page"
}
});
<?php endforeach; ?>
});
</script>
<?= $this->endSection() ?>
+38
View File
@@ -0,0 +1,38 @@
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Classes</h1>
<a href="/administrator/classsection/create" class="btn btn-primary">Add New Class</a>
</div>
<table class="table table-striped mt-3">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Description</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (!empty($classsection)): ?>
<?php foreach ($classsection as $classsec): ?>
<tr>
<td><?php echo $classsec['id']; ?></td>
<td><?php echo $classsec['name']; ?></td>
<td><?php echo $classsec['description']; ?></td>
<td>
<a href="/administrator/classsection/edit/<?php echo $classsec['id']; ?>"
class="btn btn-warning">Edit</a>
<a href="/administrator/classsection/delete/<?php echo $classsec['id']; ?>" class="btn btn-danger"
onclick="return confirm('Are you sure you want to delete this class?');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="4">No classsection found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</main>
@@ -0,0 +1,40 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="modal fade" id="classWarningModal" tabindex="-1" aria-labelledby="classWarningLabel" aria-hidden="true" role="alertdialog">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-warning">
<div class="modal-header bg-warning text-dark">
<h5 class="modal-title" id="classWarningLabel">
<i class="bi bi-exclamation-triangle-fill me-2"></i> Warning
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-dark fs-6">
<?= esc($warningMessage ?? 'An unexpected issue occurred.') ?>
</div>
<div class="modal-footer justify-content-between">
<a href="<?= base_url('/administrator/student_class_assignment') ?>" class="btn btn-secondary">
<i class="bi bi-arrow-left-circle me-1"></i> Back to Class Assignment
</a>
<!-- Optional dynamic action (add if needed) -->
<!-- <a href="<?= base_url('/some-action') ?>" class="btn btn-warning">Proceed</a> -->
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function () {
const classWarningModalEl = document.getElementById('classWarningModal');
if (classWarningModalEl) {
const modal = new bootstrap.Modal(classWarningModalEl);
modal.show();
}
});
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,44 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Communication Logs</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Date</th>
<th>Sender</th>
<th>Recipient</th>
<th>Message</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$communicationLogs = [
['date' => '2024-07-01', 'sender' => 'Teacher A', 'recipient' => 'Parent B', 'message' => 'Discussed student performance.', 'status' => 'Sent'],
['date' => '2024-07-02', 'sender' => 'Teacher C', 'recipient' => 'Parent D', 'message' => 'Meeting request.', 'status' => 'Received'],
['date' => '2024-07-03', 'sender' => 'administrator', 'recipient' => 'Teacher A', 'message' => 'Policy update.', 'status' => 'Sent'],
// Add more logs as needed
];
foreach ($communicationLogs as $log) {
$dateDisp = !empty($log['date']) ? local_date($log['date'], 'm-d-Y') : '';
echo "<tr>
<td>" . esc($dateDisp) . "</td>
<td>{$log['sender']}</td>
<td>{$log['recipient']}</td>
<td>{$log['message']}</td>
<td>{$log['status']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,56 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Course Materials</h1>
<div class="mb-3">
<form action="/administrator/upload_material" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="materialTitle">Material Title</label>
<input type="text" class="form-control" id="materialTitle" name="title" required>
</div>
<div class="form-group">
<label for="materialFile">Upload File</label>
<input type="file" class="form-control" id="materialFile" name="file" required>
</div>
<button type="submit" class="btn btn-primary">Upload</button>
</form>
</div>
<h2>Uploaded Materials</h2>
<table class="table table-striped">
<thead>
<tr>
<th>Title</th>
<th>File</th>
<th>Uploaded At</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$courseMaterials = [
['title' => 'Lecture Notes 1', 'file' => 'lecture_notes_1.pdf', 'uploaded_at' => '2023-01-10'],
['title' => 'Assignment 1', 'file' => 'assignment_1.pdf', 'uploaded_at' => '2023-01-12'],
// Add more materials as needed
];
foreach ($courseMaterials as $material) {
echo "<tr>
<td>{$material['title']}</td>
<td><a href='/uploads/{$material['file']}' target='_blank'>{$material['file']}</a></td>
<td>{$material['uploaded_at']}</td>
<td>
<a href='/administrator/delete_material/{$material['file']}' class='btn btn-danger btn-sm'>Delete</a>
</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
+25
View File
@@ -0,0 +1,25 @@
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Add New Parent</h1>
</div>
<form action="/administrator/parent/store" method="post">
<div class="form-group">
<label for="firstname">First Name</label>
<input type="text" class="form-control" id="firstname" name="firstname" required>
</div>
<div class="form-group">
<label for="lastname">Last Name</label>
<input type="text" class="form-control" id="lastname" name="lastname" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</main>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,471 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<?= csrf_meta() ?>
<?php
// Exclude unwanted grade buckets globally (e.g., legacy Grade 10/11) unless the bucket is the Arabic program.
$hiddenGrades = [10, 11];
$hasArabic = static function (array $sections): bool {
foreach ($sections as $section) {
$name = strtolower((string)($section['class_section_name'] ?? ''));
$code = strtolower((string)($section['class_section_id'] ?? ''));
if (strpos($name, 'arabic') !== false || strpos($code, 'arabic') !== false) {
return true;
}
}
return false;
};
foreach ($hiddenGrades as $hid) {
if (isset($grades[$hid]) && $hasArabic($grades[$hid])) {
continue; // keep Arabic sections even if the class_id matches a hidden grade bucket
}
unset($grades[$hid]);
}
// Prune out sections with no students (extra safety so empty Arabic-2/3 never render)
$filteredGrades = [];
foreach ($grades as $cid => $sections) {
$kept = [];
foreach ($sections as $section) {
$secKey = (string)($section['class_section_id'] ?? ($section['id'] ?? ''));
if ($secKey !== '' && !empty($studentsBySection[$secKey])) {
$kept[] = $section;
}
}
if (!empty($kept)) {
$filteredGrades[$cid] = $kept;
}
}
$grades = $filteredGrades;
?>
<div class="container-fluid">
<div class="wrapper">
<div class="content mb-3"></div>
<h2 class="text-center mt-4 mb-3">Attendance Analysis</h2>
<?= $this->include('partials/academic_filter') ?>
<?= $this->section('styles') ?>
<style>
.attn-analysis-grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 1rem;
}
.attn-analysis-card {
background: #fff;
border: 1px solid rgba(0,0,0,0.08);
border-radius: .5rem;
padding: 1rem;
}
.attn-analysis-card h6 { margin-bottom: .75rem; }
.attn-analysis-wide { grid-column: span 12; }
.attn-analysis-half { grid-column: span 6; }
.attn-analysis-table {
width: 100%;
font-size: .9rem;
}
.attn-analysis-table th,
.attn-analysis-table td { padding: .35rem .5rem; }
.attn-analysis-scroll {
max-height: none;
overflow: visible;
}
@media (max-width: 1200px) {
.attn-analysis-half { grid-column: span 12; }
}
</style>
<?= $this->endSection() ?>
<?php
$req = request();
$startDateRaw = trim((string)($req->getGet('start_date') ?? ''));
$endDateRaw = trim((string)($req->getGet('end_date') ?? ''));
$isValidDate = static function (string $d): bool {
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $d)) return false;
[$y, $m, $day] = array_map('intval', explode('-', $d));
return checkdate($m, $day, $y);
};
$filterStart = ($startDateRaw !== '' && $isValidDate($startDateRaw)) ? $startDateRaw : '';
$filterEnd = ($endDateRaw !== '' && $isValidDate($endDateRaw)) ? $endDateRaw : '';
// ---- Build labels and a stable order: KG -> Grade 1..12 -> Youth ----
$labelsByClassId = [];
$gradeNumberById = [];
$kgIds = [];
$arabicIds = [];
$youthIds = [];
foreach ($grades as $classId => $sections) {
$cid = (int)$classId;
$sampleName = '';
$isArabicProgram = false;
foreach ($sections as $s) {
$sampleName = (string)($s['class_section_name'] ?? '');
$lowName = strtolower($sampleName);
$lowCode = strtolower((string)($s['class_section_id'] ?? ''));
if (strpos($lowName, 'arabic') !== false || strpos($lowCode, 'arabic') !== false) {
$isArabicProgram = true;
}
if ($sampleName !== '') break;
}
if ($isArabicProgram || $cid === 14) {
$labelsByClassId[$cid] = 'Arabic';
$arabicIds[] = $cid;
} elseif (preg_match('/\bKG\b/i', $sampleName)) {
$labelsByClassId[$cid] = 'KG';
$kgIds[] = $cid;
} elseif (preg_match('/\bYouth\b/i', $sampleName)) {
$labelsByClassId[$cid] = 'Youth';
$youthIds[] = $cid;
} elseif (preg_match('/Grade\s*(\d+)/i', $sampleName, $m)) {
$labelsByClassId[$cid] = 'Grade ' . (int)$m[1];
$gradeNumberById[$cid] = (int)$m[1];
} else {
if ($cid === 0) {
$labelsByClassId[$cid] = 'KG';
$kgIds[] = $cid;
} elseif ($cid === 13) {
$labelsByClassId[$cid] = 'Youth';
$youthIds[] = $cid;
} else {
$labelsByClassId[$cid] = 'Grade ' . $cid;
$gradeNumberById[$cid] = $cid;
}
}
}
$sortedClassIds = [];
if ($kgIds) {
sort($kgIds, SORT_NUMERIC);
$sortedClassIds = array_merge($sortedClassIds, $kgIds);
}
if ($gradeNumberById) {
asort($gradeNumberById, SORT_NUMERIC);
$sortedClassIds = array_merge($sortedClassIds, array_keys($gradeNumberById));
}
if ($youthIds) {
sort($youthIds, SORT_NUMERIC);
$sortedClassIds = array_merge($sortedClassIds, $youthIds);
}
if ($arabicIds) {
sort($arabicIds, SORT_NUMERIC);
$sortedClassIds = array_merge($sortedClassIds, $arabicIds);
}
$presentIds = array_map('intval', array_keys($grades));
$missingIds = array_values(array_diff($presentIds, $sortedClassIds));
if (!empty($missingIds)) {
$sortedClassIds = array_merge($sortedClassIds, $missingIds);
}
$labelFor = function (int $id) use ($labelsByClassId): string {
return $labelsByClassId[$id] ?? ('Class ' . $id);
};
// ---- Attendance analysis ----
$analysisOverall = ['present' => 0, 'late' => 0, 'absent' => 0];
$analysisByGrade = [];
$analysisBySection = [];
$analysisTotal = 0;
foreach ($grades as $classId => $sections) {
$gradeLabel = $labelFor((int)$classId);
if (!isset($analysisByGrade[$gradeLabel])) {
$analysisByGrade[$gradeLabel] = ['present' => 0, 'late' => 0, 'absent' => 0];
}
foreach ($sections as $section) {
$sectionKey = (string)($section['class_section_id'] ?? ($section['id'] ?? ''));
if ($sectionKey === '' || empty($studentsBySection[$sectionKey])) continue;
$secNameRaw = trim((string)($section['class_section_name'] ?? ''));
$secLabel = $secNameRaw !== '' ? $secNameRaw : ('Section ' . $sectionKey);
if (!isset($analysisBySection[$secLabel])) {
$analysisBySection[$secLabel] = ['present' => 0, 'late' => 0, 'absent' => 0, 'total_students' => 0];
}
$sectionTotalStudents = count($studentsBySection[$sectionKey]);
if ($sectionTotalStudents > ($analysisBySection[$secLabel]['total_students'] ?? 0)) {
$analysisBySection[$secLabel]['total_students'] = $sectionTotalStudents;
}
foreach ($studentsBySection[$sectionKey] as $stu) {
$sid = (int)($stu['id'] ?? 0);
if ($sid <= 0) continue;
$entries = $attendanceData[$sectionKey][$sid] ?? [];
if (!is_array($entries)) continue;
foreach ($entries as $e) {
$d = substr((string)($e['date'] ?? ''), 0, 10);
if ($d === '') continue;
if ($filterStart !== '' && $d < $filterStart) continue;
if ($filterEnd !== '' && $d > $filterEnd) continue;
$st = strtolower(trim((string)($e['status'] ?? '')));
if (!in_array($st, ['present', 'late', 'absent'], true)) continue;
$analysisOverall[$st]++;
$analysisByGrade[$gradeLabel][$st]++;
$analysisBySection[$secLabel][$st]++;
$analysisTotal++;
}
}
}
}
$overallPercent = [];
foreach (['present', 'late', 'absent'] as $st) {
$overallPercent[$st] = $analysisTotal > 0 ? round(($analysisOverall[$st] * 100) / $analysisTotal, 1) : 0;
}
$analysisGradeLabels = [];
$analysisGradeAbsent = [];
$analysisGradeLate = [];
$analysisGradeTotals = [];
foreach ($sortedClassIds as $cid) {
$lbl = $labelFor((int)$cid);
$g = $analysisByGrade[$lbl] ?? ['present' => 0, 'late' => 0, 'absent' => 0];
$analysisGradeLabels[] = $lbl;
$analysisGradeAbsent[] = (int)($g['absent'] ?? 0);
$analysisGradeLate[] = (int)($g['late'] ?? 0);
$analysisGradeTotals[] = (int)($g['present'] ?? 0) + (int)($g['late'] ?? 0) + (int)($g['absent'] ?? 0);
}
$analysisSectionLabels = array_keys($analysisBySection);
$analysisSectionAbsent = [];
$analysisSectionLate = [];
$analysisSectionTotals = [];
foreach ($analysisSectionLabels as $lbl) {
$s = $analysisBySection[$lbl] ?? ['present' => 0, 'late' => 0, 'absent' => 0];
$analysisSectionAbsent[] = (int)($s['absent'] ?? 0);
$analysisSectionLate[] = (int)($s['late'] ?? 0);
$analysisSectionTotals[] = (int)($s['total_students'] ?? 0);
}
$totalDaysForPercent = 0;
if ($filterStart === '' && $filterEnd === '' && !empty($totalPassedDays)) {
$totalDaysForPercent = (int)$totalPassedDays;
} elseif (!empty($dateList) && is_array($dateList)) {
foreach ($dateList as $d) {
$d = (string)$d;
if ($d === '') continue;
if ($filterStart !== '' && $d < $filterStart) continue;
if ($filterEnd !== '' && $d > $filterEnd) continue;
if (!empty($noSchoolDays[$d])) continue;
$totalDaysForPercent++;
}
}
$backUrl = site_url('administrator/daily_attendance');
$queryParts = [];
if (!empty($semester)) $queryParts[] = 'semester=' . urlencode((string)$semester);
if (!empty($schoolYear)) $queryParts[] = 'school_year=' . urlencode((string)$schoolYear);
if ($queryParts) $backUrl .= '?' . implode('&', $queryParts);
?>
<div class="row justify-content-center mb-3">
<div class="col-auto">
<a class="btn btn-outline-secondary" href="<?= esc($backUrl) ?>">
Back to Daily Attendance
</a>
</div>
</div>
<form class="row g-2 justify-content-center mb-4" method="get" action="<?= esc(current_url()) ?>">
<?php if (!empty($semester)): ?>
<input type="hidden" name="semester" value="<?= esc((string)$semester) ?>">
<?php endif; ?>
<?php if (!empty($schoolYear)): ?>
<input type="hidden" name="school_year" value="<?= esc((string)$schoolYear) ?>">
<?php endif; ?>
<div class="col-12 col-md-3">
<label class="form-label">Start date</label>
<input type="date" class="form-control" name="start_date" value="<?= esc($filterStart) ?>">
</div>
<div class="col-12 col-md-3">
<label class="form-label">End date</label>
<input type="date" class="form-control" name="end_date" value="<?= esc($filterEnd) ?>">
</div>
<div class="col-12 col-md-auto d-flex align-items-end gap-2">
<button type="submit" class="btn btn-primary">Apply</button>
<a class="btn btn-outline-secondary" href="<?= esc(current_url()) ?>">Clear</a>
</div>
</form>
<div class="attn-analysis-grid">
<div class="attn-analysis-card attn-analysis-half">
<h6>Overall Distribution</h6>
<canvas id="attnPieChart" height="50" aria-label="Overall attendance pie chart"></canvas>
<table class="attn-analysis-table mt-3 no-mgmt-sticky" data-no-mgmt-sticky>
<thead>
<tr>
<th title="Attendance status for the selected date range">Status</th>
<th class="text-end" title="Number of attendance entries with this status">Count</th>
<th class="text-end" title="Percent of total attendance entries">Percent</th>
</tr>
</thead>
<tbody>
<tr>
<td>Present</td>
<td class="text-end"><?= (int)$analysisOverall['present'] ?></td>
<td class="text-end"><?= number_format($overallPercent['present'], 1) ?>%</td>
</tr>
<tr>
<td>Late</td>
<td class="text-end"><?= (int)$analysisOverall['late'] ?></td>
<td class="text-end"><?= number_format($overallPercent['late'], 1) ?>%</td>
</tr>
<tr>
<td>Absent</td>
<td class="text-end"><?= (int)$analysisOverall['absent'] ?></td>
<td class="text-end"><?= number_format($overallPercent['absent'], 1) ?>%</td>
</tr>
</tbody>
</table>
</div>
<div class="attn-analysis-card attn-analysis-half">
<h6>By Section (Absent / Late)</h6>
<canvas id="attnSectionChart" height="200" aria-label="Attendance by section bar chart"></canvas>
<div class="attn-analysis-scroll mt-3">
<table id="sectionAnalysisTable" class="attn-analysis-table no-mgmt-sticky" data-no-mgmt-sticky>
<thead>
<tr>
<th title="Class section name">Section</th>
<th class="text-end" title="Total semester days in range (excluding no-school days)">Semester Days</th>
<th class="text-end" title="Total students in the class section">Total Students</th>
<th class="text-end" title="Number of absent entries in range">Absent</th>
<th class="text-end" title="Absent entries as a percent of total possible attendance entries (total students × total days in range)">Absent%</th>
<th class="text-end" title="Number of late entries in range">Late</th>
<th class="text-end" title="Late entries as a percent of total possible attendance entries (total students × total days in range)">Late%</th>
</tr>
</thead>
<tbody>
<?php foreach ($analysisSectionLabels as $i => $lbl): ?>
<?php
$tot = (int)($analysisSectionTotals[$i] ?? 0);
$abs = (int)($analysisSectionAbsent[$i] ?? 0);
$late = (int)($analysisSectionLate[$i] ?? 0);
$denom = $totalDaysForPercent > 0 ? ($totalDaysForPercent * $tot) : 0;
$absPct = $denom > 0 ? round(($abs * 100) / $denom, 1) : 0;
$latePct = $denom > 0 ? round(($late * 100) / $denom, 1) : 0;
?>
<tr>
<td><?= esc($lbl) ?></td>
<td class="text-end"><?= (int)$totalDaysForPercent ?></td>
<td class="text-end"><?= $tot ?></td>
<td class="text-end"><?= $abs ?></td>
<td class="text-end"><?= number_format($absPct, 1) ?>%</td>
<td class="text-end"><?= $late ?></td>
<td class="text-end"><?= number_format($latePct, 1) ?>%</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ATTN_ANALYSIS = <?= json_encode([
'overall' => [
'labels' => ['Present', 'Late', 'Absent'],
'counts' => [
(int)($analysisOverall['present'] ?? 0),
(int)($analysisOverall['late'] ?? 0),
(int)($analysisOverall['absent'] ?? 0),
],
],
'byGrade' => [
'labels' => $analysisGradeLabels,
'absent' => $analysisGradeAbsent,
'late' => $analysisGradeLate,
],
'bySection' => [
'labels' => $analysisSectionLabels,
'absent' => $analysisSectionAbsent,
'late' => $analysisSectionLate,
],
]) ?>;
function percentOfTotal(value, total) {
if (!total) return '0%';
return (Math.round((value * 1000) / total) / 10).toFixed(1) + '%';
}
function buildAttendanceCharts() {
if (!window.Chart) return;
const pieEl = document.getElementById('attnPieChart');
const sectionEl = document.getElementById('attnSectionChart');
if (!pieEl || !sectionEl) return;
const overall = ATTN_ANALYSIS.overall || { labels: [], counts: [] };
const total = (overall.counts || []).reduce((a, b) => a + (parseInt(b, 10) || 0), 0);
new Chart(pieEl.getContext('2d'), {
type: 'pie',
data: {
labels: overall.labels,
datasets: [{
data: overall.counts,
backgroundColor: ['#198754', '#f0ad4e', '#dc3545'],
borderWidth: 1,
}]
},
options: {
plugins: {
tooltip: {
callbacks: {
label: function(ctx) {
const val = parseInt(ctx.parsed, 10) || 0;
return ctx.label + ': ' + val + ' (' + percentOfTotal(val, total) + ')';
}
}
},
legend: { position: 'bottom' }
}
}
});
new Chart(sectionEl.getContext('2d'), {
type: 'bar',
data: {
labels: ATTN_ANALYSIS.bySection?.labels || [],
datasets: [
{
label: 'Absent',
data: ATTN_ANALYSIS.bySection?.absent || [],
backgroundColor: '#dc3545',
},
{
label: 'Late',
data: ATTN_ANALYSIS.bySection?.late || [],
backgroundColor: '#f0ad4e',
}
]
},
options: {
plugins: { legend: { position: 'bottom' } },
scales: {
y: { beginAtZero: true, ticks: { precision: 0 } }
}
}
});
}
document.addEventListener('DOMContentLoaded', buildAttendanceCharts);
document.addEventListener('DOMContentLoaded', function () {
if (window.jQuery && $.fn.DataTable) {
$('#sectionAnalysisTable').DataTable({
paging: false,
searching: false,
info: false,
order: [[0, 'asc']]
});
}
});
</script>
<?= $this->endSection() ?>
+28
View File
@@ -0,0 +1,28 @@
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Edit Parent</h1>
</div>
<form action="/administrator/parent/update/<?php echo $parent['id']; ?>" method="post">
<div class="form-group">
<label for="firstname">First Name</label>
<input type="text" class="form-control" id="firstname" name="firstname"
value="<?php echo $parent['firstname']; ?>" required>
</div>
<div class="form-group">
<label for="lastname">Last Name</label>
<input type="text" class="form-control" id="lastname" name="lastname"
value="<?php echo $parent['lastname']; ?>" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" class="form-control" id="email" name="email" value="<?php echo $parent['email']; ?>"
required>
</div>
<div class="form-group">
<label for="password">Password (leave blank to keep current password)</label>
<input type="password" class="form-control" id="password" name="password">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</main>
+23
View File
@@ -0,0 +1,23 @@
<div class="content-wrapper">
<div class="container">
<h1>Edit Teacher</h1>
<form action="teacher/update/<?php echo $teacher['id']; ?>" method="POST">
<div class="form-group">
<label for="firstname">First Name:</label>
<input type="text" class="form-control" id="firstname" name="firstname"
value="<?php echo $teacher['firstname']; ?>" required>
</div>
<div class="form-group">
<label for="lastname">Last Name:</label>
<input type="text" class="form-control" id="lastname" name="lastname"
value="<?php echo $teacher['lastname']; ?>" required>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" class="form-control" id="email" name="email"
value="<?php echo $teacher['email']; ?>" required>
</div>
<button type="submit" class="btn btn-primary">Update</button>
</form>
</div>
</div>
@@ -0,0 +1,27 @@
<div class="container mt-5">
<h2>Edit Emergency Contact</h2>
<form action="<?= base_url('administrator/emergency_contact/update/' . $contact['id']) ?>" method="post">
<?= csrf_field() ?>
<div class="form-group">
<label>Name:</label>
<input type="text" class="form-control" name="emergency_contact_name"
value="<?= esc($contact['emergency_contact_name']) ?>" required>
</div>
<div class="form-group">
<label>Phone:</label>
<input type="text" class="form-control" name="cellphone" value="<?= esc($contact['cellphone']) ?>"
required>
</div>
<div class="form-group">
<label>Relation:</label>
<input type="text" class="form-control" name="relation" value="<?= esc($contact['relation']) ?>"
required>
</div>
<button type="submit" class="btn btn-primary">Update</button>
<a href="<?= base_url('administrator/emergency_contact') ?>" class="btn btn-secondary">Cancel</a>
</form>
</div>
@@ -0,0 +1,148 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content">
<h2 class="text-center mt-4 mb-3">Emergency Contact Information</h2>
<?= $this->include('partials/academic_filter') ?>
<div class="table-responsive">
<table id="emergencyTable" class="display table table-bordered table-striped align-middle">
<thead class="table-dark">
<tr>
<th>Parent Name</th>
<th>Students</th>
<th>Contact Name</th>
<th>Relation</th>
<th>Phone</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php $modals = ''; ?>
<?php foreach ($groups as $group): ?>
<?php
// Build clickable student names (open Family Card)
$studentNames = array_map(function($s) {
$sid = (int)($s['id'] ?? $s['student_id'] ?? 0);
$label = esc(($s['firstname'] ?? '') . ' ' . ($s['lastname'] ?? ''));
if ($sid > 0) {
return '<a href="#" class="text-decoration-none" data-family-student-id="' . $sid . '">' . $label . '</a>';
}
return $label;
}, $group['students']);
$studentsCell = implode('<br>', $studentNames);
?>
<?php foreach ($group['contacts'] as $contact): ?>
<tr>
<td>
<?php $pid = (int)($group['parent_id'] ?? 0); ?>
<?php if ($pid): ?>
<a href="#" class="text-decoration-none" data-family-guardian-id="<?= $pid ?>">
<?= esc($group['parent_name']) ?>
</a>
<?php else: ?>
<?= esc($group['parent_name']) ?>
<?php endif; ?>
</td>
<td><?= $studentsCell ?></td>
<td><?= esc($contact['emergency_contact_name']) ?></td>
<td><?= esc($contact['relation']) ?></td>
<td>
<?php
$phone = trim((string)($contact['cellphone'] ?? ''));
if ($phone === '' && !empty($group['parent_phones']) && is_array($group['parent_phones'])) {
$phone = implode(' / ', array_map('esc', $group['parent_phones']));
}
echo $phone !== '' ? esc($phone) : 'N/A';
?>
</td>
<td>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal"
data-bs-target="#editContactModal<?= (int)$contact['id'] ?>">
Edit
</button>
<a href="<?= base_url('administrator/emergency_contact/delete/' . (int)$contact['id']) ?>"
class="btn btn-sm btn-danger"
onclick="return confirm('Are you sure?')">Delete</a>
</td>
</tr>
<?php
$modalId = (int)$contact['id'];
$modals .= '
<div class="modal fade"
id="editContactModal' . $modalId . '"
tabindex="-1"
aria-labelledby="editContactLabel' . $modalId . '"
aria-hidden="true"
data-bs-backdrop="static"
data-bs-keyboard="false">
<div class="modal-dialog" role="document">
<form action="' . base_url('administrator/emergency_contact/update/' . $modalId) . '"
method="post"
class="modal-content">
' . csrf_field() . '
<div class="modal-header">
<h5 class="modal-title" id="editContactLabel' . $modalId . '">Edit Emergency Contact</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="name_' . $modalId . '" class="form-label">Name</label>
<input type="text" class="form-control"
name="emergency_contact_name"
id="name_' . $modalId . '"
value="' . esc($contact['emergency_contact_name']) . '"
required>
</div>
<div class="mb-3">
<label for="phone_' . $modalId . '" class="form-label">Phone</label>
<input type="text" class="form-control"
name="cellphone"
id="phone_' . $modalId . '"
value="' . esc($contact['cellphone']) . '"
required>
</div>
<div class="mb-3">
<label for="relation_' . $modalId . '" class="form-label">Relation</label>
<input type="text" class="form-control"
name="relation"
id="relation_' . $modalId . '"
value="' . esc($contact['relation']) . '"
required>
</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-primary">Update</button>
</div>
</form>
</div>
</div>';
?>
<?php endforeach; ?>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- All modals rendered outside the table -->
<?= $modals ?>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
$(document).ready(function() {
$('#emergencyTable').DataTable({
pageLength: 100,
lengthMenu: [5, 10, 25, 50, 100],
columnDefs: [
{ orderable: false, targets: [1, 5] } // Students + Actions columns not sortable
]
});
});
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,59 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container mt-4">
<h2>Create Event</h2>
<form method="post" action="<?= site_url('administrator/events/create') ?>" enctype="multipart/form-data">
<?= csrf_field() ?>
<div class="mb-3">
<label class="form-label">Event Name</label>
<input type="text" name="event_name" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Category</label>
<select name="event_category" class="form-control" required>
<option value="" selected disabled>Select category</option>
<?php foreach (($categories ?? []) as $category): ?>
<option value="<?= esc($category) ?>"><?= esc(ucwords($category)) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea name="description" class="form-control"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Amount</label>
<input type="number" step="0.01" name="amount" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Flyer (optional)</label>
<input type="file" name="flyer" class="form-control">
</div>
<div class="mb-3">
<label class="form-label">Expiration Date</label>
<input type="date" name="expiration_date" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Semester</label>
<input type="text" name="semester" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">School Year</label>
<input type="text" name="school_year" class="form-control" required>
</div>
<button type="submit" class="btn btn-success">Create Event</button>
<a href="<?= site_url('administrator/events') ?>" class="btn btn-secondary">Cancel</a>
</form>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,78 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container mt-4">
<h2>Edit Event</h2>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= session()->getFlashdata('error') ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success"><?= session()->getFlashdata('success') ?></div>
<?php endif; ?>
<form method="post" action="<?= site_url('administrator/events/edit/' . $event['id']) ?>" enctype="multipart/form-data">
<?= csrf_field() ?>
<div class="mb-3">
<label class="form-label">Event Name</label>
<input type="text" name="event_name" class="form-control" value="<?= esc($event['event_name']) ?>" required>
</div>
<div class="mb-3">
<label class="form-label">Category</label>
<select name="event_category" class="form-control" required>
<option value="" disabled <?= empty($event['event_category']) ? 'selected' : '' ?>>Select category</option>
<?php foreach (($categories ?? []) as $category): ?>
<option value="<?= esc($category) ?>" <?= (string)($event['event_category'] ?? '') === (string)$category ? 'selected' : '' ?>>
<?= esc(ucwords($category)) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea name="description" class="form-control"><?= esc($event['description']) ?></textarea>
</div>
<div class="mb-3">
<label class="form-label">Amount</label>
<input type="number" step="0.01" name="amount" class="form-control" value="<?= esc($event['amount']) ?>" required>
</div>
<div class="mb-3">
<label class="form-label">Current Flyer</label><br>
<?php if ($event['flyer']): ?>
<img src="<?= base_url('writable/uploads/' . $event['flyer']) ?>" width="150" class="mb-2">
<?php else: ?>
<div class="text-muted">No flyer uploaded.</div>
<?php endif; ?>
</div>
<div class="mb-3">
<label class="form-label">New Flyer (optional)</label>
<input type="file" name="flyer" class="form-control">
</div>
<div class="mb-3">
<label class="form-label">Expiration Date</label>
<input type="date" name="expiration_date" class="form-control" value="<?= esc($event['expiration_date']) ?>" required>
</div>
<div class="mb-3">
<label class="form-label">Semester</label>
<input type="text" name="semester" class="form-control" value="<?= esc($event['semester']) ?>" required>
</div>
<div class="mb-3">
<label class="form-label">School Year</label>
<input type="text" name="school_year" class="form-control" value="<?= esc($event['school_year']) ?>" required>
</div>
<button type="submit" class="btn btn-primary">Update Event</button>
<a href="<?= site_url('administrator/events') ?>" class="btn btn-secondary">Cancel</a>
</form>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,162 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container mt-5">
<h3>Event Charges</h3>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success"><?= session()->getFlashdata('success') ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= session()->getFlashdata('error') ?></div>
<?php endif; ?>
<!-- Add New Charge Form -->
<form action="<?= site_url('payment/event_charges') ?>" method="post">
<?= csrf_field() ?>
<div class="row">
<div class="col-md-3 mt-3">
<label for="event_id" class="form-label">Select Event</label>
<select name="event_id" id="event_id" class="form-select" required>
<option value="">-- Select Event --</option>
<?php foreach ($events as $event): ?>
<option value="<?= esc($event['id']) ?>">
<?= esc($event['event_name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3 mt-3">
<label for="parent_id" class="form-label">Parent</label>
<select id="parent_id" name="parent_id" class="form-select" required>
<option value="">-- Select Parent --</option>
<?php foreach ($parents as $parent): ?>
<option value="<?= $parent['id'] ?>">
<?= esc($parent['firstname'] . ' ' . $parent['lastname']) ?> (<?= esc($parent['school_id']) ?>)
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12 mt-3" id="studentCheckboxContainer" style="display: none;">
<label>Select Students:</label>
<div id="studentList" class="row"></div>
</div>
</div>
<button type="submit" class="btn btn-primary mt-3">Submit</button>
<a href="<?= site_url('administrator/events') ?>" class="btn btn-success mt-3">Event List</a>
</form>
<br>
<!-- Charge Tables grouped by event -->
<?php
$grouped = [];
foreach ($charges as $charge) {
$label = $charge['event_name'] ?? 'N/A';
$grouped[$label][] = $charge;
}
?>
<?php if (empty($grouped)): ?>
<div class="alert alert-info">No charges found.</div>
<?php else: ?>
<?php foreach ($grouped as $eventLabel => $rows): ?>
<div class="card mb-4">
<div class="card-header bg-light fw-semibold">
<?= esc($eventLabel) ?>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-bordered table-striped mb-0 no-mgmt-sticky">
<thead class="table-light">
<tr>
<th>ID</th>
<th>Parent Name</th>
<th>Student Name</th>
<th>Charged Amount</th>
<th>Is Participating</th>
<th>Semester</th>
<th>Year</th>
<th>Created</th>
</tr>
</thead>
<tbody>
<?php foreach ($rows as $charge): ?>
<tr>
<td><?= esc($charge['id']) ?></td>
<td><?= esc($charge['parent_firstname'] . ' ' . $charge['parent_lastname']) ?></td>
<td>
<?= $charge['student_firstname']
? esc($charge['student_firstname'] . ' ' . $charge['student_lastname'])
: '-' ?>
</td>
<td>$<?= esc(number_format($charge['charged'] ?? 0, 2)) ?></td>
<td>
<?= $charge['participation']
? '<span class="badge bg-success">Yes</span>'
: '<span class="badge bg-warning">No</span>' ?>
</td>
<td><?= esc($charge['semester'] ?? '-') ?></td>
<td><?= esc($charge['school_year'] ?? '-') ?></td>
<td><?= esc(!empty($charge['created_at']) ? local_datetime($charge['created_at'], 'm-d-Y H:i') : '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
function loadStudentsWithCharges() {
let parentId = $('#parent_id').val();
let eventId = $('#event_id').val();
let semester = '<?= esc($semester) ?>';
let schoolYear = '<?= esc($school_year) ?>';
if (parentId && eventId) {
$.get('<?= base_url('administrator/get-students-with-charges') ?>', {
parent_id: parentId,
event_id: eventId,
semester: semester,
school_year: schoolYear
}, function(data) {
let container = $('#studentList');
container.empty();
if (data.length > 0) {
$('#studentCheckboxContainer').show();
data.forEach(student => {
container.append(`
<div class="col-md-4">
<div class="form-check">
<input type="hidden" name="participation[${student.id}]" value="no">
<input class="form-check-input" type="checkbox" name="participation[${student.id}]" value="yes" id="student_${student.id}" ${student.charged ? 'checked' : ''}>
<label class="form-check-label" for="student_${student.id}">
${student.name}
</label>
</div>
</div>
`);
});
} else {
$('#studentCheckboxContainer').hide();
}
}, 'json');
} else {
$('#studentCheckboxContainer').hide();
$('#studentList').empty();
}
}
$('#parent_id').on('change', loadStudentsWithCharges);
$('#event_id').on('change', loadStudentsWithCharges);
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,60 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container mt-4">
<h2 class="text-center mt-4 mb-3">Event Managment</h2>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success"><?= session()->getFlashdata('success') ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= session()->getFlashdata('error') ?></div>
<?php endif; ?>
<?php if (empty($events)): ?>
<div class="alert alert-info mb-3 d-inline-block">No events found.</div>
<?php else: ?>
<div class="table-responsive">
<table class="table table-bordered no-mgmt-sticky">
<thead>
<tr>
<th>Event Name</th>
<th>Category</th>
<th>Amount</th>
<th>Expiration Date</th>
<th>Semester</th>
<th>School Year</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($events as $event): ?>
<tr>
<td><?= esc($event['event_name']) ?></td>
<td><?= esc($event['event_category'] ?? '—') ?></td>
<td><?= esc($event['amount']) ?></td>
<td><?= esc(!empty($event['expiration_date']) ? local_date($event['expiration_date'], 'm-d-Y') : '') ?></td>
<td><?= esc($event['semester']) ?></td>
<td><?= esc($event['school_year']) ?></td>
<td>
<a href="<?= site_url('administrator/events/edit/' . $event['id']) ?>" class="btn btn-sm btn-warning">Edit</a>
<form action="<?= site_url('administrator/events/delete/' . $event['id']) ?>" method="post" style="display:inline;">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure you want to delete this event?')">Delete</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<div class="d-flex gap-2 mb-3 justify-content-end">
<a href="<?= site_url('administrator/events/create') ?>" class="btn btn-primary mb-3">Create New Event</a>
<a href="<?= site_url('administrator/event-charges') ?>" class="btn btn-secondary mb-3">List Event Participants</a>
</div>
</div>
<?= $this->endSection() ?>
+290
View File
@@ -0,0 +1,290 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid px-4 py-4">
<div class="d-flex flex-column flex-lg-row justify-content-between align-items-lg-center gap-3 mb-4">
<div>
<h1 class="h3 mb-1">Exam Draft Submissions</h1>
<p class="text-muted mb-0 small">
<?= esc($semester ?: 'Semester') ?> <?= esc($schoolYear ?: '') ?>
</p>
</div>
<div class="text-muted small d-flex flex-column align-items-lg-end">
<span>Allowed files: <?= esc(implode(', ', $allowedExtensions)) ?></span>
<span>Max size: <?= number_format($maxUploadBytes / 1024 / 1024, 0) ?> MB</span>
</div>
</div>
<?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; ?>
<ul class="nav nav-pills mb-3" id="examDraftTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="submissions-tab" data-bs-toggle="pill" data-bs-target="#submissions" type="button" role="tab" aria-controls="submissions" aria-selected="true">
Submissions
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="legacy-tab" data-bs-toggle="pill" data-bs-target="#legacy" type="button" role="tab" aria-controls="legacy" aria-selected="false">
Legacy Exams
</button>
</li>
</ul>
<div class="tab-content" id="examDraftTabsContent">
<div class="tab-pane fade show active" id="submissions" role="tabpanel" aria-labelledby="submissions-tab">
<div class="card mb-4">
<div class="card-body">
<?php if (empty($drafts)): ?>
<p class="text-muted mb-0">No exam drafts have been submitted yet.</p>
<?php else: ?>
<div class="table-responsive">
<table class="table table-striped table-bordered align-middle mb-0 exam-drafts-table no-mgmt-sticky">
<thead class="table-light">
<tr>
<th>Teacher</th>
<th>Class</th>
<th>Title / Type</th>
<th>Version</th>
<th>Submitted</th>
<th>Status</th>
<th>Files</th>
<th>PDF</th>
<th>Review</th>
</tr>
</thead>
<tbody>
<?php foreach ($drafts as $draft): ?>
<?php
$teacherName = trim(($draft['teacher_first'] ?? '') . ' ' . ($draft['teacher_last'] ?? ''));
if ($teacherName === '') {
$teacherName = ($draft['admin_id'] ?? null) === ($draft['teacher_id'] ?? null)
? 'Admin Upload'
: 'User #' . ($draft['teacher_id'] ?? 'N/A');
}
$statusInfo = $statusBadges[$draft['status'] ?? 'pending'] ?? ['label' => 'Unknown', 'class' => 'bg-secondary text-white'];
$adminName = trim(($draft['admin_first'] ?? '') . ' ' . ($draft['admin_last'] ?? ''));
?>
<tr>
<td><?= esc($teacherName) ?></td>
<td><?= esc($draft['class_section_name'] ?? 'Unknown') ?></td>
<td>
<strong><?= esc($draft['draft_title'] ?? 'Untitled') ?></strong>
<div class="small text-muted"><?= esc($draft['exam_type'] ?? 'N/A') ?></div>
<?php if (!empty($draft['description'])): ?>
<div class="small text-muted"><?= esc($draft['description']) ?></div>
<?php endif; ?>
</td>
<td class="text-nowrap">v<?= esc($draft['version'] ?? 1) ?></td>
<td class="text-nowrap"><?= esc((!empty($draft['created_at']) ? local_datetime($draft['created_at'], 'M j, Y g:i A') : '—')) ?></td>
<td>
<span class="badge <?= esc($statusInfo['class']) ?>">
<?= esc($statusInfo['label']) ?>
</span>
<?php if (!empty($draft['reviewed_at'])): ?>
<div class="small text-muted mt-1">
Reviewed <?= esc(local_datetime($draft['reviewed_at'], 'M j, Y g:i A')) ?>
<?php if ($adminName !== ''): ?>
by <?= esc($adminName) ?>
<?php endif; ?>
</div>
<?php endif; ?>
</td>
<td>
<?php if (!empty($draft['teacher_file'])): ?>
<div>
<a href="<?= base_url('exam-drafts/files/teacher/' . $draft['teacher_file']) ?>" target="_blank">
<?= esc($draft['teacher_filename'] ?? 'Submitted draft') ?>
</a>
</div>
<?php else: ?>
<span class="text-muted small">No teacher file</span>
<?php endif; ?>
<?php if (!empty($draft['final_file'])): ?>
<div class="mt-2">
<a href="<?= base_url('exam-drafts/files/final/' . $draft['final_file']) ?>" target="_blank" class="link-success small">
<?= esc($draft['final_filename'] ?? 'Final draft') ?>
</a>
</div>
<?php endif; ?>
</td>
<td class="text-nowrap">
<?php
$pdfFile = $draft['final_pdf_file'] ?? null;
// Fallback: if final_file itself is a pdf, use it
if (empty($pdfFile) && !empty($draft['final_file']) && strtolower(pathinfo($draft['final_file'], PATHINFO_EXTENSION)) === 'pdf') {
$pdfFile = $draft['final_file'];
}
?>
<?php if (!empty($pdfFile)): ?>
<a href="<?= base_url('exam-drafts/files/final/' . $pdfFile) ?>" target="_blank" class="link-success small">
PDF
</a>
<?php else: ?>
<span class="text-muted small">—</span>
<?php endif; ?>
</td>
<td>
<form method="post" action="<?= base_url('/administrator/exam-drafts/review') ?>" enctype="multipart/form-data">
<?= csrf_field() ?>
<input type="hidden" name="draft_id" value="<?= esc($draft['id']) ?>">
<div class="mb-2">
<textarea
name="admin_comments"
rows="2"
class="form-control form-control-sm"
placeholder="Optional note for the teacher"
><?= esc($draft['admin_comments'] ?? '') ?></textarea>
</div>
<div class="mb-2">
<label class="form-label small mb-1">Status</label>
<select name="review_status" class="form-select form-select-sm">
<?php foreach ($statusOptions as $option): ?>
<option value="<?= esc($option) ?>" <?= ($option === ($draft['status'] ?? '')) ? 'selected' : '' ?>>
<?= esc(ucfirst($option)) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-2">
<label class="form-label small mb-1">Upload final draft</label>
<input type="file" name="final_file" class="form-control form-control-sm">
</div>
<button type="submit" class="btn btn-sm btn-primary w-100">
Save review
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
<div class="tab-pane fade" id="legacy" role="tabpanel" aria-labelledby="legacy-tab">
<div class="card border-primary-subtle mb-3">
<div class="card-header bg-light d-flex justify-content-between align-items-center">
<div>
<strong>Upload Old / Legacy Exam</strong>
<div class="small text-muted">Store historic exams as finalized records.</div>
</div>
<span class="badge bg-primary-subtle text-primary">Admin only</span>
</div>
<div class="card-body">
<form method="post" action="<?= base_url('/administrator/exam-drafts/upload-legacy') ?>" enctype="multipart/form-data" class="row g-3">
<?= csrf_field() ?>
<div class="col-md-4">
<label class="form-label small">Class Section</label>
<select name="class_section_id" class="form-select" required>
<option value="">Select class</option>
<?php foreach ($classSections as $cs): ?>
<option value="<?= esc($cs['class_section_id']) ?>"><?= esc($cs['class_section_name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<label class="form-label small">School Year</label>
<input type="text" name="school_year" class="form-control" value="<?= esc($schoolYear) ?>" placeholder="2025-2026" required>
</div>
<div class="col-md-2">
<label class="form-label small">Semester</label>
<select name="semester" class="form-select" required>
<option value="Fall" <?= ($semester === 'Fall') ? 'selected' : '' ?>>Fall</option>
<option value="Spring" <?= ($semester === 'Spring') ? 'selected' : '' ?>>Spring</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label small">Exam Type</label>
<select name="exam_type" class="form-select">
<option value="">— Select type —</option>
<?php foreach ($examTypes as $type): ?>
<option value="<?= esc($type) ?>"><?= esc($type) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12">
<label class="form-label small">Upload File</label>
<input type="file" name="old_exam_file" class="form-control" accept=".doc,.docx,.pdf" required>
<div class="form-text">Allowed: <?= esc(implode(', ', $allowedExtensions)) ?> • Max <?= number_format($maxUploadBytes / 1024 / 1024, 0) ?> MB</div>
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary">Upload Legacy Exam</button>
</div>
</form>
</div>
</div>
<div class="card mb-4">
<div class="card-body">
<?php if (empty($legacyByClass)): ?>
<p class="text-muted mb-0">No legacy exams uploaded yet.</p>
<?php else: ?>
<?php foreach ($legacyByClass as $group): ?>
<div class="mb-4">
<h6 class="mb-2"><?= esc($group['class_section_name'] ?? 'Class') ?></h6>
<div class="list-group">
<?php foreach ($group['items'] as $item): ?>
<div class="list-group-item d-flex justify-content-between align-items-start">
<div class="me-3">
<div class="fw-semibold"><?= esc($item['draft_title'] ?? 'Legacy Exam') ?></div>
<div class="small text-muted">
<?= esc($item['exam_type'] ?? 'N/A') ?> •
<?= esc($item['semester'] ?? '') ?> <?= esc($item['school_year'] ?? '') ?>
</div>
</div>
<div class="text-end">
<?php if (!empty($item['final_file'])): ?>
<a href="<?= base_url('exam-drafts/files/final/' . $item['final_file']) ?>" target="_blank" class="btn btn-sm btn-outline-primary">
Download
</a>
<?php else: ?>
<span class="text-muted small">File missing</span>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('styles') ?>
<style>
.exam-drafts-table th,
.exam-drafts-table td {
white-space: normal;
word-break: break-word;
}
.exam-drafts-table th {
min-width: 120px;
}
.exam-drafts-table td {
max-width: 220px;
}
.exam-drafts-table {
table-layout: auto;
}
.exam-drafts-table thead th {
background-color: #f8f9fa;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
padding-bottom: 0.75rem;
}
.exam-drafts-table tbody tr {
background-color: #fff;
}
</style>
<?= $this->endSection() ?>
@@ -0,0 +1,42 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Expense Management</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Expense ID</th>
<th>Description</th>
<th>Amount</th>
<th>Date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$expenses = [
['expense_id' => 'E001', 'description' => 'Purchase of textbooks', 'amount' => '$500', 'date' => '2024-07-01', 'status' => 'Approved'],
['expense_id' => 'E002', 'description' => 'Lab equipment maintenance', 'amount' => '$300', 'date' => '2024-07-02', 'status' => 'Pending'],
['expense_id' => 'E003', 'description' => 'Sports equipment', 'amount' => '$700', 'date' => '2024-07-03', 'status' => 'Approved'],
// Add more records as needed
];
foreach ($expenses as $expense) {
$dateDisp = !empty($expense['date']) ? local_date($expense['date'], 'm-d-Y') : '';
echo "<tr>
<td>{$expense['expense_id']}</td>
<td>{$expense['description']}</td>
<td>{$expense['amount']}</td>
<td>" . esc($dateDisp) . "</td>
<td>{$expense['status']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,44 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Fee Collection</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Payment ID</th>
<th>Student Name</th>
<th>Amount</th>
<th>Date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$feeCollections = [
['payment_id' => 'F001', 'student_name' => 'John Doe', 'amount' => '$200', 'date' => '2024-07-01', 'status' => 'Paid'],
['payment_id' => 'F002', 'student_name' => 'Jane Smith', 'amount' => '$250', 'date' => '2024-07-02', 'status' => 'Pending'],
['payment_id' => 'F003', 'student_name' => 'Michael Johnson', 'amount' => '$300', 'date' => '2024-07-03', 'status' => 'Paid'],
// Add more records as needed
];
foreach ($feeCollections as $collection) {
$dateDisp = !empty($collection['date']) ? local_date($collection['date'], 'm-d-Y') : '';
echo "<tr>
<td>{$collection['payment_id']}</td>
<td>{$collection['student_name']}</td>
<td>{$collection['amount']}</td>
<td>" . esc($dateDisp) . "</td>
<td>{$collection['status']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,44 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Fee Payment Records</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Payment ID</th>
<th>Student Name</th>
<th>Amount</th>
<th>Date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$feePayments = [
['payment_id' => 'F001', 'student_name' => 'John Doe', 'amount' => '$200', 'date' => '2024-07-01', 'status' => 'Paid'],
['payment_id' => 'F002', 'student_name' => 'Jane Smith', 'amount' => '$250', 'date' => '2024-07-02', 'status' => 'Pending'],
['payment_id' => 'F003', 'student_name' => 'Michael Johnson', 'amount' => '$300', 'date' => '2024-07-03', 'status' => 'Paid'],
// Add more records as needed
];
foreach ($feePayments as $payment) {
$dateDisp = !empty($payment['date']) ? local_date($payment['date'], 'm-d-Y') : '';
echo "<tr>
<td>{$payment['payment_id']}</td>
<td>{$payment['student_name']}</td>
<td>{$payment['amount']}</td>
<td>" . esc($dateDisp) . "</td>
<td>{$payment['status']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,44 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Feedback and Complaints</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Feedback/Complaint</th>
<th>Status</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$feedbackComplaints = [
['id' => '1', 'name' => 'John Doe', 'email' => 'john.doe@example.com', 'message' => 'Great school!', 'status' => 'Resolved', 'date' => '2024-07-01'],
['id' => '2', 'name' => 'Jane Smith', 'email' => 'jane.smith@example.com', 'message' => 'Issue with teacher', 'status' => 'Pending', 'date' => '2024-07-02'],
['id' => '3', 'name' => 'Michael Johnson', 'email' => 'michael.johnson@example.com', 'message' => 'Need more facilities', 'status' => 'In Progress', 'date' => '2024-07-03'],
// Add more records as needed
];
foreach ($feedbackComplaints as $fc) {
$dateDisp = !empty($fc['date']) ? local_date($fc['date'], 'm-d-Y') : '';
echo "<tr>
<td>{$fc['id']}</td>
<td>{$fc['name']}</td>
<td>{$fc['email']}</td>
<td>{$fc['message']}</td>
<td>{$fc['status']}</td>
<td>" . esc($dateDisp) . "</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
<?= $this->endSection() ?>
+39
View File
@@ -0,0 +1,39 @@
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Scores</h1>
<a href="/administrator/scores/create" class="btn btn-primary">Add New Score</a>
</div>
<table class="table table-striped mt-3">
<thead>
<tr>
<th>ID</th>
<th>Student Name</th>
<th>Course</th>
<th>Score</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (!empty($scores)): ?>
<?php foreach ($scores as $score): ?>
<tr>
<td><?php echo $score['id']; ?></td>
<td><?php echo $score['student_name']; ?></td>
<td><?php echo $score['course']; ?></td>
<td><?php echo $score['score']; ?></td>
<td>
<a href="/administrator/scores/edit/<?php echo $score['id']; ?>" class="btn btn-warning">Edit</a>
<a href="/administrator/scores/delete/<?php echo $score['id']; ?>" class="btn btn-danger"
onclick="return confirm('Are you sure you want to delete this score record?');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5">No score records found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</main>
@@ -0,0 +1,72 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1 class="text-center">Score Management</h1>
<div class="d-flex justify-content-center">
<div class="col-12 col-lg-10">
<?= $this->include('partials/academic_filter') ?>
</div>
</div>
<br>
<!-- Tabs navigation -->
<ul class="nav nav-tabs justify-content-center" id="gradingTabs" role="tablist">
<?php foreach ($grades as $classId => $sections): ?>
<li class="nav-item">
<a class="nav-link <?= ($classId === 1) ? 'active' : '' ?>"
id="grade<?= $classId ?>-tab"
data-bs-toggle="tab"
href="#grade<?= $classId ?>"
role="tab"
aria-controls="grade<?= $classId ?>"
aria-selected="<?= ($classId === 1) ? 'true' : 'false' ?>">
<?= $classId === 12 ? 'Youth' : "Grade $classId" ?>
</a>
</li>
<?php endforeach; ?>
</ul>
<!-- Tabs content -->
<div class="tab-content mt-4">
<?php foreach ($grades as $classId => $sections): ?>
<div class="tab-pane fade <?= ($classId === 1) ? 'show active' : '' ?>" id="grade<?= $classId ?>" role="tabpanel">
<?php foreach ($sections as $section): ?>
<?php
$sectionId = $section['class_section_id'];
if (empty($studentsBySection[$sectionId])) continue;
?>
<h4 class="text-center mt-4"><?= esc($section['class_section_name']) ?></h4>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Student Name</th>
<th>School ID</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($studentsBySection[$sectionId] as $student): ?>
<tr>
<td><?= esc($student['firstname'] . ' ' . $student['lastname']) ?></td>
<td><?= esc($student['school_id']) ?></td>
<td>
<?php
$types = ['homework', 'quiz', 'project', 'test', 'midterm', 'final', 'comments'];
foreach ($types as $type):
?>
<a href="<?= base_url("grading/$type/{$sectionId}/{$student['id']}") ?>" class="btn btn-sm btn-outline-primary m-1">
<?= ucfirst($type) ?>
</a>
<?php endforeach; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,43 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Health Records</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Student ID</th>
<th>Name</th>
<th>Date of Birth</th>
<th>Allergies</th>
<th>Medical Conditions</th>
<th>Emergency Contact</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$healthRecords = [
['student_id' => 'S001', 'name' => 'John Doe', 'dob' => '2008-05-01', 'allergies' => 'Peanuts', 'conditions' => 'Asthma', 'emergency_contact' => 'john.doe@example.com'],
['student_id' => 'S002', 'name' => 'Jane Smith', 'dob' => '2007-03-15', 'allergies' => 'None', 'conditions' => 'None', 'emergency_contact' => 'jane.smith@example.com'],
['student_id' => 'S003', 'name' => 'Michael Johnson', 'dob' => '2006-09-20', 'allergies' => 'Latex', 'conditions' => 'Diabetes', 'emergency_contact' => 'michael.johnson@example.com'],
// Add more records as needed
];
foreach ($healthRecords as $record) {
echo "<tr>
<td>{$record['student_id']}</td>
<td>{$record['name']}</td>
<td>{$record['dob']}</td>
<td>{$record['allergies']}</td>
<td>{$record['conditions']}</td>
<td>{$record['emergency_contact']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
<?= $this->endSection() ?>
+43
View File
@@ -0,0 +1,43 @@
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4" id="main-content">
<div
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboard</h1>
</div>
<div id="content">
<!-- Initial content goes here -->
<p>Welcome to the administrator dashboard.</p>
</div>
</main>
<?php include(__DIR__ . '/../partials/footer_back.php'); ?>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-cVj0DXe9C1fYM2oK5RD7T0I5vBnv3tntntGiTfP1F3e5Sf5fp3E3Ql5h0QQjxyV" crossorigin="anonymous"></script>
<script>
$(document).ready(function() {
$('.nav-link').click(function(e) {
var target = $(this).attr('href');
if (target.startsWith('#')) {
// Handle collapse toggle for menu items
return;
}
e.preventDefault();
$.ajax({
url: target,
method: 'GET',
success: function(response) {
$('#content').html(response);
},
error: function() {
$('#content').html(
'<p>Sorry, an error occurred while loading the page.</p>');
}
});
});
});
</script>
+112
View File
@@ -0,0 +1,112 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content">
<h2 class="text-center mt-4 mb-3">IP Bans</h2>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success"><?= esc(session()->getFlashdata('success')) ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<div class="d-flex justify-content-between align-items-center mb-3">
<form method="get" action="<?= site_url('administrator/ip_bans') ?>" class="d-flex align-items-center gap-2">
<label class="me-2">Show</label>
<select name="status" class="form-select form-select-sm" style="min-width: 160px;">
<?php $cur = strtolower((string)($status ?? 'all')); ?>
<option value="all" <?= $cur==='all'?'selected':'' ?>>All entries</option>
<option value="active" <?= $cur==='active'?'selected':'' ?>>Active bans only</option>
</select>
<button type="submit" class="btn btn-secondary btn-sm">Apply</button>
</form>
<form method="post" action="<?= site_url('administrator/ip_bans/unban') ?>">
<?= csrf_field() ?>
<input type="hidden" name="all" value="1" />
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Unban all currently blocked IPs?')">Unban All</button>
</form>
</div>
<div class="table-responsive">
<table id="ipBansTable" class="table table-striped table-hover align-middle" style="width:100%">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>IP Address</th>
<th>Attempts</th>
<th>Last Attempt</th>
<th>Blocked Until</th>
<th>Status</th>
<th>Created</th>
<th>Updated</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (!empty($banned)): ?>
<?php foreach ($banned as $row): ?>
<?php $isBlocked = !empty($row['blocked_until']) && strtotime($row['blocked_until']) > time(); ?>
<tr>
<td><?= (int)$row['id'] ?></td>
<td><?= esc($row['ip_address']) ?></td>
<td><?= (int)$row['attempts'] ?></td>
<td><?= esc(!empty($row['last_attempt_at']) ? local_datetime($row['last_attempt_at'], 'm-d-Y H:i') : '') ?></td>
<td><?= esc(!empty($row['blocked_until']) ? local_datetime($row['blocked_until'], 'm-d-Y H:i') : '') ?></td>
<td>
<?php if ($isBlocked): ?>
<span class="badge bg-danger">Active</span>
<?php else: ?>
<span class="badge bg-secondary">Not Active</span>
<?php endif; ?>
</td>
<td><?= esc(!empty($row['created_at']) ? local_datetime($row['created_at'], 'm-d-Y H:i') : '') ?></td>
<td><?= esc(!empty($row['updated_at']) ? local_datetime($row['updated_at'], 'm-d-Y H:i') : '') ?></td>
<td>
<?php if ($isBlocked): ?>
<form method="post" action="<?= site_url('administrator/ip_bans/unban') ?>" class="d-inline">
<?= csrf_field() ?>
<input type="hidden" name="id" value="<?= (int)$row['id'] ?>" />
<button type="submit" class="btn btn-sm btn-primary" title="Unban" onclick="return confirm('Unban IP <?= esc($row['ip_address']) ?>?')">Unban</button>
</form>
<?php else: ?>
<form method="post" action="<?= site_url('administrator/ip_bans/ban-now') ?>" class="d-inline me-1">
<?= csrf_field() ?>
<input type="hidden" name="id" value="<?= (int)$row['id'] ?>" />
<input type="hidden" name="hours" value="24" />
<button type="submit" class="btn btn-sm btn-warning" title="Ban for 24h" onclick="return confirm('Ban IP <?= esc($row['ip_address']) ?> for 24 hours?')">Ban 24h</button>
</form>
<form method="post" action="<?= site_url('administrator/ip_bans/unban') ?>" class="d-inline">
<?= csrf_field() ?>
<input type="hidden" name="id" value="<?= (int)$row['id'] ?>" />
<button type="submit" class="btn btn-sm btn-secondary" title="Clear Attempts" onclick="return confirm('Clear attempts for IP <?= esc($row['ip_address']) ?>?')">Clear Attempts</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
(function(){
if (typeof $ === 'undefined' || !$.fn.DataTable) return;
const table = $('#ipBansTable').DataTable({
responsive: true,
pageLength: 50,
lengthMenu: [10, 25, 50, 100],
order: [[4, 'desc']],
stateSave: true,
language: { emptyTable: 'No active IP bans.' }
});
})();
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,99 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid mt-3">
<h2 class="mb-3">Late Slip Logs</h2>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success"><?= esc(session()->getFlashdata('success')) ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<form class="row g-2 align-items-end mb-3" method="get" action="<?= site_url('administrator/late_slips') ?>">
<div class="col-auto">
<label for="school_year" class="form-label">School Year</label>
<input type="text" name="school_year" id="school_year" class="form-control" value="<?= esc($schoolYear ?? ($_GET['school_year'] ?? '')) ?>" placeholder="e.g. 2025-2026">
</div>
<div class="col-auto">
<label for="semester" class="form-label">Semester</label>
<?php $semVal = (string)($semester ?? ($_GET['semester'] ?? '')); ?>
<select name="semester" id="semester" class="form-select">
<option value="">—</option>
<option value="Fall" <?= (strcasecmp($semVal, 'Fall') === 0 ? 'selected' : '') ?>>Fall</option>
<option value="Spring" <?= (strcasecmp($semVal, 'Spring') === 0 ? 'selected' : '') ?>>Spring</option>
</select>
</div>
<div class="col-auto">
<label for="q" class="form-label">Student</label>
<input type="text" name="q" id="q" class="form-control" value="<?= esc($filters['q'] ?? '') ?>" placeholder="search name">
</div>
<div class="col-auto">
<label for="date_from" class="form-label">From</label>
<input type="date" name="date_from" id="date_from" class="form-control" value="<?= esc($filters['date_from'] ?? '') ?>">
</div>
<div class="col-auto">
<label for="date_to" class="form-label">To</label>
<input type="date" name="date_to" id="date_to" class="form-control" value="<?= esc($filters['date_to'] ?? '') ?>">
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary">Filter</button>
<a class="btn btn-secondary" href="<?= site_url('administrator/late_slips') ?>">Reset</a>
</div>
</form>
<div class="table-responsive">
<table class="table table-striped" id="lateSlipLogsTable">
<thead>
<tr>
<th>ID</th>
<th>Printed At</th>
<th>Student</th>
<th>Grade</th>
<th>Date</th>
<th>Time In</th>
<th>Reason</th>
<th>School Year</th>
<th>Admin</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach (($logs ?? []) as $r): ?>
<tr>
<td><?= (int)($r['id'] ?? 0) ?></td>
<td><?= esc(!empty($r['printed_at']) ? local_datetime($r['printed_at'], 'm-d-Y H:i') : '') ?></td>
<td><?= esc($r['student_name'] ?? '') ?></td>
<td><?= esc($r['grade'] ?? '') ?></td>
<td><?= esc(!empty($r['slip_date']) ? local_date($r['slip_date'], 'm-d-Y') : '') ?></td>
<td><?= esc($r['time_in'] ?? '') ?></td>
<td><?= esc($r['reason'] ?? '') ?></td>
<td><?= esc($r['school_year'] ?? '') ?></td>
<td><?= esc($r['admin_name'] ?? '') ?></td>
<td>
<form action="<?= site_url('administrator/late_slips/reprint/' . (int)($r['id'] ?? 0)) ?>" method="post" class="d-inline">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm btn-primary" title="Reprint">
<i class="bi bi-printer"></i> Reprint
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
$(function(){
$('#lateSlipLogsTable').DataTable({
pageLength: 25,
order: [[0, 'desc']]
});
});
</script>
<?= $this->endSection() ?>
+37
View File
@@ -0,0 +1,37 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Manage Users</h1>
<a href="/administrator/add-user" class="btn btn-success">Add User</a>
<table class="table table-bordered mt-3">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Role</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($users as $user): ?>
<tr>
<td><?= $user['id']; ?></td>
<td><?= $user['name']; ?></td>
<td><?= $user['email']; ?></td>
<td><?= $user['role']; ?></td>
<td>
<a href="/administrator/edit-user/<?= $user['id']; ?>" class="btn btn-warning">Edit</a>
<a href="/administrator/delete-user/<?= $user['id']; ?>"
class="btn btn-danger">Delete</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</main>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,98 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid py-3">
<div class="d-flex align-items-center justify-content-between mb-2">
<div>
<h1 class="h4 mb-1">Admin Notification Subjects</h1>
<div class="text-muted">Choose the subject each admin should receive notifications for.</div>
</div>
</div>
<?= view('partials/flash_messages') ?>
<?php if (empty($tableReady)): ?>
<div class="alert alert-warning" role="alert">
Notification subject storage is not available. Please run migrations to enable saving.
</div>
<?php endif; ?>
<form method="post" action="<?= site_url('administrator/notifications_alerts/save') ?>">
<?= csrf_field() ?>
<div class="card">
<div class="card-header d-flex align-items-center justify-content-between">
<span>Admins</span>
<button type="submit" class="btn btn-primary btn-sm" <?= empty($tableReady) ? 'disabled' : '' ?>>Save</button>
</div>
<div class="card-body">
<div class="table-responsive">
<table id="adminNotificationsTable" class="table table-striped align-middle no-mgmt-sticky no-dt-fixedheader">
<thead class="table-dark">
<tr>
<th style="width: 60px;">#</th>
<th>Name</th>
<th>Email</th>
<?php foreach (($subjects ?? []) as $label): ?>
<th class="text-center" style="width: 140px;"><?= esc($label) ?></th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php if (empty($admins)): ?>
<tr>
<td colspan="<?= 3 + count($subjects ?? []) ?>" class="text-center text-muted">No admins found.</td>
</tr>
<?php else: ?>
<?php foreach ($admins as $index => $admin): ?>
<?php
$adminId = (int) ($admin['id'] ?? 0);
$selected = $assignedSubjects[$adminId] ?? [];
$fullName = trim(($admin['firstname'] ?? '') . ' ' . ($admin['lastname'] ?? ''));
?>
<tr>
<td><?= (int) $index + 1 ?></td>
<td><?= esc($fullName !== '' ? $fullName : ('Admin #' . $adminId)) ?></td>
<td><?= esc($admin['email'] ?? '') ?></td>
<?php foreach (($subjects ?? []) as $key => $label): ?>
<td class="text-center">
<input
type="checkbox"
class="form-check-input"
name="subjects[<?= $adminId ?>][]"
value="<?= esc($key) ?>"
<?= !empty($selected[$key]) ? 'checked' : '' ?>
<?= empty($tableReady) ? 'disabled' : '' ?>
>
</td>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<div class="card-footer text-end">
<button type="submit" class="btn btn-primary" <?= empty($tableReady) ? 'disabled' : '' ?>>Save Changes</button>
</div>
</div>
</form>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function () {
if (!window.jQuery || !window.jQuery.fn || !window.jQuery.fn.DataTable) {
return;
}
const selector = '#adminNotificationsTable';
if (window.jQuery.fn.DataTable.isDataTable(selector)) {
return;
}
window.jQuery(selector).DataTable({
pageLength: 25,
order: [[1, 'asc']]
});
});
</script>
<?= $this->endSection() ?>
+37
View File
@@ -0,0 +1,37 @@
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h2 class="text-center mt-4 mb-3">Parents</h2>
<a href="/administrator/parent/create" class="btn btn-primary">Add New Parent</a>
</div>
<table class="table table-striped mt-3">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (!empty($parents)): ?>
<?php foreach ($parents as $parent): ?>
<tr>
<td><?php echo $parent['id']; ?></td>
<td><?php echo $parent['firstname'] . ' ' . $parent['lastname']; ?></td>
<td><?php echo $parent['email']; ?></td>
<td>
<a href="/administrator/parent/edit/<?php echo $parent['id']; ?>" class="btn btn-warning">Edit</a>
<a href="/administrator/parent/delete/<?php echo $parent['id']; ?>" class="btn btn-danger"
onclick="return confirm('Are you sure you want to delete this parent?');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="4">No parents found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</main>
+143
View File
@@ -0,0 +1,143 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content">
<h2 class="text-center mt-4 mb-3">Parent Profiles</h2>
<?= $this->include('partials/academic_filter') ?>
<table id="myTable" class="display table table-striped table-bordered align-middle" style="width:100%">
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Email Address</th>
<th>Phone Number</th>
<th>Gender</th>
<th>Registration Date</th>
<th>Students</th>
</tr>
</thead>
<tbody>
<?php if (!empty($parents)): ?>
<?php foreach ($parents as $parent): ?>
<?php
// 1. Check if the students array is empty or doesn't exist
// If it is empty, 'continue' skips the entire <tr> for this parent
if (empty($parent['students'])) {
continue;
}
?>
<tr>
<td><?= esc($parent['school_id']) ?></td>
<td>
<a href="#" class="text-decoration-none" data-family-guardian-id="<?= (int)($parent['id'] ?? 0) ?>">
<?= esc($parent['firstname']) ?>
</a>
</td>
<td>
<a href="#" class="text-decoration-none" data-family-guardian-id="<?= (int)($parent['id'] ?? 0) ?>">
<?= esc($parent['lastname']) ?>
</a>
</td>
<td><?= esc($parent['email']) ?></td>
<td><?= esc($parent['cellphone']) ?></td>
<td><?= esc($parent['gender']) ?></td>
<td><?= esc(local_date($parent['created_at'], 'm-d-Y')) ?></td>
<td>
<ul class="list-unstyled mb-0">
<?php foreach ($parent['students'] as $student): ?>
<li><?= esc($student['name']) ?> (<?= esc($student['class_section']) ?>)</li>
<?php endforeach; ?>
</ul>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="8" class="text-center">No parent profiles found.</td></tr>
<?php endif; ?>
</tbody>
<tfoot>
<tr>
<th colspan="8" class="text-end"></th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
$(document).ready(function() {
function getFixedHeaderOffset(){
let total = 0;
const stack = [];
const header = document.querySelector('header.navbar.sticky-top, header.navbar.fixed-top');
if (header) stack.push(header);
const mgmt = document.getElementById('navbarManagement');
if (mgmt && (mgmt.classList.contains('sticky-top') || mgmt.classList.contains('fixed-top'))) stack.push(mgmt);
document.querySelectorAll('.navbar.sticky-top, .navbar.fixed-top').forEach(el => { if (!stack.includes(el)) stack.push(el); });
stack.forEach(el => { const h = el.offsetHeight || el.getBoundingClientRect().height || 0; total += Math.max(0, Math.round(h)); });
return total;
}
function loadScript(src, id){
return new Promise((resolve, reject)=>{
if (id && document.getElementById(id)) return resolve();
const s = document.createElement('script');
if (id) s.id = id;
s.src = src;
s.onload = resolve;
s.onerror = reject;
document.head.appendChild(s);
});
}
function loadCss(href, id){
return new Promise((resolve)=>{
if (id && document.getElementById(id)) return resolve();
const l = document.createElement('link');
if (id) l.id = id;
l.rel = 'stylesheet';
l.href = href;
l.onload = resolve;
document.head.appendChild(l);
});
}
function initTable(){
$('#myTable').DataTable({
responsive: true,
pageLength: 100,
lengthMenu: [5, 10, 25, 50, 100],
order: [[6, 'desc']], // Registration Date desc
dom: 'Bfrtip',
buttons: [
{ extend: 'csv', title: 'parent_profiles' },
{ extend: 'excel',title: 'parent_profiles' },
{ extend: 'pdf', title: 'parent_profiles', orientation: 'landscape', pageSize: 'A4' },
{ extend: 'print',title: 'Parent Profiles' }
],
columnDefs: [
{ targets: [7], className: 'text-center' }
],
fixedHeader: {
header: true,
headerOffset: getFixedHeaderOffset()
}
});
}
const hasFH = $.fn.dataTable && $.fn.dataTable.FixedHeader;
if (hasFH) {
initTable();
} else {
Promise.all([
loadScript('https://cdn.jsdelivr.net/npm/datatables.net-fixedheader@3.4.0/js/dataTables.fixedHeader.min.js', 'dt-fixedheader'),
loadCss('https://cdn.jsdelivr.net/npm/datatables.net-fixedheader-bs5@3.4.0/css/fixedHeader.bootstrap5.min.css', 'dt-fixedheader-css')
]).then(()=>initTable()).catch(()=>initTable());
}
});
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,82 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h2 class="text-center mt-4 mb-3">PayPal Transactions</h2>
<?= $this->include('partials/academic_filter') ?>
<!-- Export Button -->
<div class="d-flex gap-2 mb-3 justify-content-end">
<a href="<?= base_url('admin/paypal-transactions/export') . ($keyword ? '?q=' . urlencode($keyword) : '') ?>"
class="btn btn-success">
Export CSV
</a>
</div>
<!-- Search Form -->
<!--form method="get" action="<--?= base_url('admin/paypal-transactions') ?>" class="mb-3">
<--?= csrf_field(); ?>
<div class="input-group">
<input type="text" name="q" value="<--?= esc($keyword) ?>" class="form-control"
placeholder="Search by transaction ID, email, order ID, or event type">
<button type="submit" class="btn btn-primary">Search</button>
<--?php if ($keyword): ?>
<a href="<--?= base_url('admin/paypal-transactions') ?>" class="btn btn-secondary">Clear</a>
<--?php endif; ?>
</div>
</form-->
<!-- Transactions Table -->
<table id="myTable" class="display table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Transaction ID</th>
<th>Order ID</th>
<th>Parent School ID</th>
<th>Email</th>
<th>Amount</th>
<th>Net Amount</th>
<th>Currency</th>
<th>Status</th>
<th>Event Type</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<?php foreach ($transactions as $t): ?>
<tr>
<td><?= esc($t['id']) ?></td>
<td><?= esc($t['transaction_id']) ?></td>
<td><?= esc($t['order_id']) ?></td>
<td><?= esc($t['parent_school_id']) ?></td>
<td><?= esc($t['payer_email']) ?></td>
<td>$<?= esc(number_format($t['amount'], 2)) ?></td>
<td>$<?= esc(number_format($t['net_amount'], 2)) ?></td>
<td><?= esc($t['currency']) ?></td>
<td><?= esc($t['status']) ?></td>
<td><?= esc($t['event_type']) ?></td>
<td><?= esc(local_datetime($t['created_at'], 'm-d-Y H:i')) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- Pagination -->
<div class="d-flex justify-content-center">
<?= $pager->links() ?>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
$(document).ready(function() {
$('#myTable').DataTable({
"order": [
[0, "desc"]
],
"pageLength": 10
});
});
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,42 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Payroll Management</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Employee Name</th>
<th>Position</th>
<th>Salary</th>
<th>Pay Date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$payrolls = [
['name' => 'John Doe', 'position' => 'Teacher', 'salary' => '$3000', 'pay_date' => '2024-07-01', 'status' => 'Paid'],
['name' => 'Jane Smith', 'position' => 'administrator Assistant', 'salary' => '$2500', 'pay_date' => '2024-07-01', 'status' => 'Paid'],
['name' => 'Sam Brown', 'position' => 'Principal', 'salary' => '$5000', 'pay_date' => '2024-07-01', 'status' => 'Pending'],
// Add more payroll records as needed
];
foreach ($payrolls as $payroll) {
$dateDisp = !empty($payroll['pay_date']) ? local_date($payroll['pay_date'], 'm-d-Y') : '';
echo "<tr>
<td>{$payroll['name']}</td>
<td>{$payroll['position']}</td>
<td>{$payroll['salary']}</td>
<td>" . esc($dateDisp) . "</td>
<td>{$payroll['status']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,42 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Performance Reviews</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Employee</th>
<th>Position</th>
<th>Review Date</th>
<th>Rating</th>
<th>Comments</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in real scenario, this will be fetched from the database
$performanceReviews = [
['employee' => 'John Doe', 'position' => 'Teacher', 'review_date' => '2024-01-15', 'rating' => 'Excellent', 'comments' => 'Great performance throughout the year.'],
['employee' => 'Jane Smith', 'position' => 'administrator', 'review_date' => '2024-01-20', 'rating' => 'Good', 'comments' => 'Consistently meets expectations.'],
['employee' => 'Emily Johnson', 'position' => 'Teacher', 'review_date' => '2024-02-10', 'rating' => 'Satisfactory', 'comments' => 'Room for improvement in classroom management.'],
// Add more reviews as needed
];
foreach ($performanceReviews as $review) {
$dateDisp = !empty($review['review_date']) ? local_date($review['review_date'], 'm-d-Y') : '';
echo "<tr>
<td>{$review['employee']}</td>
<td>{$review['position']}</td>
<td>" . esc($dateDisp) . "</td>
<td>{$review['rating']}</td>
<td>{$review['comments']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,80 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid py-3">
<div class="d-flex align-items-center justify-content-between mb-3">
<div>
<h1 class="h4 mb-1">Print Notification Recipients</h1>
<div class="text-muted">Select which administrators should receive alerts whenever a print request changes.</div>
</div>
<a href="<?= site_url('administrator/notifications_alerts') ?>" class="btn btn-outline-secondary btn-sm">
General Notification Subjects
</a>
</div>
<?= view('partials/flash_messages') ?>
<?php if (empty($tableReady)): ?>
<div class="alert alert-warning" role="alert">
Notification subject storage is not available. Please run migrations to enable this feature.
</div>
<?php endif; ?>
<form method="post" action="<?= site_url('administrator/print-notifications/save') ?>">
<?= csrf_field() ?>
<div class="card">
<div class="card-header d-flex align-items-center justify-content-between">
<span>Administrators</span>
<button type="submit" class="btn btn-primary btn-sm" <?= empty($tableReady) ? 'disabled' : '' ?>>Save</button>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-striped align-middle mb-0">
<thead class="table-dark">
<tr>
<th style="width: 60px;">#</th>
<th>Name</th>
<th>Email</th>
<th class="text-center">Send Print Alerts</th>
</tr>
</thead>
<tbody>
<?php if (empty($admins)): ?>
<tr>
<td colspan="4" class="text-center text-muted py-4">No administrators found.</td>
</tr>
<?php else: ?>
<?php foreach ($admins as $index => $admin): ?>
<?php
$adminId = (int) ($admin['id'] ?? 0);
$fullName = trim(($admin['firstname'] ?? '') . ' ' . ($admin['lastname'] ?? ''));
?>
<tr>
<td><?= $index + 1 ?></td>
<td><?= esc($fullName !== '' ? $fullName : ('Admin #' . $adminId)) ?></td>
<td><?= esc($admin['email'] ?? '') ?></td>
<td class="text-center">
<div class="form-check form-switch">
<input
class="form-check-input"
type="checkbox"
name="notify[<?= $adminId ?>]"
value="1"
<?= !empty($assigned[$adminId]) ? 'checked' : '' ?>
<?= empty($tableReady) ? 'disabled' : '' ?>
>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<div class="card-footer text-end">
<button type="submit" class="btn btn-primary" <?= empty($tableReady) ? 'disabled' : '' ?>>Save Changes</button>
</div>
</div>
</form>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,134 @@
<?php
$activeStudents = $active_students ?? [];
$removedStudents = $removed_students ?? [];
$schoolYear = $school_year ?? '';
?>
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content">
<h2 class="text-center mt-4 mb-2">Student Removal</h2>
<p class="text-center text-muted mb-4">
Removed students keep their data but do not appear in classes or attendance.
<?= $schoolYear !== '' ? 'Current year: ' . esc($schoolYear) : '' ?>
</p>
<?= $this->include('partials/flash_messages') ?>
<div class="card mb-4 shadow-sm">
<div class="card-header d-flex align-items-center justify-content-between">
<span>Active Students</span>
<span class="badge bg-primary"><?= count($activeStudents) ?></span>
</div>
<div class="card-body">
<div class="table-responsive">
<table id="activeStudentsTable" class="table table-striped table-hover align-middle">
<thead class="table-dark">
<tr>
<th>School ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Class</th>
<th style="min-width: 140px;">Action</th>
</tr>
</thead>
<tbody>
<?php if (!empty($activeStudents)): ?>
<?php foreach ($activeStudents as $student): ?>
<tr>
<td><?= esc($student['school_id'] ?? '') ?></td>
<td><?= esc($student['firstname'] ?? '') ?></td>
<td><?= esc($student['lastname'] ?? '') ?></td>
<td><?= esc($student['class_section_name'] ?? 'No class assigned') ?></td>
<td>
<form method="post" action="<?= site_url('administrator/students/set-active') ?>" class="d-inline">
<?= csrf_field() ?>
<input type="hidden" name="student_id" value="<?= (int)($student['id'] ?? 0) ?>">
<input type="hidden" name="is_active" value="0">
<button type="submit" class="btn btn-outline-danger btn-sm" onclick="return confirm('Remove this student from classes and attendance?');">
Remove
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5" class="text-center">No active students found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<div class="card shadow-sm">
<div class="card-header d-flex align-items-center justify-content-between">
<span>Removed Students</span>
<span class="badge bg-secondary"><?= count($removedStudents) ?></span>
</div>
<div class="card-body">
<div class="table-responsive">
<table id="removedStudentsTable" class="table table-striped table-hover align-middle">
<thead class="table-dark">
<tr>
<th>School ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Last Class</th>
<th style="min-width: 140px;">Action</th>
</tr>
</thead>
<tbody>
<?php if (!empty($removedStudents)): ?>
<?php foreach ($removedStudents as $student): ?>
<tr>
<td><?= esc($student['school_id'] ?? '') ?></td>
<td><?= esc($student['firstname'] ?? '') ?></td>
<td><?= esc($student['lastname'] ?? '') ?></td>
<td><?= esc($student['class_section_name'] ?? 'No class assigned') ?></td>
<td>
<form method="post" action="<?= site_url('administrator/students/set-active') ?>" class="d-inline">
<?= csrf_field() ?>
<input type="hidden" name="student_id" value="<?= (int)($student['id'] ?? 0) ?>">
<input type="hidden" name="is_active" value="1">
<button type="submit" class="btn btn-outline-success btn-sm" onclick="return confirm('Restore this student to classes and attendance?');">
Restore
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="5" class="text-center">No removed students found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
if (window.jQuery && typeof jQuery.fn.DataTable === 'function') {
if (document.getElementById('activeStudentsTable')) {
jQuery('#activeStudentsTable').DataTable();
}
if (document.getElementById('removedStudentsTable')) {
jQuery('#removedStudentsTable').DataTable();
}
}
});
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,41 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<h1>Scholarship Information</h1>
<table class="table table-striped">
<thead>
<tr>
<th>Scholarship ID</th>
<th>Name</th>
<th>Amount</th>
<th>Eligibility Criteria</th>
<th>Application Deadline</th>
</tr>
</thead>
<tbody>
<?php
// Example data - in a real scenario, this will be fetched from the database
$scholarships = [
['scholarship_id' => 'S001', 'name' => 'Academic Excellence', 'amount' => '$1000', 'criteria' => 'Top 10% of class', 'deadline' => '2024-09-01'],
['scholarship_id' => 'S002', 'name' => 'Sports Scholarship', 'amount' => '$800', 'criteria' => 'Outstanding performance in sports', 'deadline' => '2024-10-01'],
['scholarship_id' => 'S003', 'name' => 'Need-Based Scholarship', 'amount' => '$1200', 'criteria' => 'Financial need', 'deadline' => '2024-08-15'],
// Add more records as needed
];
foreach ($scholarships as $scholarship) {
echo "<tr>
<td>{$scholarship['scholarship_id']}</td>
<td>{$scholarship['name']}</td>
<td>{$scholarship['amount']}</td>
<td>{$scholarship['criteria']}</td>
<td>{$scholarship['deadline']}</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
<?= $this->endSection() ?>
+163
View File
@@ -0,0 +1,163 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content"></div>
<br>
<main>
<section>
<form action="<?= site_url('administrator/userSearch') ?>" method="get" class="form-inline mb-4">
<div class="form-group">
<input type="text" name="query" class="form-control"
value="<?= htmlspecialchars($query, ENT_QUOTES, 'UTF-8'); ?>"
placeholder="Enter search term" required>
<button type="submit" class="btn btn-primary ms-2">Search</button>
</div>
</form>
<h2>Search Results for "<?= htmlspecialchars($query, ENT_QUOTES, 'UTF-8'); ?>"</h2>
<?php if (empty($results) || array_filter($results, fn($group) => empty($group)) === $results): ?>
<p>No results found.</p>
<?php else: ?>
<!-- Users Table -->
<?php if (!empty($results['users'])): ?>
<h4>Users</h4>
<table class="table table-bordered table-sm">
<thead>
<tr>
<th>School ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<?php foreach ($results['users'] as $user): ?>
<tr>
<td><?= esc($user['school_id'] ?? 'N/A') ?></td>
<td><?= esc($user['firstname'] ?? 'N/A') ?></td>
<td><?= esc($user['lastname'] ?? 'N/A') ?></td>
<td><?= esc($user['email'] ?? 'N/A') ?></td>
<td><?= esc($user['cellphone'] ?? 'N/A') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<!-- Students Table -->
<?php if (!empty($results['students'])): ?>
<h4>Students</h4>
<table class="table table-bordered table-sm">
<thead>
<tr>
<th>School ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Date of Birth</th>
</tr>
</thead>
<tbody>
<?php foreach ($results['students'] as $student): ?>
<tr>
<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['dob'] ?? 'N/A') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<!-- Parents Table -->
<?php if (!empty($results['parents'])): ?>
<h4>Parents</h4>
<table class="table table-bordered table-sm">
<thead>
<tr>
<th>First Parent ID</th>
<th>Second Parent</th>
<th>Email</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<?php foreach ($results['parents'] as $parent): ?>
<tr>
<td><?= esc($parent['firstparent_id'] ?? 'N/A') ?></td>
<td><?= esc(trim(($parent['secondparent_firstname'] ?? '') . ' ' . ($parent['secondparent_lastname'] ?? ''))) ?></td>
<td><?= esc($parent['secondparent_email'] ?? 'N/A') ?></td>
<td><?= esc($parent['secondparent_phone'] ?? 'N/A') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<!-- Staff Table -->
<?php if (!empty($results['staff'])): ?>
<h4>Staff</h4>
<table class="table table-bordered table-sm">
<thead>
<tr>
<th>Role</th>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($results['staff'] as $staff): ?>
<tr>
<td><?= esc($staff['role_name'] ?? 'N/A') ?></td>
<td><?= esc(trim(($staff['firstname'] ?? '') . ' ' . ($staff['lastname'] ?? ''))) ?></td>
<td><?= esc($staff['email'] ?? 'N/A') ?></td>
<td><?= esc($staff['phone'] ?? 'N/A') ?></td>
<td><?= esc($staff['active_role'] ?? 'N/A') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<!-- Emergency Contacts Table -->
<?php if (!empty($results['emergency_contacts'])): ?>
<h4>Emergency Contacts</h4>
<table class="table table-bordered table-sm">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Relation</th>
<th>Phone</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<?php foreach ($results['emergency_contacts'] as $contact): ?>
<tr>
<td><?= esc($contact['id'] ?? 'N/A') ?></td>
<td><?= esc($contact['emergency_contact_name'] ?? 'N/A') ?></td>
<td><?= esc($contact['relation'] ?? 'N/A') ?></td>
<td><?= esc($contact['cellphone'] ?? 'N/A') ?></td>
<td><?= esc($contact['email'] ?? 'N/A') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php endif; ?>
</section>
</main>
</div>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,239 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content">
<h2 class="text-center mt-4 mb-3">Auto-Distribute Students into Sections</h2>
<div class="row g-2 align-items-center justify-content-center mb-3">
<div class="col-auto"><label for="adYearSelect" class="col-form-label">School year</label></div>
<div class="col-auto">
<form method="get" action="<?= site_url('administrator/sections/auto-distribute') ?>" class="d-flex align-items-center gap-2">
<select id="adYearSelect" name="schoolYear" class="form-select form-select-sm" style="min-width: 180px;">
<?php
$years = isset($schoolYears) && is_array($schoolYears) ? $schoolYears : [];
if (empty($years) && !empty($selectedYear)) $years = [$selectedYear];
foreach ($years as $y): $val = is_array($y) && isset($y['school_year']) ? $y['school_year'] : (string)$y; ?>
<option value="<?= esc($val) ?>" <?= ((string)($selectedYear ?? '') === (string)$val) ? 'selected' : '' ?>>
<?= esc($val) ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-secondary btn-sm">Apply</button>
</form>
</div>
</div>
<div class="card">
<div class="card-body">
<div class="row g-3 align-items-end mb-2">
<div class="col-md-3">
<label for="globalPer" class="form-label">Students per section</label>
<input type="number" min="1" id="globalPer" class="form-control" placeholder="e.g. 20" />
</div>
<div class="col-md-4 d-flex gap-2">
<button type="button" id="refreshTotalsBtn" class="btn btn-outline-secondary">Refresh Totals</button>
<button type="button" id="generateAllBtn" class="btn btn-primary">Generate All</button>
</div>
<div class="col-md-7 text-end">
<span id="pageMsg" class="small text-muted"></span>
</div>
</div>
<div class="table-responsive">
<table class="table table-sm table-striped align-middle no-mgmt-sticky" id="classesTable">
<thead class="table-light">
<tr id="tblHeader">
<th>Class</th>
<th>Total</th>
<th>Sections Needed</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="tblBody"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<input type="hidden" id="csrfName" value="<?= esc(csrf_token()) ?>" />
<input type="hidden" id="csrfValue" value="<?= esc(csrf_hash()) ?>" />
<script>
(function(){
const selectedYear = '<?= esc($selectedYear ?? '') ?>';
const totalsUrl = '<?= site_url('administrator/sections/promotion-totals') ?>?school_year=' + encodeURIComponent(selectedYear);
const distUrl = '<?= site_url('administrator/sections/auto-distribute') ?>';
const tblBody = document.getElementById('tblBody');
const tblHeader = document.getElementById('tblHeader');
const perInput = document.getElementById('globalPer');
const msgEl = document.getElementById('pageMsg');
const refreshBtn= document.getElementById('refreshTotalsBtn');
let headerSections = []; // list of generated section names as columns
let rowIndexByClassId = {}; // mapping to locate rows
function calcNeeded(total) {
const per = parseInt(perInput.value || '0', 10);
if (!per || per <= 0) return '';
return Math.ceil(total / per);
}
function ensureSectionColumns(sectionNames) {
// Add any new section name as a column if not already present
sectionNames.forEach(function(name){
if (!name) return;
if (headerSections.indexOf(name) >= 0) return;
headerSections.push(name);
const th = document.createElement('th');
th.textContent = name;
tblHeader.appendChild(th);
// Also add empty cell to each existing row
document.querySelectorAll('#tblBody tr').forEach(function(tr){
const td = document.createElement('td');
td.dataset.section = name;
td.textContent = '';
tr.appendChild(td);
});
});
}
function buildInitialTable(rows) {
tblBody.innerHTML = '';
rowIndexByClassId = {};
headerSections = [];
// Remove dynamically-added section cols, keep the first four fixed
while (tblHeader.children.length > 4) tblHeader.removeChild(tblHeader.lastElementChild);
rows.forEach(function(r){
const tr = document.createElement('tr');
tr.dataset.classId = r.class_id;
tr.dataset.classSectionId = r.class_section_id;
tr.dataset.className = r.class_section_name || '';
const tdName = document.createElement('td');
tdName.textContent = r.class_section_name || '';
tr.appendChild(tdName);
const tdTotal = document.createElement('td');
tdTotal.className = 'text-end';
tdTotal.textContent = r.total;
tr.appendChild(tdTotal);
const tdNeed = document.createElement('td');
tdNeed.className = 'text-end need-cell';
tdNeed.textContent = calcNeeded(r.total);
tr.appendChild(tdNeed);
const tdAct = document.createElement('td');
const btn = document.createElement('button');
btn.className = 'btn btn-sm btn-primary';
btn.textContent = 'Generate Sections';
btn.addEventListener('click', function(){ runDistribution(r.class_section_id, r.class_section_name); });
tdAct.appendChild(btn);
tr.appendChild(tdAct);
tblBody.appendChild(tr);
rowIndexByClassId[r.class_id] = tr;
});
}
function updateNeeds() {
document.querySelectorAll('#tblBody tr').forEach(function(tr){
const total = parseInt(tr.children[1].textContent || '0', 10);
const needCell = tr.querySelector('.need-cell');
if (needCell) needCell.textContent = calcNeeded(total);
});
}
function runDistribution(baseSectionId, baseName) {
const per = parseInt(perInput.value || '0', 10);
if (!per || per <= 0) { msgEl.textContent = 'Enter students per section first.'; return; }
msgEl.textContent = 'Distributing ' + (baseName || '') + '...';
const fd = new FormData();
fd.append('class_section_id', String(baseSectionId));
fd.append('students_per_section', String(per));
fd.append('school_year', selectedYear);
// CSRF
const csrfNameEl = document.getElementById('csrfName');
const csrfValueEl= document.getElementById('csrfValue');
if (csrfNameEl && csrfValueEl) {
fd.append(csrfNameEl.value, csrfValueEl.value);
}
return fetch(distUrl, { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' }, body: fd })
.then(r => r.json())
.then(res => {
// refresh CSRF if provided
if (res && res.csrfTokenName && res.csrfHash) {
if (csrfNameEl) csrfNameEl.value = res.csrfTokenName;
if (csrfValueEl) csrfValueEl.value = res.csrfHash;
}
if (!res || !res.ok || !Array.isArray(res.sections)) {
msgEl.textContent = res && res.message ? res.message : 'Distribution finished with no sections.';
return;
}
msgEl.textContent = res && res.message ? res.message : 'Completed.';
// Collect section names, then ensure header columns
const names = res.sections.map(s => s.class_section_name).filter(Boolean);
ensureSectionColumns(names);
// Fill row cells for this class
const tr = Array.from(document.querySelectorAll('#tblBody tr')).find(function(_tr){
return (_tr.children[0].textContent || '') === (baseName || '');
});
if (!tr) return;
res.sections.forEach(function(s){
const td = tr.querySelector('td[data-section="'+ s.class_section_name +'"]');
if (td) td.textContent = (s.total ?? 0) + ' (M'+ (s.male ?? 0) + '/F'+ (s.female ?? 0) +')';
});
})
.catch(() => { msgEl.textContent = 'Failed to distribute. Please try again.'; });
}
function loadTotals() {
msgEl.textContent = 'Loading totals...';
fetch(totalsUrl, { headers: { 'X-Requested-With': 'XMLHttpRequest' }})
.then(r => r.json())
.then(res => {
if (!res || !res.ok) { msgEl.textContent = res && res.message ? res.message : 'Failed to load totals.'; return; }
buildInitialTable(res.rows || []);
updateNeeds();
msgEl.textContent = '';
})
.catch(() => { msgEl.textContent = 'Failed to load totals.'; });
}
refreshBtn.addEventListener('click', function(){ updateNeeds(); });
document.getElementById('generateAllBtn').addEventListener('click', async function(){
const per = parseInt(perInput.value || '0', 10);
if (!per || per <= 0) { msgEl.textContent = 'Enter students per section first.'; return; }
// Collect base sections from rows
const rows = Array.from(document.querySelectorAll('#tblBody tr'))
.map(tr => ({ id: tr.dataset.classSectionId, name: tr.dataset.className }))
.filter(r => r.id);
for (let i = 0; i < rows.length; i++) {
const r = rows[i];
msgEl.textContent = 'Distributing ' + (r.name || r.id) + ' (' + (i+1) + '/' + rows.length + ')...';
// eslint-disable-next-line no-await-in-loop
await runDistribution(r.id, r.name);
}
msgEl.textContent = 'All distributions completed.';
});
perInput.addEventListener('input', function(){ updateNeeds(); });
loadTotals();
})();
</script>
<?= $this->endSection() ?>
+30
View File
@@ -0,0 +1,30 @@
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Settings</h1>
</div>
<form action="/administrator/settings/update" method="post">
<div class="form-group">
<label for="site_name">Site Name</label>
<input type="text" class="form-control" id="site_name" name="site_name"
value="<?php echo $settings['site_name']; ?>" required>
</div>
<div class="form-group">
<label for="administrator_email">administrator Email</label>
<input type="email" class="form-control" id="administrator_email" name="administrator_email"
value="<?php echo $settings['administrator_email']; ?>" required>
</div>
<div class="form-group">
<label for="timezone">Timezone</label>
<select class="form-control" id="timezone" name="timezone" required>
<?php foreach ($timezones as $timezone): ?>
<option value="<?php echo $timezone; ?>"
<?php echo $timezone == $settings['timezone'] ? 'selected' : ''; ?>>
<?php echo $timezone; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-primary">Save Settings</button>
</form>
</main>
@@ -0,0 +1,643 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content">
<h2 class="text-center mt-4 mb-3">Student Class Assignment</h2>
<div class="row g-2 align-items-center justify-content-center mb-3">
<div class="col-auto"><label for="scaYearSelect" class="col-form-label">School year</label></div>
<div class="col-auto">
<form method="get" action="<?= site_url('administrator/student_class_assignment') ?>" class="d-flex align-items-center gap-2">
<select id="scaYearSelect" name="schoolYear" class="form-select form-select-sm" style="min-width: 180px;">
<?php
$years = isset($schoolYears) && is_array($schoolYears) ? $schoolYears : [];
if (empty($years) && !empty($selectedYear)) $years = [$selectedYear];
foreach ($years as $y): $val = is_array($y) && isset($y['school_year']) ? $y['school_year'] : (string)$y; ?>
<option value="<?= esc($val) ?>" <?= ((string)($selectedYear ?? '') === (string)$val) ? 'selected' : '' ?>>
<?= esc($val) ?>
</option>
<?php endforeach; ?>
</select>
<?php $semVal = (string)($semester ?? ($_GET['semester'] ?? '')); ?>
<label for="scaSemesterSelect" class="col-form-label">Semester</label>
<select id="scaSemesterSelect" name="semester" class="form-select form-select-sm" style="min-width: 140px;">
<option value="">—</option>
<option value="Fall" <?= (strcasecmp($semVal,'Fall')===0?'selected':'') ?>>Fall</option>
<option value="Spring" <?= (strcasecmp($semVal,'Spring')===0?'selected':'') ?>>Spring</option>
</select>
<button type="submit" class="btn btn-secondary btn-sm">Apply</button>
</form>
</div>
<?php if (isset($isCurrentYear) && !$isCurrentYear): ?>
<div class="col-auto"><span class="badge bg-secondary">Read-only (Past Year)</span></div>
<?php endif; ?>
</div>
<div class="table-responsive">
<!-- Auto-distribute panel -->
<div class="card mb-3">
<div class="card-body">
<form id="autoDistForm" action="<?= site_url('administrator/sections/auto-distribute') ?>" method="post" class="row g-2 align-items-end">
<?= csrf_field() ?>
<div class="col-md-4">
<label for="autoDistSection" class="form-label">Class (base section)</label>
<select id="autoDistSection" name="class_section_id" class="form-select" required>
<option value="">Select class…</option>
<?php if (!empty($classes)): ?>
<?php foreach ($classes as $c): ?>
<?php $nm = (string)($c['class_section_name'] ?? '');
$isBase = (strpos($nm, '-') === false);
if (!$isBase) continue; ?>
<option value="<?= (int)($c['class_section_id'] ?? 0) ?>">
<?= esc($nm) ?>
</option>
<?php endforeach; ?>
<?php endif; ?>
</select>
</div>
<div class="col-md-3">
<label for="autoDistPer" class="form-label">Students per section</label>
<input type="number" min="1" id="autoDistPer" name="students_per_section" class="form-control" required />
</div>
<div class="col-md-3">
<label for="autoDistYear" class="form-label">School year</label>
<input type="text" id="autoDistYear" name="school_year" value="<?= esc($selectedYear ?? '') ?>" class="form-control" />
</div>
<div class="col-md-2 d-grid">
<button type="submit" class="btn btn-outline-primary">Auto-Distribute</button>
</div>
<div class="col-12">
<div id="autoDistMsg" class="small text-muted"></div>
</div>
</form>
</div>
</div>
<table id="studentsTable" class="display table table-striped table-hover align-middle" style="width:100%">
<thead class="table-dark">
<tr>
<th>Student Name</th>
<th>Age</th>
<th>Registration Grade</th>
<th>New Student</th>
<th>Current Assigned Grade</th>
<th>Registration Date</th>
<th>School Year</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (!empty($students)): ?>
<?php foreach ($students as $student): ?>
<tr data-student-id="<?= (int)($student['student_id'] ?? 0) ?>"
data-student-name="<?= esc($student['name'] ?? '') ?>"
data-class-ids='<?= esc(json_encode($student['class_section_ids'] ?? [])) ?>'
data-class-names='<?= esc(json_encode($student['class_section_names'] ?? [])) ?>'>
<td>
<a href="#" class="text-decoration-none" data-family-student-id="<?= (int)($student['student_id'] ?? 0) ?>">
<?= esc($student['name']) ?>
</a>
</td>
<td><?= esc($student['age']) ?></td>
<td><?= esc($student['registration_grade']) ?></td>
<td><?= esc($student['new_student']) ?></td>
<?php
$sIds = $student['class_section_ids'] ?? [];
$sNames = $student['class_section_names'] ?? [];
$sidVal = (int)($student['student_id'] ?? 0);
$badgeHtml = '';
if (!empty($sIds) && is_array($sIds)) {
foreach ($sIds as $idx => $cid) {
$nm = $sNames[$idx] ?? ('Class #'.$cid);
$badgeHtml .= '<span class="badge bg-success me-1 mb-1 d-inline-flex align-items-center gap-1">';
$badgeHtml .= '<span>'.esc($nm).'</span>';
$badgeHtml .= '<button type="button" class="btn-close btn-close-white btn-close-sm remove-class-chip" data-student-id="'.$sidVal.'" data-class-id="'.(int)$cid.'" aria-label="Remove"></button>';
$badgeHtml .= '</span>';
}
} else {
$badgeHtml = '<span class="text-muted">No class assigned</span>';
}
?>
<td class="assigned-classes" data-class-ids='<?= esc(json_encode($sIds)) ?>' data-class-names='<?= esc(json_encode($sNames)) ?>'>
<?= $badgeHtml ?>
</td>
<td data-order="<?= esc($student['registration_date']) ?>">
<?php if (!empty($student['registration_date'])): ?>
<?= local_date($student['registration_date'], 'm-d-Y') ?>
<?php else: ?>
-
<?php endif; ?>
</td>
<td><?= esc($student['school_year']) ?></td>
<td>
<button
type="button"
class="btn btn-primary btn-sm assign-student-btn" <?= (isset($isCurrentYear) && !$isCurrentYear) ? 'disabled title="Editing disabled for non-current year"' : '' ?>
data-bs-toggle="modal"
data-bs-target="#assignStudentClassModal"
data-student-id="<?= esc($student['student_id']) ?>"
data-student-name="<?= esc($student['name']) ?>"
data-current-classes="<?= esc($student['class_section_name'] ?? '') ?>"
data-class-ids="<?= esc(json_encode($student['class_section_ids'] ?? [])) ?>"
data-class-names="<?= esc(json_encode($student['class_section_names'] ?? [])) ?>">
Assign Class
</button>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Single Modal (do NOT include another modal partial) -->
<div class="modal fade" id="assignStudentClassModal" tabindex="-1" aria-labelledby="assignStudentClassModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="assignStudentClassModalLabel">Assign Class to Student</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="assignError" class="alert alert-danger d-none"></div>
<div class="mb-2 small text-muted" id="currentAssignments"></div>
<form id="assignStudentClassForm" action="<?= base_url('assign_class_student') ?>" method="post">
<?= csrf_field() ?>
<input type="hidden" name="student_id" id="studentId">
<div class="mb-3">
<label for="studentName" class="form-label">Student Name</label>
<input type="text" class="form-control" id="studentName" readonly>
</div>
<div class="mb-3">
<label for="student_class_section_id" class="form-label">Select Class(es)</label>
<select name="class_section_id[]" id="student_class_section_id" class="form-control" multiple required size="8">
<option value="" disabled>Select one or more classes</option>
<?php if (!empty($classes) && is_array($classes)): ?>
<?php foreach ($classes as $class_): ?>
<option value="<?= htmlspecialchars($class_['class_section_id']) ?>">
<?= htmlspecialchars($class_['class_section_name']) ?>
</option>
<?php endforeach; ?>
<?php else: ?>
<option value="" disabled>No classes available</option>
<?php endif; ?>
</select>
<div class="form-text">Use Ctrl/Cmd + click to select multiple classes. Existing assignments are kept.</div>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" value="1" id="is_event_only" name="is_event_only">
<label class="form-check-label" for="is_event_only">
Event class only (no tuition)
</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="assignStudentClassButton">Assign Class</button>
</div>
</div>
</div>
</div>
</div>
<!-- Remove Modal -->
<div class="modal fade" id="removeStudentClassModal" tabindex="-1" aria-labelledby="removeStudentClassModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="removeStudentClassModalLabel">Remove Class from Student</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="removeError" class="alert alert-danger d-none"></div>
<form id="removeStudentClassForm" action="<?= site_url('remove_class_student') ?>" method="post">
<?= csrf_field() ?>
<input type="hidden" name="student_id" id="removeStudentId">
<div class="mb-3">
<label for="removeStudentName" class="form-label">Student Name</label>
<input type="text" class="form-control" id="removeStudentName" readonly>
</div>
<div class="mb-3">
<label for="remove_class_section_id" class="form-label">Select Class to Remove</label>
<select name="class_section_id" id="remove_class_section_id" class="form-control" required>
<option value="" disabled selected>Select a class</option>
</select>
<div class="form-text">Only currently assigned classes are shown.</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-danger" id="removeStudentClassButton">Remove Class</button>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
// Helpers to render assigned class chips
let CSRF_NAME = '<?= csrf_token() ?>';
let CSRF_HASH = '<?= csrf_hash() ?>';
function applyCsrfToForms(name, hash) {
if (!name || !hash) return;
$('form').each(function(){
const $f = $(this);
let $fld = $f.find('input[name="'+name+'"]');
if (!$fld.length) {
$f.find('input[type="hidden"]').filter(function(){ return this.name && this.name.startsWith('csrf_'); }).remove();
$fld = $('<input>', {type:'hidden', name});
$f.prepend($fld);
}
$fld.val(hash);
});
}
function updateCsrf($form, res){
if (!$form || !res) return;
let name = res.csrfTokenName || null;
let hash = res.csrfHash || null;
if (!hash) {
$form.find('input[type="hidden"]').each(function(){
const nm = this.name;
if (nm && res[nm]) { name = nm; hash = res[nm]; return false; }
});
}
if (name && hash) {
CSRF_NAME = name;
CSRF_HASH = hash;
applyCsrfToForms(CSRF_NAME, CSRF_HASH);
}
}
function findColIndexByHeader(text){
let idx = -1;
$('#studentsTable thead th').each(function(i){
if ($(this).text().trim() === text) idx = i;
});
return idx >= 0 ? idx : 4; // fallback
}
const CLASS_COL_INDEX = findColIndexByHeader('Current Assigned Grade');
function renderBadgesHtml(ids, names, studentId){
if (!Array.isArray(ids) || ids.length === 0) return '<span class="text-muted">No class assigned</span>';
if (!Array.isArray(names)) names = [];
return ids.map((id, idx) => {
const nm = names[idx] || ('Class #' + id);
return `<span class="badge bg-success me-1 mb-1 d-inline-flex align-items-center gap-1">
<span>${nm}</span>
<button type="button" class="btn-close btn-close-white btn-close-sm remove-class-chip" data-student-id="${studentId}" data-class-id="${id}" aria-label="Remove"></button>
</span>`;
}).join('');
}
function renderAssignedBadges() {
const decodeAttr = (raw) => {
if (!raw) return '';
return raw.replace(/&quot;/g, '"').replace(/&#39;/g, "'").trim();
};
$('#studentsTable tbody tr').each(function(){
const $row = $(this);
const $cell = $row.find('td').eq(CLASS_COL_INDEX);
if (!$cell.length) return;
if (!$cell.attr('data-class-ids') && $row.attr('data-class-ids')) {
$cell.attr('data-class-ids', $row.attr('data-class-ids'));
}
if (!$cell.attr('data-class-names') && $row.attr('data-class-names')) {
$cell.attr('data-class-names', $row.attr('data-class-names'));
}
const idsRaw = decodeAttr($cell.attr('data-class-ids') || $row.attr('data-class-ids') || '[]');
const namesRaw = decodeAttr($cell.attr('data-class-names') || $row.attr('data-class-names') || '[]');
let ids = [], names = [];
try { ids = JSON.parse(idsRaw) || []; } catch(_) {}
try { names = JSON.parse(namesRaw) || []; } catch(_) {}
const studentId = parseInt($row.attr('data-student-id') || '0', 10);
const badgeHtml = renderBadgesHtml(ids, names, studentId);
$cell.html(badgeHtml || $cell.html());
});
}
window.renderAssignedBadges = renderAssignedBadges;
window.renderBadgesHtml = renderBadgesHtml;
// Keep table row + button data in sync after add/remove so modals stay up-to-date without refresh.
function syncAssignButtonData($row, ids, names, displayVal){
const idsJson = JSON.stringify(ids || []);
const namesJson = JSON.stringify(names || []);
const $cell = $row.find('td').eq(CLASS_COL_INDEX);
$row.attr('data-class-ids', idsJson).attr('data-class-names', namesJson);
$cell.attr('data-class-ids', idsJson).attr('data-class-names', namesJson);
$row.find('.assign-student-btn')
.attr('data-class-ids', idsJson)
.attr('data-class-names', namesJson)
.attr('data-current-classes', displayVal || '')
.data('class-ids', ids || [])
.data('class-names', names || [])
.data('current-classes', displayVal || '');
}
window.syncAssignButtonData = syncAssignButtonData;
function getAssignmentsFromRow($row){
const res = { ids: [], names: [] };
const $cell = $row.find('td').eq(CLASS_COL_INDEX);
const $chips = $cell.find('.remove-class-chip');
if ($chips.length) {
$chips.each(function(){
const cid = parseInt($(this).data('class-id') || 0, 10);
if (cid) res.ids.push(cid);
const label = $(this).closest('.badge').clone().children('button').remove().end().text().trim();
res.names.push(label || ('Class #' + cid));
});
return res;
}
const decodeAttr = (raw) => {
if (!raw) return '';
return raw.replace(/&quot;/g, '"').replace(/&#39;/g, "'").trim();
};
try { res.ids = JSON.parse(decodeAttr($cell.attr('data-class-ids') || $row.attr('data-class-ids') || '[]')) || []; } catch(_) {}
try { res.names = JSON.parse(decodeAttr($cell.attr('data-class-names') || $row.attr('data-class-names') || '[]')) || []; } catch(_) {}
return res;
}
// Track the rows used by modals across handlers
let rowNode = null;
let removeRowNode = null;
(function () {
if (typeof $ === 'undefined' || !$.fn.DataTable) return;
window.studentsTable = $.fn.DataTable.isDataTable('#studentsTable')
? $('#studentsTable').DataTable()
: $('#studentsTable').DataTable({
responsive: true,
pageLength: 100,
lengthMenu: [5, 10, 25, 50, 100],
order: [[0, 'asc']],
stateSave: true,
columnDefs: [{ orderable: false, targets: [7] }],
language: { emptyTable: 'No students found.' }
});
window.studentsTable.on('draw', function(){ renderAssignedBadges(); });
window.getStudentsTable = function(){
return window.studentsTable || ($.fn.DataTable.isDataTable('#studentsTable') ? $('#studentsTable').DataTable() : null);
};
window.table = window.studentsTable; // legacy reference for existing handlers
// Open modal and set values
$(document).on('click', '.assign-student-btn', function(){
const $row = $(this).closest('tr');
rowNode = $row.get(0);
$('#studentId').val($(this).data('student-id'));
$('#studentName').val($(this).data('student-name'));
// Pre-select currently assigned classes using live badges, fallback to data attrs.
const fromRow = getAssignmentsFromRow($row);
const ids = Array.isArray(fromRow.ids) ? fromRow.ids : [];
const names = Array.isArray(fromRow.names) ? fromRow.names : [];
$('#student_class_section_id').val(ids.map(String)).trigger('change');
let current = $(this).attr('data-current-classes') || $(this).data('current-classes') || '';
if (!current && names.length) current = names.join(', ');
$('#currentAssignments').text(current ? `Currently assigned: ${current}` : 'Currently assigned: none');
$('#assignError').addClass('d-none').text('');
});
// Open remove modal and populate options
$(document).on('click', '.remove-class-btn', function(){
removeRowNode = $(this).closest('tr').get(0);
$('#removeStudentId').val($(this).data('student-id'));
$('#removeStudentName').val($(this).data('student-name'));
$('#removeError').addClass('d-none').text('');
const select = $('#remove_class_section_id');
select.empty();
select.append('<option value="" disabled selected>Select a class</option>');
try {
const ids = JSON.parse($(this).attr('data-class-ids') || '[]') || [];
const names = JSON.parse($(this).attr('data-class-names') || '[]') || [];
ids.forEach((id, idx) => {
const label = names[idx] || `Class #${id}`;
select.append(`<option value="${id}">${label}</option>`);
});
} catch (_) {
select.append('<option value="" disabled>Error loading classes</option>');
}
});
// Button click → trigger AJAX submit
$('#assignStudentClassButton').on('click', function(){
const form = document.getElementById('assignStudentClassForm');
if (form.checkValidity()) $('#assignStudentClassForm').trigger('submit');
else alert('Please select a class before submitting.');
});
// AJAX submit for assign
$(document).on('submit', '#assignStudentClassForm', function(e){
e.preventDefault();
const $form = $(this);
const url = $form.attr('action');
applyCsrfToForms(CSRF_NAME, CSRF_HASH);
const $btn = $('#assignStudentClassButton');
const orig = $btn.text();
$btn.prop('disabled', true).text('Assigning...');
const csrfField = $form.find('input[name="'+CSRF_NAME+'"]');
const csrfHeaderVal = csrfField.length ? csrfField.val() : CSRF_HASH;
$.ajax({
type: 'POST',
url: url,
data: $form.serialize(),
dataType: 'json',
headers: {
'X-Requested-With': 'XMLHttpRequest',
...(csrfHeaderVal ? {'X-CSRF-TOKEN': csrfHeaderVal} : {})
}
})
.done(function(res){
updateCsrf($form, res);
if (!res || !res.ok) {
$('#assignError').removeClass('d-none').text(res?.message || 'Unable to assign class.');
return;
}
// Update the table cell
if (rowNode) {
const tbl = window.getStudentsTable ? window.getStudentsTable() : null;
if (!tbl) return;
const displayVal = Array.isArray(res.class_section_names)
? res.class_section_names.join(', ')
: (res.class_section_name || '-');
const badgeHtml = renderBadgesHtml(res.class_section_ids || [], res.class_section_names || [], parseInt($(rowNode).attr('data-student-id') || '0', 10));
const rowIdx = tbl.row(rowNode).index();
tbl.cell(rowIdx, CLASS_COL_INDEX).data(badgeHtml);
const $row = $(tbl.row(rowIdx).node());
const $cell = $row.find('td').eq(CLASS_COL_INDEX);
$cell.html(badgeHtml);
syncAssignButtonData($row, res.class_section_ids || [], res.class_section_names || [], displayVal);
tbl.draw(false);
renderAssignedBadges();
$(rowNode).addClass('table-success');
setTimeout(()=>$(rowNode).removeClass('table-success'), 1200);
}
// Close modal
const modalEl = document.getElementById('assignStudentClassModal');
const modal = bootstrap.Modal.getInstance(modalEl);
if (modal) modal.hide();
})
.fail(function(xhr){
try {
const res = JSON.parse(xhr.responseText);
updateCsrf($form, res);
$('#assignError').removeClass('d-none').text(res?.message || 'Server error.');
} catch (_) {
$('#assignError').removeClass('d-none').text('Network or server error.');
}
})
.always(function(){
$btn.prop('disabled', false).text(orig);
});
});
})();
</script>
<script>
// Handle auto-distribute submit via AJAX
(function(){
const form = document.getElementById('autoDistForm');
if (!form) return;
form.addEventListener('submit', function(e){
e.preventDefault();
const fd = new FormData(form);
const msg = document.getElementById('autoDistMsg');
msg.textContent = 'Running distribution...';
fetch(form.action, { method: 'POST', body: fd, headers: { 'X-Requested-With':'XMLHttpRequest' } })
.then(r => r.json())
.then(res => {
msg.textContent = (res && res.message) ? res.message : 'Done.';
if (res && res.sections) {
console.log('Section summary', res.sections);
}
})
.catch(() => { msg.textContent = 'Failed to distribute.'; });
});
})();
// Handle remove class submit
(function(){
$(document).on('click', '#removeStudentClassButton', function(){
const form = document.getElementById('removeStudentClassForm');
if (form.checkValidity()) {
$('#removeStudentClassForm').trigger('submit');
} else {
alert('Please select a class to remove.');
}
});
$(document).on('submit', '#removeStudentClassForm', function(e){
e.preventDefault();
const $form = $(this);
const url = $form.attr('action');
const $btn = $('#removeStudentClassButton');
const orig = $btn.text();
$btn.prop('disabled', true).text('Removing...');
applyCsrfToForms(CSRF_NAME, CSRF_HASH);
const csrfField = $form.find('input[name="'+CSRF_NAME+'"]');
const csrfHeaderVal = csrfField.length ? csrfField.val() : CSRF_HASH;
$.ajax({
type: 'POST',
url: url,
data: $form.serialize(),
dataType: 'json',
headers: {
'X-Requested-With': 'XMLHttpRequest',
...(csrfHeaderVal ? {'X-CSRF-TOKEN': csrfHeaderVal} : {})
}
})
.done(function(res){
updateCsrf($form, res);
if (!res || !res.ok) {
$('#removeError').removeClass('d-none').text(res?.message || 'Unable to remove class.');
return;
}
// Update the table cell + data attributes
const tbl = window.getStudentsTable ? window.getStudentsTable() : null;
if (tbl) {
const targetRowNode = removeRowNode || $('#studentsTable tbody tr[data-student-id="'+(res.student_id || '')+'"]').get(0);
if (targetRowNode) {
const badgeHtml = renderBadgesHtml(res.remaining_ids || [], res.remaining_names || [], parseInt($(targetRowNode).attr('data-student-id') || '0', 10));
const rowIdx = tbl.row(targetRowNode).index();
tbl.cell(rowIdx, CLASS_COL_INDEX).data(badgeHtml);
const $row = $(tbl.row(rowIdx).node());
const $cell = $row.find('td').eq(CLASS_COL_INDEX);
$cell.html(badgeHtml); // ensure DOM shows latest without refresh
if (typeof syncAssignButtonData === 'function') {
syncAssignButtonData($row, res.remaining_ids || [], res.remaining_names || [], res.remaining_display || '');
}
tbl.draw(false); // redraw to keep ordering but stay on page
}
}
renderAssignedBadges();
const rowEl = removeRowNode || $('#studentsTable tbody tr[data-student-id="'+(res.student_id || '')+'"]').get(0);
if (rowEl) {
$(rowEl).addClass('table-warning');
setTimeout(()=>$(rowEl).removeClass('table-warning'), 1200);
}
const modalEl = document.getElementById('removeStudentClassModal');
const modal = bootstrap.Modal.getInstance(modalEl);
if (modal) modal.hide();
})
.fail(function(xhr){
try {
const res = JSON.parse(xhr.responseText);
updateCsrf($form, res);
$('#removeError').removeClass('d-none').text(res?.message || 'Server error.');
} catch (_) {
$('#removeError').removeClass('d-none').text('Network or server error.');
}
})
.always(function(){
$btn.prop('disabled', false).text(orig);
});
});
})();
// Inline chip removal (no modal)
$(document).on('click', '.remove-class-chip', function(e){
e.preventDefault();
const cid = parseInt($(this).data('class-id') || 0, 10);
const sid = parseInt($(this).data('student-id') || 0, 10);
if (!cid || !sid) return;
removeRowNode = $(this).closest('tr').get(0);
$('#removeStudentId').val(sid);
const label = $(this).closest('.badge').clone().children('button').remove().end().text().trim();
$('#remove_class_section_id').empty().append(`<option value="${cid}" selected>${label || ('Class #' + cid)}</option>`).val(cid);
$('#removeStudentClassForm').trigger('submit');
});
// Initial render of badges on page load
renderAssignedBadges();
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,670 @@
<?php
// Options (can be moved to config or controller)
$medicalOptions = $medicalOptions ?? [
"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"
];
$allergyOptions = $allergyOptions ?? [
"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"
];
$gradeOptions = $gradeOptions ?? ['KG', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', 'Youth'];
?>
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid">
<div class="wrapper">
<div class="content">
<h2 class="text-center mt-4 mb-3">Student Profiles</h2>
<div class="table-responsive">
<table id="myTable" class="table table-striped table-hover align-middle">
<thead class="table-dark">
<tr>
<th>School ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>DOB</th>
<th>Age</th>
<th>Gender</th>
<th>Assigned Grade</th>
<th>Active</th>
<th>Medical Conditions</th>
<th>Allergies</th>
<th>Photo Consent</th>
<th>Registration Date</th>
<th style="min-width: 220px;">Action</th>
</tr>
</thead>
<tbody>
<?php if (!empty($students)): ?>
<?php foreach ($students as $student): ?>
<?php
$modalIdContact = 'contactModal' . (int)$student['id'];
$modalIdEdit = 'editStudentModal' . (int)$student['id'];
$updateUrl = base_url('administrator/students/update'); // POST target
// Format DOB for display
$dobDisp = '';
if (!empty($student['dob'])) {
try {
$dobDisp = (new DateTime($student['dob']))->format('m-d-Y');
} catch (\Throwable $e) {
$dobDisp = esc($student['dob']);
}
}
// Format Reg Date for display (as local mm-dd-YYYY)
$regDisp = '';
if (!empty($student['registration_date'])) {
try {
$dt = new \DateTime($student['registration_date'], new \DateTimeZone('UTC'));
$dt->setTimezone(new \DateTimeZone(user_timezone()));
$regDisp = local_datetime($student['registration_date'], 'm-d-Y H:i');
} catch (\Throwable $e) {
$regDisp = esc($student['registration_date']);
}
}
// Input-safe values
$dobVal = $dobDisp;
$regDateLocal = '';
if (!empty($student['registration_date'])) {
try {
$dt2 = new \DateTime($student['registration_date'], new \DateTimeZone('UTC'));
$dt2->setTimezone(new \DateTimeZone(user_timezone()));
$regDateLocal = local_datetime($student['registration_date'], 'Y-m-d\TH:i');
} catch (\Throwable $e) {
$regDateLocal = '';
}
}
$isActive = (int)($student['is_active'] ?? 1);
// Preselected lists (server-computed strings or joined lists)
$medSelected = array_filter(array_map('trim', preg_split('/[,\n;]+/', (string)($student['medical_conditions'] ?? ''), -1, PREG_SPLIT_NO_EMPTY)));
$allSelected = array_filter(array_map('trim', preg_split('/[,\n;]+/', (string)($student['allergies'] ?? ''), -1, PREG_SPLIT_NO_EMPTY)));
$medOther = implode(', ', array_diff($medSelected, $medicalOptions));
$allOther = implode(', ', array_diff($allSelected, $allergyOptions));
$medId = 'medical_conditions_' . (int)$student['id'];
$allId = 'allergies_' . (int)$student['id'];
$medOtherId = 'medical_other_' . (int)$student['id'];
$allOtherId = 'allergy_other_' . (int)$student['id'];
?>
<tr>
<td><?= esc($student['school_id']) ?></td>
<td>
<a href="#" class="text-decoration-none" data-family-student-id="<?= (int)($student['id'] ?? 0) ?>">
<?= esc($student['firstname']) ?>
</a>
</td>
<td>
<a href="#" class="text-decoration-none" data-family-student-id="<?= (int)($student['id'] ?? 0) ?>">
<?= esc($student['lastname']) ?>
</a>
</td>
<td><?= esc($dobDisp) ?></td>
<td><?= esc((string)($student['age'] ?? '')) ?></td>
<td><?= esc($student['gender']) ?></td>
<td><?= esc($student['class_section_name']) ?></td>
<td><?= $isActive ? 'Yes' : 'No' ?></td>
<td><?= esc($student['medical_conditions'] ?? '') ?></td>
<td><?= esc($student['allergies'] ?? '') ?></td>
<td><?= !empty($student['photo_consent']) ? 'Yes' : 'No' ?></td>
<td><?= esc($regDisp) ?></td>
<td class="d-flex flex-wrap gap-2">
<!-- Contact Modal Trigger -->
<button class="btn btn-warning btn-sm" data-bs-toggle="modal" data-bs-target="#<?= $modalIdContact ?>">
Contact Information
</button>
<!-- Edit Modal Trigger -->
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#<?= $modalIdEdit ?>">
Edit Student
</button>
<!-- Contact Modal -->
<div class="modal fade" id="<?= $modalIdContact ?>" tabindex="-1" aria-labelledby="<?= $modalIdContact ?>Label" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content shadow">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title" id="<?= $modalIdContact ?>Label">
Contact Info - <?= esc($student['firstname']) ?> <?= esc($student['lastname']) ?>
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<h5 class="mb-3 border-bottom pb-1">👨‍👩‍👧 Parent Information</h5>
<div class="row mb-3">
<div class="col-md-6">
<p><strong>Name:</strong> <?= esc(($student['parent_firstname'] ?? '') . ' ' . ($student['parent_lastname'] ?? '')) ?></p>
<p><strong>Phone:</strong> <?= esc($student['parent_phone'] ?? 'N/A') ?></p>
</div>
<div class="col-md-6">
<p><strong>Email:</strong> <?= esc($student['parent_email'] ?? 'N/A') ?></p>
</div>
</div>
<h5 class="mb-3 border-bottom pb-1">🚨 Emergency Contact</h5>
<div class="row">
<div class="col-md-6">
<p><strong>Name:</strong> <?= esc($student['emergency_name'] ?? 'N/A') ?></p>
<p><strong>Relationship:</strong> <?= esc($student['emergency_relationship'] ?? 'N/A') ?></p>
<p><strong>Phone:</strong> <?= esc($student['emergency_phone'] ?? 'N/A') ?></p>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Edit Student Modal -->
<div class="modal fade" id="<?= $modalIdEdit ?>" tabindex="-1" aria-labelledby="<?= $modalIdEdit ?>Label" aria-hidden="true">
<div class="modal-dialog modal-xl">
<div class="modal-content shadow">
<div class="modal-header bg-success text-white">
<h5 class="modal-title" id="<?= $modalIdEdit ?>Label">
Edit Student - <?= esc($student['firstname']) ?> <?= esc($student['lastname']) ?>
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post" action="<?= $updateUrl ?>" class="needs-validation" novalidate>
<?= csrf_field() ?>
<input type="hidden" name="id" value="<?= (int)$student['id'] ?>">
<div class="modal-body">
<div class="row g-3">
<!-- school_id -->
<div class="col-md-3">
<label class="form-label">School ID</label>
<input type="text" name="school_id" class="form-control" value="<?= esc($student['school_id'] ?? '') ?>" maxlength="100">
</div>
<!-- firstname -->
<div class="col-md-3">
<label class="form-label">First Name</label>
<input type="text" name="firstname" class="form-control"
value="<?= esc($student['firstname'] ?? '') ?>"
pattern="^[A-Za-z\s\-]{2,30}$" required>
<div class="invalid-feedback">Only letters, spaces, or dashes (230 chars).</div>
</div>
<!-- lastname -->
<div class="col-md-3">
<label class="form-label">Last Name</label>
<input type="text" name="lastname" class="form-control"
value="<?= esc($student['lastname'] ?? '') ?>"
pattern="^[A-Za-z\s\-]{2,30}$" required>
<div class="invalid-feedback">Only letters, spaces, or dashes (230 chars).</div>
</div>
<!-- gender -->
<div class="col-md-3">
<label class="form-label">Gender</label>
<select name="gender" class="form-select" required>
<option value="" disabled <?= empty($student['gender']) ? 'selected' : '' ?>>Select…</option>
<option value="Male" <?= (strcasecmp($student['gender'] ?? '', 'Male') === 0) ? 'selected' : '' ?>>Male</option>
<option value="Female" <?= (strcasecmp($student['gender'] ?? '', 'Female') === 0) ? 'selected' : '' ?>>Female</option>
</select>
<div class="invalid-feedback">Please choose a gender.</div>
</div>
<!-- dob -->
<div class="col-md-3">
<label class="form-label">DOB</label>
<input type="date" name="dob" class="form-control js-dob" value="<?= esc($dobVal) ?>" required>
<div class="invalid-feedback">Please provide a valid date of birth.</div>
</div>
<!-- age (readonly) -->
<div class="col-md-3">
<label class="form-label">Age</label>
<input type="number" name="age" class="form-control js-age" value="<?= (int)($student['age'] ?? 0) ?>" min="0" step="1" readonly>
<div class="form-text">Auto-calculated from DOB.</div>
</div>
<!-- registration_grade -->
<div class="col-md-3">
<label class="form-label">Grade</label>
<select name="registration_grade" class="form-select" required>
<option value="" disabled <?= empty($student['registration_grade']) ? 'selected' : '' ?>>Select…</option>
<?php foreach ($gradeOptions as $opt): ?>
<option value="<?= esc($opt) ?>" <?= (strcasecmp((string)($student['registration_grade'] ?? ''), (string)$opt) === 0) ? 'selected' : '' ?>>
<?= esc($opt) ?>
</option>
<?php endforeach; ?>
</select>
<div class="invalid-feedback">Please choose a grade.</div>
</div>
<!-- registration_date (datetime-local) -->
<div class="col-md-3">
<label class="form-label">Registration Date & Time</label>
<input type="datetime-local" name="registration_date" class="form-control" value="<?= esc($regDateLocal) ?>">
<div class="form-text">Local time (<?= esc(user_timezone()) ?>).</div>
</div>
<!-- parent_id -->
<div class="col-md-3">
<label class="form-label">Parent ID</label>
<input type="number" name="parent_id" class="form-control" value="<?= (int)($student['parent_id'] ?? 0) ?>" min="1" step="1" required>
<div class="invalid-feedback">Parent ID is required.</div>
</div>
<!-- year_of_registration -->
<div class="col-md-3">
<label class="form-label">Year of Registration</label>
<input type="text" name="year_of_registration" class="form-control"
value="<?= esc($student['year_of_registration'] ?? '') ?>"
pattern="^\d{4}(-\d{4})?$" maxlength="25" placeholder="e.g., 2025 or 2025-2026">
<div class="invalid-feedback">Use YYYY or YYYY-YYYY.</div>
</div>
<!-- school_year -->
<?php
$syDatalistId = 'datalist-school-years-' . (int)$student['id'];
$nowY = (int)date('Y');
$syOptions = [];
for ($y = $nowY - 2; $y <= $nowY + 3; $y++) {
$syOptions[] = $y . '-' . ($y + 1);
}
?>
<div class="col-md-3">
<label class="form-label">School Year</label>
<input list="<?= $syDatalistId ?>" name="school_year" class="form-control"
value="<?= esc($student['school_year'] ?? '') ?>"
pattern="^\d{4}-\d{4}$" maxlength="9" placeholder="YYYY-YYYY">
<datalist id="<?= $syDatalistId ?>">
<?php foreach ($syOptions as $sy): ?>
<option value="<?= esc($sy) ?>"></option>
<?php endforeach; ?>
</datalist>
<div class="invalid-feedback">Format: YYYY-YYYY.</div>
</div>
<!-- rfid_tag -->
<div class="col-md-3">
<label class="form-label">RFID Tag</label>
<input type="text" name="rfid_tag" class="form-control" value="<?= esc($student['rfid_tag'] ?? '') ?>" maxlength="100" placeholder="Optional">
</div>
<!-- semester -->
<div class="col-md-3">
<label class="form-label">Semester</label>
<?php $sem = trim((string)($student['semester'] ?? '')); ?>
<select name="semester" class="form-select">
<option value="" <?= $sem === '' ? 'selected' : '' ?>>—</option>
<option value="Fall" <?= strcasecmp($sem, 'Fall') === 0 ? 'selected' : '' ?>>Fall</option>
<option value="Spring" <?= strcasecmp($sem, 'Spring') === 0 ? 'selected' : '' ?>>Spring</option>
<option value="Summer" <?= strcasecmp($sem, 'Summer') === 0 ? 'selected' : '' ?>>Summer</option>
</select>
</div>
<!-- tuition_paid -->
<div class="col-md-3">
<label class="form-label d-block">Tuition Paid</label>
<input type="hidden" name="tuition_paid" value="0">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" value="1" id="tuition_paid_<?= (int)$student['id'] ?>" name="tuition_paid" <?= !empty($student['tuition_paid']) ? 'checked' : '' ?>>
<label class="form-check-label" for="tuition_paid_<?= (int)$student['id'] ?>">Mark as paid</label>
</div>
</div>
<!-- is_new -->
<div class="col-md-3">
<label class="form-label">Is New Student?</label>
<?php $isNewVal = (string)($student['is_new'] ?? '1'); ?>
<select name="is_new" class="form-select" required>
<option value="1" <?= $isNewVal === '1' ? 'selected' : '' ?>>Yes (New)</option>
<option value="0" <?= $isNewVal === '0' ? 'selected' : '' ?>>No (Returning)</option>
</select>
</div>
<!-- is_active -->
<div class="col-md-3">
<label class="form-label d-block">Active Status</label>
<input type="hidden" name="is_active" value="0">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" value="1" id="is_active_<?= (int)$student['id'] ?>" name="is_active" <?= $isActive ? 'checked' : '' ?>>
<label class="form-check-label" for="is_active_<?= (int)$student['id'] ?>">Show in classes/attendance</label>
</div>
</div>
<!-- photo_consent -->
<div class="col-md-12">
<div class="form-check mt-2">
<input type="hidden" name="photo_consent" value="0">
<input class="form-check-input" type="checkbox" value="1" id="photo_consent_<?= (int)$student['id'] ?>" name="photo_consent" <?= !empty($student['photo_consent']) ? 'checked' : '' ?>>
<label class="form-check-label" for="photo_consent_<?= (int)$student['id'] ?>">Photo Consent</label>
</div>
</div>
<!-- Medical Conditions -->
<div class="col-md-6">
<label class="form-label">Medical Conditions</label>
<!-- Hidden payload + touched flag -->
<input type="hidden" name="medical_conditions" id="<?= $medId ?>_hidden">
<input type="hidden" name="medical_touched" id="<?= $medId ?>_touched" value="0">
<select class="form-select js-multi"
id="<?= $medId ?>"
multiple
data-none="None"
data-hidden="#<?= $medId ?>_hidden"
data-other="#<?= $medOtherId ?>"
data-touched="#<?= $medId ?>_touched">
<?php foreach ($medicalOptions as $opt): ?>
<option value="<?= esc($opt) ?>" <?= in_array($opt, $medSelected, true) ? 'selected' : '' ?>>
<?= esc($opt) ?>
</option>
<?php endforeach; ?>
</select>
<div class="form-text">Hold Ctrl/⌘ to select multiple. Choose <b>None</b> if no conditions.</div>
<label class="form-label mt-2">Other (free text, comma-separated)</label>
<input type="text" class="form-control" id="<?= $medOtherId ?>" placeholder="e.g., Seasonal rhinitis" value="<?= esc($medOther) ?>">
</div>
<!-- Allergies -->
<div class="col-md-6">
<label class="form-label">Allergies</label>
<!-- Hidden payload + touched flag -->
<input type="hidden" name="allergies" id="<?= $allId ?>_hidden">
<input type="hidden" name="allergies_touched" id="<?= $allId ?>_touched" value="0">
<select class="form-select js-multi"
id="<?= $allId ?>"
multiple
data-none="None"
data-hidden="#<?= $allId ?>_hidden"
data-other="#<?= $allOtherId ?>"
data-touched="#<?= $allId ?>_touched">
<?php foreach ($allergyOptions as $opt): ?>
<option value="<?= esc($opt) ?>" <?= in_array($opt, $allSelected, true) ? 'selected' : '' ?>>
<?= esc($opt) ?>
</option>
<?php endforeach; ?>
</select>
<div class="form-text">Hold Ctrl/⌘ to select multiple. Choose <b>None</b> if no allergies.</div>
<label class="form-label mt-2">Other (free text, comma-separated)</label>
<input type="text" class="form-control" id="<?= $allOtherId ?>" placeholder="e.g., Penicillin" value="<?= esc($allOther) ?>">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success">Save Changes</button>
</div>
</form>
</div>
</div>
</div>
<!-- /Edit Student Modal -->
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="13" class="text-center">No students found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
/* ============== DataTables (guarded) ============== */
if (window.jQuery && typeof jQuery.fn.DataTable === 'function' && document.getElementById('myTable')) {
jQuery('#myTable').DataTable(); // Use your own options if needed
}
/* ============== Bootstrap 5 validation ============== */
(function() {
const forms = document.querySelectorAll('.needs-validation');
Array.prototype.forEach.call(forms, (form) => {
form.addEventListener('submit', (evt) => {
if (!form.checkValidity()) {
evt.preventDefault();
evt.stopPropagation();
}
form.classList.add('was-validated');
}, false);
});
})();
/* ============== Helpers ============== */
function splitCSV(s) {
if (!s) return [];
return s.split(',').map(x => x.trim()).filter(Boolean);
}
function uniq(arr) {
const seen = new Set();
const out = [];
for (const v of arr) {
const k = v.toLowerCase();
if (!seen.has(k)) {
seen.add(k);
out.push(v);
}
}
return out;
}
function setTouched(sel) {
const tSel = sel.dataset.touched ? document.querySelector(sel.dataset.touched) : null;
if (tSel) tSel.value = '1';
}
function updateHiddenFromSelect(sel) {
const hiddenSel = sel.dataset.hidden ? document.querySelector(sel.dataset.hidden) : null;
if (!hiddenSel) return;
const noneLabel = (sel.dataset.none || 'None').toLowerCase();
const otherInp = sel.dataset.other ? document.querySelector(sel.dataset.other) : null;
const selected = Array.from(sel.selectedOptions).map(o => o.value).filter(Boolean);
const others = otherInp && !otherInp.disabled ? splitCSV(otherInp.value) : [];
let final = uniq([...selected, ...others]);
// Remove "None" token from payload; exclusivity handled elsewhere
final = final.filter(v => v.toLowerCase() !== noneLabel);
hiddenSel.value = final.length ? final.join(', ') : '';
}
function enforceNone(sel) {
const noneLabel = sel.dataset.none || 'None';
const values = Array.from(sel.selectedOptions).map(o => o.value);
const otherInp = sel.dataset.other ? document.querySelector(sel.dataset.other) : null;
if (values.includes(noneLabel)) {
// Deselect everything except "None"
Array.from(sel.options).forEach(opt => {
if (opt.value !== noneLabel) opt.selected = false;
});
if (otherInp) {
otherInp.value = '';
otherInp.disabled = true;
}
} else {
// Ensure "None" is off if other items chosen
Array.from(sel.options).forEach(opt => {
if (opt.value === noneLabel) opt.selected = false;
});
if (otherInp) {
otherInp.disabled = false;
}
}
updateHiddenFromSelect(sel);
}
function bindSelect(sel) {
if (sel.__bound) return;
sel.__bound = true;
// Initial normalize + pack based on server preselects
enforceNone(sel);
sel.addEventListener('change', () => {
enforceNone(sel);
setTouched(sel);
});
const otherInp = sel.dataset.other ? document.querySelector(sel.dataset.other) : null;
if (otherInp) {
otherInp.addEventListener('input', () => {
updateHiddenFromSelect(sel);
setTouched(sel);
});
}
}
// Bind all multi-selects present on the page (all modals rendered server-side)
document.querySelectorAll('select.js-multi').forEach(bindSelect);
// Modal lifecycle: wire age + (re)bind selects inside shown modal
document.querySelectorAll('.modal').forEach((modal) => {
modal.addEventListener('shown.bs.modal', () => {
// Age from DOB
const dob = modal.querySelector('.js-dob');
const age = modal.querySelector('.js-age');
if (dob && age) {
const update = () => {
if (!dob.value) {
age.value = '';
return;
}
const d = new Date(dob.value + 'T00:00:00');
if (isNaN(d.getTime())) {
age.value = '';
return;
}
const t = new Date();
let a = t.getFullYear() - d.getFullYear();
const m = t.getMonth() - d.getMonth();
if (m < 0 || (m === 0 && t.getDate() < d.getDate())) a--;
age.value = a >= 0 ? a : '';
};
update();
dob.addEventListener('change', update);
dob.addEventListener('input', update);
}
modal.querySelectorAll('select.js-multi').forEach(bindSelect);
// Ensure hidden fields packed and flags set on submit
modal.querySelectorAll('form').forEach((form) => {
if (form.__syncBound) return;
form.__syncBound = true;
form.addEventListener('submit', () => {
form.querySelectorAll('select.js-multi').forEach((sel) => {
enforceNone(sel);
updateHiddenFromSelect(sel);
setTouched(sel);
});
});
});
});
});
// --- CSRF sync: ensure latest token is sent on submit ---
(function attachCsrfSync() {
const TOKEN_NAME = '<?= csrf_token() ?>';
const COOKIE_NAME = <?= json_encode(config('Security')->csrfCookieName ?? (config('Security')->cookieName ?? 'csrf_cookie_name')) ?>;
function getCookie(name) {
const parts = document.cookie.split(';');
for (let i = 0; i < parts.length; i++) {
const p = parts[i].trim();
if (p.startsWith(name + '=')) return decodeURIComponent(p.substring(name.length + 1));
}
return '';
}
function syncFormToken(form) {
try {
const cookieVal = getCookie(COOKIE_NAME);
if (!cookieVal) return; // nothing to sync
const hidden = form.querySelector('input[name="' + TOKEN_NAME + '"]');
if (hidden) hidden.value = cookieVal;
} catch (_) {
// no-op
}
}
// Bind to all edit forms on this page
document.querySelectorAll('form').forEach((form) => {
if (form.__csrfSyncBound) return;
form.__csrfSyncBound = true;
form.addEventListener('submit', () => syncFormToken(form));
});
})();
});
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,205 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<?php
$classes = $classes ?? [];
$entries = $entries ?? [];
$subjectLabels = $subjectLabels ?? [];
$editEntry = $editEntry ?? null;
?>
<div class="container-fluid">
<div class="d-flex flex-wrap align-items-center justify-content-between mb-3 gap-2">
<div>
<h2 class="mb-1">Subject Curriculum</h2>
<p class="text-muted mb-0">Add, update, or remove the unit/surah targets that teachers use when reporting class progress.</p>
</div>
<a href="<?= site_url('administrator/class_assignment') ?>" class="btn btn-outline-secondary btn-sm">Back to class list</a>
</div>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success"><?= esc(session()->getFlashdata('success')) ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('errors')): ?>
<div class="alert alert-danger">
<ul class="mb-0">
<?php foreach (session()->getFlashdata('errors') as $error): ?>
<li><?= esc($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<div class="row g-4">
<div class="col-lg-5">
<div class="card shadow-sm">
<div class="card-header bg-white">
<h5 class="mb-0">Add new curriculum entry</h5>
</div>
<div class="card-body">
<form action="<?= site_url('administrator/subject-curriculum/save') ?>" method="post">
<?= csrf_field() ?>
<div class="mb-3">
<label class="form-label">Class</label>
<select name="class_id" class="form-select" required>
<option value="">Select a class</option>
<?php foreach ($classes as $class): ?>
<option value="<?= esc($class['id']) ?>"
<?= set_select('class_id', $class['id'], false) ?>>
<?= esc($class['class_name'] ?? 'Unnamed') ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label">Subject</label>
<select name="subject" class="form-select" required>
<?php foreach ($subjectLabels as $key => $label): ?>
<option value="<?= esc($key) ?>" <?= set_select('subject', $key, $key === 'islamic') ?>>
<?= esc($label) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label">Unit / Term number</label>
<input type="number" name="unit_number" class="form-control" placeholder="Optional" value="<?= set_value('unit_number') ?>">
<div class="form-text text-muted small">Leave blank for Quran/Arabic entries that only need a chapter/surah.</div>
</div>
<div class="mb-3">
<label class="form-label">Unit title</label>
<input type="text" name="unit_title" class="form-control" placeholder="Optional summary" value="<?= set_value('unit_title') ?>">
</div>
<div class="mb-3">
<label class="form-label">Chapter / Surah</label>
<input type="text" name="chapter_name" class="form-control" required value="<?= set_value('chapter_name') ?>">
</div>
<button type="submit" class="btn btn-primary w-100">Save curriculum entry</button>
</form>
</div>
</div>
<?php if ($editEntry): ?>
<div class="card shadow-sm mt-4">
<div class="card-header bg-white">
<h5 class="mb-0">Edit entry</h5>
</div>
<div class="card-body">
<form action="<?= site_url('administrator/subject-curriculum/update/' . $editEntry['id']) ?>" method="post">
<?= csrf_field() ?>
<div class="mb-3">
<label class="form-label">Class</label>
<select name="class_id" class="form-select" required>
<?php foreach ($classes as $class): ?>
<?php
$selected = set_select('class_id', $class['id'], ((int)($editEntry['class_id'] ?? 0)) === (int)$class['id']);
?>
<option value="<?= esc($class['id']) ?>" <?= $selected ?>>
<?= esc($class['class_name'] ?? 'Unnamed') ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label">Subject</label>
<select name="subject" class="form-select" required>
<?php foreach ($subjectLabels as $key => $label): ?>
<?php
$isSelected = set_select('subject', $key, ($editEntry['subject'] ?? '') === $key);
?>
<option value="<?= esc($key) ?>" <?= $isSelected ?>>
<?= esc($label) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label">Unit / Term number</label>
<input type="number" name="unit_number" class="form-control" placeholder="Optional" value="<?= set_value('unit_number', $editEntry['unit_number'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label">Unit title</label>
<input type="text" name="unit_title" class="form-control" placeholder="Optional summary" value="<?= set_value('unit_title', $editEntry['unit_title'] ?? '') ?>">
</div>
<div class="mb-3">
<label class="form-label">Chapter / Surah</label>
<input type="text" name="chapter_name" class="form-control" required value="<?= set_value('chapter_name', $editEntry['chapter_name'] ?? '') ?>">
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-success w-100">Apply changes</button>
<a href="<?= site_url('administrator/subject-curriculum') ?>" class="btn btn-outline-secondary w-100">Cancel</a>
</div>
</form>
</div>
</div>
<?php endif; ?>
</div>
<div class="col-lg-7">
<div class="card shadow-sm">
<div class="card-header bg-white d-flex align-items-center justify-content-between">
<h5 class="mb-0">Current curriculum entries</h5>
<span class="text-muted small"><?= count($entries) ?> record<?= count($entries) === 1 ? '' : 's' ?></span>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered table-hover align-middle mb-0" data-no-mgmt-sticky>
<thead class="table-light">
<tr>
<th>Class</th>
<th>Subject</th>
<th>Unit</th>
<th>Unit title</th>
<th>Chapter / Surah</th>
<th>Updated</th>
<th style="min-width: 170px;">Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($entries)): ?>
<tr>
<td colspan="7" class="text-center text-muted">No curriculum records yet.</td>
</tr>
<?php else: ?>
<?php foreach ($entries as $entry): ?>
<?php
$subjectLabel = $subjectLabels[$entry['subject']] ?? ucfirst($entry['subject'] ?? '');
$updatedAt = $entry['updated_at'] ?? $entry['created_at'] ?? '';
$updatedDisplay = '';
if ($updatedAt) {
try {
$updatedDisplay = (new \DateTime($updatedAt))->format('M d, Y H:i');
} catch (\Exception $e) {
$updatedDisplay = $updatedAt;
}
}
?>
<tr>
<td><?= esc(($entry['class_name'] ?? '') ?: '—') ?></td>
<td><?= esc($subjectLabel) ?></td>
<td><?= $entry['unit_number'] ? esc((string)$entry['unit_number']) : '—' ?></td>
<td><?= esc(($entry['unit_title'] ?? '') ?: '—') ?></td>
<td><?= esc(($entry['chapter_name'] ?? '') ?: '—') ?></td>
<td><?= esc($updatedDisplay ?: '—') ?></td>
<td class="d-flex gap-2 flex-wrap">
<a href="<?= site_url('administrator/subject-curriculum/edit/' . $entry['id']) ?>" class="btn btn-sm btn-outline-primary">Edit</a>
<form action="<?= site_url('administrator/subject-curriculum/delete/' . $entry['id']) ?>" method="post" onsubmit="return confirm('Remove this curriculum entry?');">
<?= csrf_field() ?>
<button type="submit" class="btn btn-sm btn-outline-danger">Delete</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<?= $this->endSection() ?>
@@ -0,0 +1,514 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div
class="container-fluid"
data-api-list="<?= esc($apiListUrl ?? '') ?>"
data-api-assign="<?= esc($apiAssignUrl ?? '') ?>"
data-api-delete="<?= esc($apiDeleteUrl ?? '') ?>"
data-csrf-name="<?= esc($csrfTokenName ?? csrf_token()) ?>"
data-csrf-value="<?= esc($csrfTokenValue ?? csrf_hash()) ?>"
>
<div class="wrapper">
<div class="content">
<h2 class="text-center mt-4 mb-2">Teacher Class Assignments</h2>
<?php if (!empty($missingYear)): ?>
<div class="alert alert-warning" role="alert">
Current school year is not configured. This page is read-only until configured in
<a href="<?= site_url('configuration/configuration_view') ?>" class="alert-link">Add/Edit Configuration</a> (key: <code>school_year</code>).
</div>
<?php endif; ?>
<div class="d-flex justify-content-center align-items-center gap-2 mb-3">
<label for="tcaYearSelect" class="col-form-label">School year</label>
<form method="get" action="<?= site_url('administrator/teacher_class_assignment') ?>" class="d-flex align-items-center gap-2">
<select id="tcaYearSelect" name="schoolYear" class="form-select form-select-sm" style="min-width: 180px;">
<?php
$years = isset($schoolYears) && is_array($schoolYears) ? $schoolYears : [];
if (empty($years) && !empty($schoolYear)) $years = [$schoolYear];
foreach ($years as $y): $val = is_array($y) && isset($y['school_year']) ? $y['school_year'] : (string)$y; ?>
<option value="<?= esc($val) ?>" <?= ((string)($schoolYear ?? '') === (string)$val) ? 'selected' : '' ?>>
<?= esc($val) ?>
</option>
<?php endforeach; ?>
</select>
<?php $semVal = (string)($semester ?? ($_GET['semester'] ?? '')); ?>
<label for="tcaSemesterSelect" class="col-form-label">Semester</label>
<select id="tcaSemesterSelect" name="semester" class="form-select form-select-sm" style="min-width: 140px;">
<option value="">—</option>
<option value="Fall" <?= (strcasecmp($semVal,'Fall')===0?'selected':'') ?>>Fall</option>
<option value="Spring" <?= (strcasecmp($semVal,'Spring')===0?'selected':'') ?>>Spring</option>
</select>
<button type="submit" class="btn btn-secondary btn-sm">Apply</button>
</form>
<?php if (isset($isCurrentYear) && !$isCurrentYear): ?>
<span class="badge bg-secondary">Read-only (Past Year)</span>
<?php endif; ?>
</div>
<?= view('partials/flash_messages') ?>
<div id="assignmentMessages"></div>
<table id="teachersTable" class="display table table-striped table-hover align-middle">
<thead class="table-dark">
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Phone Number</th>
<th>Role</th>
<th>Assigned Classes</th>
<th>School Year</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="9" class="text-center text-muted">Loading...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<?= $this->include('partials/assign_class_teacher_modal') ?>
<?= $this->endSection() ?>
<?= $this->section('scripts') ?>
<script>
document.addEventListener('DOMContentLoaded', function () {
var container = document.querySelector('.container-fluid[data-api-list]');
if (!container) {
return;
}
var apiList = container.getAttribute('data-api-list') || '';
var apiAssign = container.getAttribute('data-api-assign') || '';
var apiDelete = container.getAttribute('data-api-delete') || '';
var csrfName = container.getAttribute('data-csrf-name') || '';
var csrfValue = container.getAttribute('data-csrf-value') || '';
var tableEl = document.getElementById('teachersTable');
var tbody = tableEl ? tableEl.querySelector('tbody') : null;
var messages = document.getElementById('assignmentMessages');
var selectedYear = <?= json_encode((string)($schoolYear ?? '')) ?>;
var isCurrentYear = <?= json_encode((bool)($isCurrentYear ?? true)) ?>;
var classSelect = document.getElementById('class_section_id');
var assignForm = document.getElementById('assignClassForm');
var assignButton = document.getElementById('assignClassButton');
var teacherRoleHidden = document.getElementById('teacherRoleHidden');
var teacherRoleDisplay = document.getElementById('teacherRoleDisplay');
var assignModalEl = document.getElementById('assignClassModal');
function updateCsrfFromResponse(data) {
if (!data) return;
if (data.csrf_token && data.csrf_hash) {
csrfName = data.csrf_token;
csrfValue = data.csrf_hash;
container.setAttribute('data-csrf-name', csrfName);
container.setAttribute('data-csrf-value', csrfValue);
var hiddenField = document.getElementById('assignCsrfField');
if (hiddenField) {
hiddenField.setAttribute('name', csrfName);
hiddenField.value = csrfValue;
}
}
}
function applyCsrf(payload) {
var data = Object.assign({}, payload || {});
if (csrfName) {
data[csrfName] = csrfValue;
}
return data;
}
function bootstrapAlertClass(type) {
if (type === 'success') return 'alert-success';
if (type === 'info') return 'alert-info';
if (type === 'warning') return 'alert-warning';
return 'alert-danger';
}
function showMessage(type, text) {
if (!messages || !text) return;
var alert = document.createElement('div');
alert.className = 'alert ' + bootstrapAlertClass(type) + ' alert-dismissible fade show';
alert.setAttribute('role', 'alert');
alert.innerHTML = '<span>' + text + '</span>'
+ '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>';
messages.appendChild(alert);
setTimeout(function () {
if (alert && alert.parentNode) {
alert.classList.remove('show');
alert.classList.add('fade');
setTimeout(function () {
if (alert.parentNode) {
alert.parentNode.removeChild(alert);
}
}, 200);
}
}, 6000);
}
function renderAssignedList(assignments, label, teacher) {
var fragment = document.createDocumentFragment();
assignments.forEach(function (assignment) {
var li = document.createElement('li');
li.className = 'd-flex align-items-center justify-content-between gap-2 mb-1';
var text = document.createElement('span');
text.innerHTML = '<strong>' + (assignment.class_section_name || 'Class #' + assignment.section_id) + '</strong> (' + label + ')';
li.appendChild(text);
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'btn btn-outline-danger btn-sm remove-assignment-btn';
btn.textContent = 'Remove';
btn.setAttribute('data-teacher-id', teacher.teacher_id);
btn.setAttribute('data-teacher-name', teacher.name || '');
btn.setAttribute('data-class-section-id', assignment.section_id);
btn.setAttribute('data-class-name', assignment.class_section_name || 'Class #' + assignment.section_id);
var position = (assignment.role || '').toLowerCase();
if (position !== 'main' && position !== 'ta') {
position = label.toLowerCase() === 'ta' ? 'ta' : 'main';
}
btn.setAttribute('data-position', position);
var assignmentSemester = assignment.semester || '';
var assignmentSchoolYear = assignment.school_year || selectedYear || '';
btn.setAttribute('data-semester', assignmentSemester);
btn.setAttribute('data-school-year', assignmentSchoolYear);
li.appendChild(btn);
fragment.appendChild(li);
});
return fragment;
}
function renderTeachersTable(teachers, schoolYear) {
if (!tbody) return;
if (window.jQuery && $.fn.DataTable && $.fn.DataTable.isDataTable('#teachersTable')) {
$('#teachersTable').DataTable().destroy();
}
tbody.innerHTML = '';
if (!teachers || teachers.length === 0) {
var emptyRow = document.createElement('tr');
var emptyCell = document.createElement('td');
emptyCell.colSpan = 9;
emptyCell.className = 'text-center text-muted';
emptyCell.textContent = 'No teachers found.';
emptyRow.appendChild(emptyCell);
tbody.appendChild(emptyRow);
} else {
teachers.forEach(function (teacher, index) {
var row = document.createElement('tr');
var orderCell = document.createElement('td');
orderCell.className = 'text-center';
orderCell.textContent = index + 1;
row.appendChild(orderCell);
var firstCell = document.createElement('td');
firstCell.textContent = teacher.firstname || '';
row.appendChild(firstCell);
var lastCell = document.createElement('td');
lastCell.textContent = teacher.lastname || '';
row.appendChild(lastCell);
var emailCell = document.createElement('td');
emailCell.textContent = teacher.email || '';
row.appendChild(emailCell);
var phoneCell = document.createElement('td');
phoneCell.textContent = teacher.cellphone || '';
row.appendChild(phoneCell);
var roleCell = document.createElement('td');
var role = (teacher.role || '').replace(/_/g, ' ');
roleCell.textContent = role ? role.charAt(0).toUpperCase() + role.slice(1) : 'N/A';
row.appendChild(roleCell);
var assignedCell = document.createElement('td');
if ((teacher.main_assignments && teacher.main_assignments.length) ||
(teacher.ta_assignments && teacher.ta_assignments.length)) {
var list = document.createElement('ul');
list.className = 'list-unstyled mb-0';
if (teacher.main_assignments && teacher.main_assignments.length) {
list.appendChild(renderAssignedList(teacher.main_assignments, 'Main', teacher));
}
if (teacher.ta_assignments && teacher.ta_assignments.length) {
list.appendChild(renderAssignedList(teacher.ta_assignments, 'TA', teacher));
}
assignedCell.appendChild(list);
} else {
var empty = document.createElement('em');
empty.textContent = 'No classes assigned';
assignedCell.appendChild(empty);
}
row.appendChild(assignedCell);
var schoolYearCell = document.createElement('td');
schoolYearCell.textContent = teacher.school_year || schoolYear || '';
row.appendChild(schoolYearCell);
var actionsCell = document.createElement('td');
var assignBtn = document.createElement('button');
assignBtn.type = 'button';
assignBtn.className = 'btn btn-success btn-sm assign-class-btn';
if (!isCurrentYear) { assignBtn.disabled = true; assignBtn.title = 'Editing disabled for non-current year'; }
assignBtn.setAttribute('data-bs-toggle', 'modal');
assignBtn.setAttribute('data-bs-target', '#assignClassModal');
assignBtn.setAttribute('data-teacher-id', teacher.teacher_id);
assignBtn.setAttribute('data-teacher-name', teacher.name || '');
assignBtn.setAttribute('data-teacher-role', teacher.role || '');
assignBtn.textContent = 'Assign Class';
actionsCell.appendChild(assignBtn);
row.appendChild(actionsCell);
tbody.appendChild(row);
});
}
if (window.jQuery && $.fn.DataTable) {
$('#teachersTable').DataTable({
pageLength: 100,
lengthMenu: [5, 10, 25, 50, 100],
order: [[0, 'asc']],
columnDefs: [
{ orderable: false, targets: [6, 8] }
]
});
}
}
function populateClassOptions(classes) {
if (!classSelect) {
return;
}
classSelect.innerHTML = '';
var placeholder = document.createElement('option');
placeholder.value = '';
placeholder.disabled = true;
placeholder.selected = true;
placeholder.textContent = 'Select a class';
classSelect.appendChild(placeholder);
(classes || []).forEach(function (cls) {
var option = document.createElement('option');
option.value = cls.class_section_id;
option.textContent = cls.class_section_name || ('Class #' + cls.class_section_id);
classSelect.appendChild(option);
});
}
function loadAssignments() {
if (!apiList || !tbody) {
return;
}
tbody.innerHTML = '<tr><td colspan="9" class="text-center text-muted">Loading...</td></tr>';
var url = apiList + (selectedYear ? ('?schoolYear=' + encodeURIComponent(selectedYear)) : '');
fetch(url, {
headers: { 'Accept': 'application/json' },
credentials: 'same-origin'
})
.then(function (response) {
return response.json()
.catch(function () { return {}; })
.then(function (data) {
return { ok: response.ok, data: data };
});
})
.then(function (result) {
if (!result) {
throw new Error('No response received.');
}
var data = result.data || {};
updateCsrfFromResponse(data);
if (!result.ok || data.ok === false) {
showMessage('danger', data.message || 'Unable to load teacher assignments.');
return;
}
renderTeachersTable(data.teachers || [], data.school_year || '');
populateClassOptions(data.classes || []);
})
.catch(function (error) {
showMessage('danger', error.message || 'Failed to load assignments.');
});
}
function submitAssignment(payload) {
if (!apiAssign || !isCurrentYear) {
showMessage('warning', 'Editing disabled for non-current year.');
return;
}
fetch(apiAssign, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
credentials: 'same-origin',
body: JSON.stringify(applyCsrf(payload))
})
.then(function (response) {
return response.json()
.catch(function () { return {}; })
.then(function (data) {
return { ok: response.ok, data: data };
});
})
.then(function (result) {
if (!result) {
throw new Error('No response received.');
}
var data = result.data || {};
updateCsrfFromResponse(data);
if (!result.ok || data.ok === false) {
showMessage('danger', data.message || 'Unable to assign class.');
return;
}
showMessage('success', data.message || 'Class assigned successfully.');
if (assignForm) {
assignForm.reset();
}
if (assignModalEl && window.bootstrap) {
var modalInstance = bootstrap.Modal.getInstance(assignModalEl);
if (modalInstance) {
modalInstance.hide();
}
}
loadAssignments();
})
.catch(function (error) {
showMessage('danger', error.message || 'Unable to assign class.');
});
}
function removeAssignment(payload) {
if (!apiDelete || !isCurrentYear) {
showMessage('warning', 'Editing disabled for non-current year.');
return;
}
fetch(apiDelete, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
credentials: 'same-origin',
body: JSON.stringify(applyCsrf(payload))
})
.then(function (response) {
return response.json()
.catch(function () { return {}; })
.then(function (data) {
return { ok: response.ok, data: data };
});
})
.then(function (result) {
if (!result) {
throw new Error('No response received.');
}
var data = result.data || {};
updateCsrfFromResponse(data);
if (!result.ok || data.ok === false) {
showMessage('danger', data.message || 'Unable to remove assignment.');
return;
}
showMessage('success', data.message || 'Assignment removed successfully.');
loadAssignments();
})
.catch(function (error) {
showMessage('danger', error.message || 'Unable to remove assignment.');
});
}
document.addEventListener('click', function (event) {
var assignBtn = event.target.closest('.assign-class-btn');
if (assignBtn) {
var teacherId = assignBtn.getAttribute('data-teacher-id') || '';
var teacherName = assignBtn.getAttribute('data-teacher-name') || '';
var teacherRole = assignBtn.getAttribute('data-teacher-role') || '';
var nameInput = document.getElementById('teacherName');
var idInput = document.getElementById('teacherId');
if (idInput) {
idInput.value = teacherId;
}
if (nameInput) {
nameInput.value = teacherName;
}
if (teacherRoleHidden) {
teacherRoleHidden.value = teacherRole;
}
if (teacherRoleDisplay) {
teacherRoleDisplay.value = teacherRole === 'teacher_assistant' ? 'Teaching Assistant' : 'Teacher';
}
}
var removeBtn = event.target.closest('.remove-assignment-btn');
if (removeBtn) {
event.preventDefault();
var teacherId = removeBtn.getAttribute('data-teacher-id');
var classSectionId = removeBtn.getAttribute('data-class-section-id');
var position = removeBtn.getAttribute('data-position');
var semester = removeBtn.getAttribute('data-semester') || '';
var schoolYear = removeBtn.getAttribute('data-school-year') || '';
var className = removeBtn.getAttribute('data-class-name') || 'this class';
var teacherName = removeBtn.getAttribute('data-teacher-name') || 'this teacher';
if (confirm('Remove ' + className + ' (' + position.toUpperCase() + ') from ' + teacherName + '?')) {
removeAssignment({
teacher_id: teacherId,
class_section_id: classSectionId,
position: position,
semester: semester,
school_year: schoolYear
});
}
}
});
if (assignButton && assignForm) {
assignButton.addEventListener('click', function (event) {
event.preventDefault();
if (!assignForm.checkValidity()) {
assignForm.reportValidity ? assignForm.reportValidity() : alert('Please fill out all required fields.');
return;
}
submitAssignment({
teacher_id: assignForm.teacher_id.value,
teacher_role: assignForm.teacher_role ? assignForm.teacher_role.value : (teacherRoleHidden ? teacherRoleHidden.value : ''),
class_section_id: assignForm.class_section_id.value
});
});
}
loadAssignments();
});
</script>
<?= $this->endSection() ?>
@@ -0,0 +1,190 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<div class="container-fluid px-4 py-4 teacher-submissions-page">
<div class="card shadow-sm">
<div class="card-header bg-light d-flex justify-content-between align-items-center">
<div>
<strong>Teacher Submission Dashboard</strong>
</div>
<?php if (!empty($semester) || !empty($schoolYear)): ?>
<span class="small text-muted">
<?= esc(ucfirst($semester)) ?> <?= esc($schoolYear) ?>
</span>
<?php endif; ?>
</div>
<?php
$notificationHistory = $notificationHistory ?? [];
$summary = $summary ?? [];
$completionPercent = max(0, min(100, (int)($summary['submission_percentage'] ?? 0)));
$missingItemsCount = max(0, (int)($summary['missing_items'] ?? 0));
$submittedItems = max(0, (int)($summary['submitted_items'] ?? 0));
$totalItems = max(0, (int)($summary['total_items'] ?? 0));
?>
<div class="card-body">
<div class="border rounded-3 p-3 mb-4 bg-light">
<div class="d-flex flex-wrap gap-4 align-items-center">
<div>
<div class="text-uppercase small text-muted">Submission completion</div>
<div class="h4 fw-semibold mb-1"><?= esc($completionPercent) ?>%</div>
<div class="progress" style="height:6px;">
<div
class="progress-bar bg-primary"
role="progressbar"
style="width: <?= esc($completionPercent) ?>%;"
aria-valuenow="<?= esc($completionPercent) ?>"
aria-valuemin="0"
aria-valuemax="100"
></div>
</div>
</div>
<div>
<div class="text-uppercase small text-muted">Missing items</div>
<div class="h4 fw-semibold text-danger mb-0"><?= esc($missingItemsCount) ?></div>
</div>
<div class="text-muted small">
<?= esc($submittedItems) ?> submitted / <?= esc($totalItems) ?> total items
</div>
</div>
</div>
<form method="post" action="<?= site_url('administrator/teacher-submissions/notify') ?>">
<?= csrf_field() ?>
<div class="table-responsive">
<table
class="table table-striped table-bordered m-0 align-middle teacher-submissions-table"
data-no-mgmt-sticky
data-no-dt-fixedheader
>
<thead class="table-light">
<tr>
<th>Class Section</th>
<th>Teacher</th>
<th class="text-center">Midterm Score</th>
<th class="text-center">Midterm Comment</th>
<th class="text-center">Participation</th>
<th class="text-center">PTAP Comment</th>
<th class="text-center">Notifications</th>
</tr>
</thead>
<tbody>
<?php if (!empty($rows)): ?>
<?php foreach ($rows as $row): ?>
<tr>
<td><?= esc($row['class_section']) ?></td>
<td>
<?php if (!empty($row['teachers'])): ?>
<?php foreach ($row['teachers'] as $teacher): ?>
<div><?= esc($teacher['label'] ?? 'Teacher') ?></div>
<?php endforeach; ?>
<?php else: ?>
<span class="text-muted small">Unassigned</span>
<?php endif; ?>
</td>
<?php foreach ([
'midterm_score_status',
'midterm_comment_status',
'participation_status',
'ptap_comment_status'
] as $statusKey): ?>
<?php $status = $row[$statusKey] ?? ['label' => 'N/A', 'badge' => 'bg-secondary']; ?>
<td class="text-center">
<span class="badge <?= esc($status['badge'] ?? 'bg-secondary') ?>">
<?= esc($status['label'] ?? 'N/A') ?>
</span>
<?php if (!empty($status['detail'])): ?>
<div class="small text-muted"><?= esc($status['detail']) ?></div>
<?php endif; ?>
</td>
<?php endforeach; ?>
<td>
<?php if (!empty($row['teachers'])): ?>
<?php $missingPayload = base64_encode(json_encode($row['missing_items'] ?? [])); ?>
<?php foreach ($row['teachers'] as $teacher): ?>
<?php $history = $notificationHistory[$row['class_section_id']][$teacher['id']] ?? []; ?>
<?php $lastEntry = $history[0] ?? null; ?>
<div class="mb-2">
<label class="d-flex align-items-center gap-2 mb-1">
<input
type="checkbox"
class="form-check-input"
name="notify[<?= esc($row['class_section_id']) ?>][<?= esc($teacher['id']) ?>]"
value="1"
/>
<span class="fw-semibold"><?= esc($teacher['label'] ?? 'Teacher') ?></span>
</label>
<input
type="hidden"
name="missing_items[<?= esc($row['class_section_id']) ?>][<?= esc($teacher['id']) ?>]"
value="<?= esc($missingPayload) ?>"
/>
<div class="small text-muted">
<?php if ($lastEntry !== null): ?>
<span>
Last <?= esc($lastEntry['status'] === 'sent' ? 'sent' : 'attempted') ?> on <?= esc($lastEntry['sent_at_text'] ?: 'N/A') ?>
by <?= esc($lastEntry['admin_name'] ?? '') ?>
</span>
<?php if (count($history) > 1): ?>
<div>History: <?= count($history) ?> entries</div>
<?php endif; ?>
<?php else: ?>
<span>No notifications sent yet</span>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<?php else: ?>
<span class="text-muted small">No teacher assigned</span>
<?php endif; ?>
<?php if (!empty($row['missing_items'])): ?>
<div class="small text-danger mt-1">
Outstanding: <?= esc(implode(', ', $row['missing_items'])) ?>
</div>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="7" class="text-center">No teacher-class assignments found for this term.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<div class="mt-3 text-end">
<button type="submit" class="btn btn-primary" <?= empty($rows) ? 'disabled' : '' ?>>
Send notifications to selected teachers
</button>
</div>
</form>
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('styles') ?>
<style>
.teacher-submissions-page {
width: 100%;
max-width: 100%;
}
.teacher-submissions-page .card {
width: 100%;
}
.teacher-submissions-page .table-responsive {
max-width: none;
}
.card-body .table-responsive table thead th {
position: static !important;
top: auto !important;
}
.card-body .table-responsive .teacher-submissions-table {
width: 100% !important;
min-width: 100%;
table-layout: auto;
}
.card-body .table-responsive .teacher-submissions-table th,
.card-body .table-responsive .teacher-submissions-table td {
white-space: normal;
}
</style>
<?= $this->endSection() ?>
+43
View File
@@ -0,0 +1,43 @@
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Teachers</h1>
<a href="/register" class="btn btn-primary">Add New Teacher</a>
</div>
<table id="myTable" class="display">
<thead>
<tr>
<th>ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Phone</th>
<th>Email</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($teachers as $teacher): ?>
<tr>
<td><?php echo $teacher['id']; ?></td>
<td><?php echo $teacher['firstname']; ?></td>
<td><?php echo $teacher['lastname']; ?></td>
<td><?php echo $teacher['cellphone']; ?></td>
<td><?php echo $teacher['email']; ?></td>
<td>
<a href="teacher/edit/<?php echo $teacher['id']; ?>" class="btn btn-warning">Edit</a>
<a href="teacher/delete/<?php echo $teacher['id']; ?>" class="btn btn-danger"
onclick="return confirm('Are you sure you want to delete this teacher?');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</main>
<?= $this->section('scripts') ?>
<script>
$(document).ready(function() {
$('#myTable').DataTable();
});
</script>
<?= $this->endSection() ?>
+41
View File
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Manage Students</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="/css/administrator.css">
</head>
<body>
<div class="content-wrapper">
<div class="container">
<?php include('partials/header.php'); ?>
<h1>Teachers</h1>
<a href="teacher/create" class="btn btn-primary">Add New Teacher</a>
<table class="table table-striped mt-3">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($teachers as $teacher): ?>
<tr>
<td><?php echo $teacher['id']; ?></td>
<td><?php echo $teacher['firstname'] . ' ' . $teacher['lastname']; ?></td>
<td><?php echo $teacher['email']; ?></td>
<td>
<a href="teacher/edit/<?php echo $teacher['id']; ?>" class="btn btn-warning">Edit</a>
<a href="teacher/delete/<?php echo $teacher['id']; ?>" class="btn btn-danger" onclick="return confirm('Are you sure you want to delete this teacher?');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>