580 lines
26 KiB
PHP
580 lines
26 KiB
PHP
<?= $this->extend('layout/management_layout') ?>
|
||
<?= $this->section('content') ?>
|
||
|
||
<?php
|
||
$page_title = $title ?? 'Pending Attendance Violations';
|
||
$school_year = $school_year ?? '';
|
||
$semester = $semester ?? '';
|
||
$show_actions = true; // pending page shows actions
|
||
?>
|
||
|
||
<div class="container-fluid px-0">
|
||
<h2 class="text-center mt-4 mb-3"><?= esc($page_title) ?></h2>
|
||
<div class="d-flex justify-content-center mb-2">
|
||
<div class="col-12 col-lg-10">
|
||
<?= $this->include('partials/academic_filter') ?>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if ($msg = session()->getFlashdata('message')): ?>
|
||
<div class="alert alert-success mx-3"><?= esc($msg) ?></div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($err = session()->getFlashdata('error')): ?>
|
||
<div class="alert alert-danger mx-3"><?= esc($err) ?></div>
|
||
<?php endif; ?>
|
||
|
||
<div class="row g-0">
|
||
<div class="col-12">
|
||
<div class="card border-0 rounded-3 shadow-sm">
|
||
|
||
<div class="card-header d-flex flex-wrap gap-2 justify-content-between align-items-center">
|
||
<h5 class="mt-2 mb-0">Pending Attendance Violations</h5>
|
||
<div class="d-flex flex-wrap gap-2 align-items-center">
|
||
<span class="badge bg-primary">School Year: <?= esc($school_year) ?></span>
|
||
<?php if ($semester !== ''): ?>
|
||
<span class="badge bg-secondary">Semester: <?= esc($semester) ?></span>
|
||
<?php endif; ?>
|
||
<a href="<?= site_url('attendance/violations/notified') ?>" class="btn btn-outline-dark btn-sm">
|
||
<i class="fas fa-check-circle"></i> View Notified
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card-body">
|
||
<div id="autoEmailAlert" class="alert d-none mb-3" role="alert"></div>
|
||
|
||
<!-- Legend -->
|
||
<div class="d-flex flex-wrap gap-3 mb-3 small">
|
||
<span><span class="badge rounded-pill" style="background-color:#e6f7ff;color:#000;"> </span> 1 Absence — Auto Email</span>
|
||
<span><span class="badge rounded-pill" style="background-color:#fadb14;color:#000;"> </span> 2 Absences (row/≤3 weeks) — Team Notify</span>
|
||
<span><span class="badge rounded-pill" style="background-color:#fa8c16;"> </span> 3 Absences (row/≤4 weeks) — Team Notify</span>
|
||
<span><span class="badge rounded-pill" style="background-color:#ff4d4f;"> </span> 4 Absences (row/≤5 weeks) — Expel Notify / 4 Lates — Team Notify</span>
|
||
<span><span class="badge rounded-pill" style="background-color:#bae7ff;color:#000;"> </span> 2 Lates (row/≤3 weeks) — Auto Email</span>
|
||
<span><span class="badge rounded-pill" style="background-color:#002766;"> </span> 3 Lates or 2L+1A (≤4 weeks) — Team Notify</span>
|
||
</div>
|
||
|
||
<?php if (empty($students)): ?>
|
||
<div class="alert alert-info mb-2">No attendance violations found.</div>
|
||
<?php if (!empty($debug ?? null)): ?>
|
||
<div class="alert alert-secondary small">
|
||
<strong>Debug</strong>
|
||
<div>School Year: <?= esc($debug['school_year_param'] ?? '') ?> | Semester: <?= esc($debug['semester_param'] ?? '') ?></div>
|
||
<div>Class students: <?= esc($debug['class_students'] ?? 0) ?> | Student IDs: <?= esc($debug['student_ids'] ?? 0) ?></div>
|
||
<div>Attendance rows (raw/filtered): <?= esc($debug['attendance_rows'] ?? 0) ?>/<?= esc($debug['filtered_rows'] ?? 0) ?></div>
|
||
<div>Violations computed: <?= esc($debug['violations'] ?? 0) ?></div>
|
||
<div>Active weeks: <?= esc($debug['active_weeks'] ?? 0) ?> | Students with attendance: <?= esc($debug['by_student'] ?? 0) ?></div>
|
||
<div>Absences last 5 weeks: <?= esc($debug['abs_last5'] ?? 0) ?> | Lates last 5 weeks: <?= esc($debug['late_last5'] ?? 0) ?></div>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php else: ?>
|
||
<div class="table-responsive">
|
||
<table id="violationsTable" class="table table-striped table-hover align-middle w-100">
|
||
<thead class="table-dark">
|
||
<tr>
|
||
<th>Student</th>
|
||
<th>Grade</th>
|
||
<th>Violation</th>
|
||
<th>Type</th>
|
||
<th class="text-center">Abs</th>
|
||
<th class="text-center">Late</th>
|
||
<th>Last Incident</th>
|
||
<th>Action</th>
|
||
<th>Notified?</th>
|
||
<th>#</th>
|
||
<th>Parent</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($students as $st): ?>
|
||
<?php
|
||
// Robust ID + name (supports multiple shapes)
|
||
$studId = (int)($st['id'] ?? $st['student_id'] ?? 0);
|
||
$studName =
|
||
($st['name'] ?? null)
|
||
?? ($st['student_name'] ?? null)
|
||
?? trim(($st['firstname'] ?? $st['first_name'] ?? '') . ' ' . ($st['lastname'] ?? $st['last_name'] ?? ''));
|
||
if ($studName === '' || $studName === null) {
|
||
$studName = $studId ? ('Student #' . $studId) : '—';
|
||
}
|
||
|
||
$grade = $st['class_name'] ?? '';
|
||
|
||
// Violation data (always present in pending view)
|
||
$viTitle = (string)($st['violation'] ?? '');
|
||
$viCode = (string)($st['violation_code'] ?? '');
|
||
$viType = (string)($st['type'] ?? '');
|
||
$viColor = (string)($st['color'] ?? '#6c757d');
|
||
$action = (string)($st['action'] ?? '');
|
||
|
||
$absences = (array)($st['absences'] ?? []);
|
||
$lates = (array)($st['lates'] ?? []);
|
||
$cntAbs = count($absences);
|
||
$cntLate = count($lates);
|
||
|
||
$lastDate = $st['last_date'] ?? null;
|
||
$incidentYmd = $lastDate ? substr((string)$lastDate, 0, 10) : '';
|
||
$incidentParam = $incidentYmd !== ''
|
||
? '&incident_date=' . urlencode($incidentYmd)
|
||
: '';
|
||
|
||
// Notified meta (may be 0/empty on pending view)
|
||
$notified = !empty($st['is_notified']);
|
||
$notifCnt = (int)($st['notif_counter'] ?? 0);
|
||
|
||
// Parent display info (optional)
|
||
$parentEmail = $st['parent_email'] ?? '';
|
||
$parentName = $st['parent_name'] ?? '';
|
||
$parentLabel = $parentName ?: ($parentEmail ?: 'View');
|
||
|
||
// Badge contrast
|
||
$isLight = in_array(strtolower($viColor), ['#bae7ff', '#e6f7ff', '#fadb14'], true);
|
||
$badgeCls = 'badge rounded-pill ' . ($isLight ? 'text-dark' : 'text-light');
|
||
$badgeCss = 'background-color:' . esc($viColor) . ';';
|
||
|
||
$subjectType = ($viType === 'late') ? 'Late' : (($viType === 'absence') ? 'Absent' : 'Attendance');
|
||
?>
|
||
<tr style="border-left:6px solid <?= esc($viColor) ?>;">
|
||
<td>
|
||
<a
|
||
href="<?= site_url('family') . '?student_id=' . (int)$studId ?>"
|
||
class="text-decoration-none"
|
||
data-family-student-id="<?= (int)$studId ?>"
|
||
title="Open Family Card">
|
||
<?= esc($studName) ?>
|
||
</a>
|
||
</td>
|
||
<td><?= esc($grade) ?></td>
|
||
<td><span class="<?= esc($badgeCls) ?>" style="<?= $badgeCss ?>"><?= esc($viTitle) ?></span></td>
|
||
<td class="text-capitalize"><?= esc($viType ?: '—') ?></td>
|
||
<td class="text-center"><?= esc($cntAbs) ?></td>
|
||
<td class="text-center"><?= esc($cntLate) ?></td>
|
||
<td data-order="<?= $incidentYmd !== '' ? esc($incidentYmd) : '' ?>">
|
||
<?= $incidentYmd !== '' ? esc(date('m-d-Y', strtotime($incidentYmd))) : '—' ?>
|
||
</td>
|
||
<td>
|
||
<?php if ($action === 'auto_email'): ?>
|
||
<span class="badge bg-primary">Auto Email</span>
|
||
<?php elseif ($action === 'expel_notify'): ?>
|
||
<span class="badge bg-danger">Expel Notify</span>
|
||
<?php else: ?>
|
||
<span class="badge bg-warning text-dark">Team Notify</span>
|
||
<?php endif; ?>
|
||
</td>
|
||
<td>
|
||
<span class="badge <?= $notified ? 'bg-success' : 'bg-warning text-dark' ?>">
|
||
<?= $notified ? 'Yes' : 'No' ?>
|
||
</span>
|
||
</td>
|
||
<td><?= esc($notifCnt) ?></td>
|
||
|
||
<!-- Parent column -->
|
||
<td>
|
||
<a
|
||
href="<?= site_url('family') . '?student_id=' . (int)$studId ?>"
|
||
class="text-decoration-none"
|
||
data-family-student-id="<?= (int)$studId ?>"
|
||
title="Open Family Card">
|
||
<?= esc($parentLabel) ?>
|
||
</a>
|
||
<?php if (!empty($parentEmail)): ?>
|
||
<a class="ms-2" href="mailto:<?= esc($parentEmail) ?>" title="Email First parent">
|
||
<i class="fas fa-envelope"></i>
|
||
</a>
|
||
<?php endif; ?>
|
||
</td>
|
||
<td class="text-nowrap">
|
||
<a href="<?= site_url('attendance/view/' . $studId) . '?code=' . urlencode($viCode) . $incidentParam ?>" class="btn btn-sm btn-info">
|
||
<i class="fas fa-eye"></i> Details
|
||
</a>
|
||
|
||
<?php if ($action === 'auto_email'): ?>
|
||
<a href="<?= site_url(
|
||
'attendance/compose?student_id=' . $studId .
|
||
'&code=' . urlencode($viCode) .
|
||
'&variant=default' .
|
||
$incidentParam
|
||
) ?>" class="btn btn-sm btn-primary ms-1">
|
||
<i class="fas fa-paper-plane"></i> Compose Auto Email
|
||
</a>
|
||
|
||
<?php else: ?>
|
||
<div class="btn-group ms-1">
|
||
<a href="<?= site_url(
|
||
'attendance/compose?student_id=' . $studId .
|
||
'&code=' . urlencode($viCode) .
|
||
'&variant=answered' .
|
||
$incidentParam
|
||
) ?>" class="btn btn-sm btn-success">
|
||
<i class="fas fa-phone-alt"></i> Compose (Answered)
|
||
</a>
|
||
|
||
<a href="<?= site_url(
|
||
'attendance/compose?student_id=' . $studId .
|
||
'&code=' . urlencode($viCode) .
|
||
'&variant=no_answer' .
|
||
$incidentParam
|
||
) ?>" class="btn btn-sm btn-warning">
|
||
<i class="fas fa-voicemail"></i> Compose (No Answer)
|
||
</a>
|
||
</div>
|
||
<?php endif; ?>
|
||
</td>
|
||
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- Hidden POST for notify (kept for batch CSRF or future single-row actions) -->
|
||
<form id="notifyForm" action="<?= site_url('attendance/record') ?>" method="post" class="d-none">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="student_id">
|
||
<input type="hidden" name="date">
|
||
<input type="hidden" name="subject_type">
|
||
<input type="hidden" name="parent_email">
|
||
<input type="hidden" name="parent_name">
|
||
<input type="hidden" name="semester" value="<?= esc($semester) ?>">
|
||
<input type="hidden" name="school_year" value="<?= esc($school_year) ?>">
|
||
<input type="hidden" name="return_url" value="<?= esc(current_url()) ?>">
|
||
</form>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<?= $this->endSection() ?>
|
||
|
||
<?= $this->section('scripts') ?>
|
||
<script>
|
||
(function() {
|
||
// Basic HTML escape util
|
||
function esc(s) {
|
||
return String(s ?? '')
|
||
.replaceAll('&', '&').replaceAll('<', '<')
|
||
.replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", "'");
|
||
}
|
||
|
||
function parentCard(p, label) {
|
||
if (!p) return '';
|
||
const name = esc(p.name || '');
|
||
const email = esc(p.email || '');
|
||
const phone = esc(p.phone || '');
|
||
return `
|
||
<div class="col-12 col-md-6">
|
||
<div class="card shadow-sm mb-3">
|
||
<div class="card-header d-flex justify-content-between align-items-center">
|
||
<strong>${esc(label)} Parent</strong>
|
||
${email ? `<a class="btn btn-sm btn-outline-primary" href="mailto:${email}">
|
||
<i class="fas fa-envelope"></i> Email</a>` : ''}
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="mb-1"><span class="text-muted">Name:</span> ${name || '—'}</div>
|
||
<div class="mb-1"><span class="text-muted">Email:</span> ${email || '—'}</div>
|
||
<div class="mb-1"><span class="text-muted">Phone:</span> ${phone || '—'}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function renderParentsModal(data) {
|
||
const body = document.getElementById('parentModalBody');
|
||
if (!data || (!data.primary && !data.secondary)) {
|
||
body.innerHTML = `<div class="alert alert-warning mb-0">No parent information available for this student.</div>`;
|
||
return;
|
||
}
|
||
const primary = data.primary ? {
|
||
name: (data.primary.firstname ? `${data.primary.firstname} ${data.primary.lastname||''}`.trim() : (data.primary.name || '')),
|
||
email: data.primary.email || '',
|
||
phone: data.primary.cellphone || data.primary.phone || ''
|
||
} : null;
|
||
|
||
const secondary = data.secondary ? {
|
||
name: (data.secondary.firstname ? `${data.secondary.firstname} ${data.secondary.lastname||''}`.trim() : (data.secondary.name || '')),
|
||
email: data.secondary.email || '',
|
||
phone: data.secondary.cellphone || data.secondary.phone || ''
|
||
} : null;
|
||
|
||
body.innerHTML = `<div class="row">${primary ? parentCard(primary, 'First') : ''}${secondary ? parentCard(secondary, 'Second') : ''}</div>`;
|
||
}
|
||
|
||
// Parent modal open/fetch
|
||
document.addEventListener('click', function(e) {
|
||
const parentBtn = e.target.closest('.show-parents');
|
||
if (!parentBtn) return;
|
||
|
||
e.preventDefault();
|
||
|
||
const sid = parentBtn.dataset.studentId || '';
|
||
const modalEl = document.getElementById('parentModal');
|
||
const bodyEl = document.getElementById('parentModalBody');
|
||
|
||
if (!sid) {
|
||
if (bodyEl) bodyEl.innerHTML = '<div class="alert alert-warning mb-0">Missing student id for this row.</div>';
|
||
if (window.bootstrap) new bootstrap.Modal(modalEl).show();
|
||
return;
|
||
}
|
||
|
||
bodyEl.innerHTML = `
|
||
<div class="text-center text-muted my-4">
|
||
<div class="spinner-border" role="status" aria-hidden="true"></div>
|
||
<div class="mt-2">Loading parent info…</div>
|
||
</div>`;
|
||
|
||
if (window.bootstrap) new bootstrap.Modal(modalEl).show();
|
||
|
||
let url = parentBtn.dataset.fetchUrl;
|
||
if (!url || !url.includes('student_id=')) {
|
||
url = '<?= site_url('attendance/parents-info') ?>?student_id=' + encodeURIComponent(sid);
|
||
}
|
||
|
||
fetch(url, {
|
||
headers: {
|
||
'X-Requested-With': 'XMLHttpRequest'
|
||
}
|
||
})
|
||
.then(async (r) => {
|
||
let data = null;
|
||
try {
|
||
data = await r.json();
|
||
} catch (e) {}
|
||
if (!r.ok) throw new Error((data && data.error) ? data.error : ('HTTP ' + r.status));
|
||
return data;
|
||
})
|
||
.then((data) => renderParentsModal(data))
|
||
.catch((err) => {
|
||
bodyEl.innerHTML = '<div class="alert alert-danger mb-0">Unable to load parent info: ' + esc(err?.message || err) + '</div>';
|
||
});
|
||
});
|
||
|
||
// ===== Helpers for FixedHeader =====
|
||
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;
|
||
}
|
||
|
||
// ===== DataTables init (with FixedHeader) =====
|
||
function initDataTable() {
|
||
if (!window.jQuery || !jQuery.fn || !jQuery.fn.DataTable) return false;
|
||
|
||
const $ = jQuery;
|
||
const table = $('#violationsTable');
|
||
|
||
if (!table.length) return true;
|
||
|
||
// Column indexes:
|
||
// 0 Student, 1 Grade, 2 Violation, 3 Type, 4 Abs, 5 Late, 6 Last Incident,
|
||
// 7 Action, 8 Notified?, 9 #, 10 Parent, 11 Actions
|
||
table.DataTable({
|
||
paging: true,
|
||
pageLength: 25,
|
||
lengthMenu: [
|
||
[10, 25, 50, 100, -1],
|
||
[10, 25, 50, 100, 'All']
|
||
],
|
||
ordering: true,
|
||
order: [
|
||
[6, 'desc'],
|
||
[0, 'asc']
|
||
], // Last Incident desc, then Student asc
|
||
searching: true,
|
||
info: true,
|
||
responsive: true,
|
||
autoWidth: false,
|
||
columnDefs: [{
|
||
targets: [4, 5, 9],
|
||
className: 'text-center'
|
||
},
|
||
{
|
||
targets: [7, 8, 9, 10, 11],
|
||
orderable: false
|
||
}, // actions / meta not meaningful to sort
|
||
{
|
||
targets: [7, 8, 9, 10, 11],
|
||
searchable: false
|
||
} // and not useful for search
|
||
],
|
||
// Keep the Bootstrap look & feel
|
||
dom: "<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>" +
|
||
"<'row'<'col-sm-12'tr>>" +
|
||
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
|
||
language: {
|
||
search: "Quick search:",
|
||
lengthMenu: "Show _MENU_ rows",
|
||
info: "Showing _START_–_END_ of _TOTAL_",
|
||
infoEmpty: "No rows to show",
|
||
zeroRecords: "No matching students found",
|
||
paginate: {
|
||
previous: "‹",
|
||
next: "›"
|
||
}
|
||
},
|
||
fixedHeader: {
|
||
header: true,
|
||
headerOffset: getFixedHeaderOffset()
|
||
}
|
||
});
|
||
|
||
return true;
|
||
}
|
||
|
||
// Ensure DT core + FixedHeader are present; then init
|
||
function ensureDataTablesThenInit() {
|
||
const head = document.head;
|
||
|
||
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;
|
||
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;
|
||
head.appendChild(l);
|
||
});
|
||
}
|
||
|
||
const hasDT = !!(window.jQuery && jQuery.fn && jQuery.fn.DataTable);
|
||
const hasFH = !!(hasDT && jQuery.fn.dataTable && jQuery.fn.dataTable.FixedHeader);
|
||
|
||
if (hasDT && hasFH) {
|
||
initDataTable();
|
||
return;
|
||
}
|
||
|
||
const tasks = [];
|
||
if (!hasDT) {
|
||
// Core + Bootstrap CSS
|
||
if (!window.jQuery) tasks.push(loadScript('https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js', 'jq-core'));
|
||
tasks.push(loadScript('https://cdn.jsdelivr.net/npm/datatables.net@1.13.10/js/jquery.dataTables.min.js', 'dt-core'));
|
||
tasks.push(loadScript('https://cdn.jsdelivr.net/npm/datatables.net-bs5@1.13.10/js/dataTables.bootstrap5.min.js', 'dt-bs5'));
|
||
tasks.push(loadCss('https://cdn.jsdelivr.net/npm/datatables.net-bs5@1.13.10/css/dataTables.bootstrap5.min.css', 'dt-bs5-css'));
|
||
}
|
||
if (!hasFH) {
|
||
tasks.push(loadScript('https://cdn.jsdelivr.net/npm/datatables.net-fixedheader@3.4.0/js/dataTables.fixedHeader.min.js', 'dt-fixedheader'));
|
||
tasks.push(loadCss('https://cdn.jsdelivr.net/npm/datatables.net-fixedheader-bs5@3.4.0/css/fixedHeader.bootstrap5.min.css', 'dt-fixedheader-css'));
|
||
}
|
||
|
||
Promise.all(tasks)
|
||
.then(() => setTimeout(initDataTable, 0))
|
||
.catch(() => console.warn('DataTables assets failed to load; table will render without enhancements.'));
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', ensureDataTablesThenInit);
|
||
})();
|
||
</script>
|
||
|
||
<!-- Parent Info Modal -->
|
||
<div class="modal fade" id="parentModal" tabindex="-1" aria-labelledby="parentModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="parentModalLabel">Parent Information</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div id="parentModalBody">
|
||
<div class="text-center text-muted my-4">
|
||
<div class="spinner-border" role="status" aria-hidden="true"></div>
|
||
<div class="mt-2">Loading parent info…</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer justify-content-between">
|
||
<div class="small text-muted">First & Second contacts are shown if available.</div>
|
||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php $cm = session('open_comment_modal'); ?>
|
||
<?php if ($cm): ?>
|
||
<!-- Add Comment/Note Modal -->
|
||
<div class="modal fade" id="notifyCommentModal" tabindex="-1" aria-labelledby="notifyCommentModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog">
|
||
<form method="post" action="<?= site_url('attendance/save-note') ?>" class="modal-content">
|
||
<?= csrf_field() ?>
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="notifyCommentModalLabel">Add Comment / Note</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<?php
|
||
$cmDateFmt = substr((string)($cm['incident_date'] ?? ''), 0, 10);
|
||
$cmName = (string)($cm['student_name'] ?? '');
|
||
if ($cmName === '' && !empty($cm['student_id'])) {
|
||
$cmName = 'Student #' . (int) $cm['student_id'];
|
||
}
|
||
?>
|
||
|
||
<div class="modal-body">
|
||
<div class="mb-2 small text-muted">
|
||
<div><strong>Student Name:</strong> <?= esc($cmName) ?></div>
|
||
<div><strong>Code:</strong> <?= esc($cm['code']) ?> <strong>Date:</strong> <?= esc($cmDateFmt) ?></div>
|
||
<?php if (!empty($cm['to'])): ?>
|
||
<div><strong>To:</strong> <?= esc($cm['to']) ?></div>
|
||
<?php endif; ?>
|
||
<?php if (!empty($cm['subject'])): ?>
|
||
<div><strong>Subject:</strong> <?= esc($cm['subject']) ?></div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="note" class="form-label">Comment / Note</label>
|
||
<textarea id="note" name="note" class="form-control" rows="5" required placeholder="Type any follow-up, call outcome, or special circumstances..."></textarea>
|
||
</div>
|
||
|
||
<!-- Hidden fields to carry context back -->
|
||
<input type="hidden" name="student_id" value="<?= esc($cm['student_id']) ?>">
|
||
<input type="hidden" name="code" value="<?= esc($cm['code']) ?>">
|
||
<input type="hidden" name="incident_date" value="<?= esc($cmDateFmt) ?>">
|
||
<input type="hidden" name="to" value="<?= esc($cm['to'] ?? '') ?>">
|
||
<input type="hidden" name="subject" value="<?= esc($cm['subject'] ?? '') ?>">
|
||
<input type="hidden" name="variant" value="<?= esc($cm['variant'] ?? '') ?>">
|
||
<input type="hidden" name="return_url" value="<?= current_url() ?>">
|
||
</div>
|
||
|
||
<div class="modal-footer">
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="bi bi-save"></i> Save Note
|
||
</button>
|
||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
var modalEl = document.getElementById('notifyCommentModal');
|
||
if (modalEl && window.bootstrap) {
|
||
new bootstrap.Modal(modalEl).show();
|
||
}
|
||
});
|
||
</script>
|
||
<?php endif; ?>
|
||
<?= $this->endSection() ?>
|