Files
alrahma_sunday_school/app/Views/grading/homework_tracking.php
T
2026-02-10 22:11:06 -05:00

231 lines
12 KiB
PHP

<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<?php
// Helpers for labels
$labelFor = function (int $cid): string {
if ($cid === 13) return 'KG';
if ($cid === 12) return 'Youth';
if ($cid >= 1 && $cid <= 11) return 'Grade ' . $cid;
return 'Class ' . $cid;
};
// Pre-format header dates for display
$headerDates = array_map(function ($ymd) {
$dt = \DateTime::createFromFormat('Y-m-d', $ymd, new \DateTimeZone('UTC'));
return $dt ? $dt->format('m-d-Y') : $ymd;
}, $sundays ?? []);
$todayYmd = local_date(utc_now(), 'Y-m-d');
?>
<div class="container-fluid py-3">
<div class="px-3">
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
<h2 class="text-center mt-4 mb-3">Homework Tracking</h2>
<div class="text-muted"><?= esc(ucfirst($semester ?? '')) ?> • <?= esc($schoolYear ?? '') ?></div>
</div>
<div class="mb-2 small">
<span class="badge text-bg-success">Entered</span>
<span class="badge text-bg-danger">Missing</span>
<span class="badge text-bg-warning">No School</span>
<span class="badge bg-future text-dark border">Future</span>
</div>
<?php if (empty($teachers)): ?>
<div class="alert alert-light border">No teacher assignments found for the current term.</div>
<?php else: ?>
<?php
$page = max(1, (int)($page ?? 1));
$totalPages = max(1, (int)($totalPages ?? 1));
$baseUrl = base_url('grading/homework-tracking');
$mkHref = function($p) use ($baseUrl) { return $baseUrl . '?page=' . (int)$p; };
?>
<div class="d-flex justify-content-end mb-2 gap-2">
<div class="btn-group" role="group" aria-label="Pagination top">
<a class="btn btn-outline-secondary <?= $page <= 1 ? 'disabled' : '' ?>" href="<?= $mkHref(1) ?>">First</a>
<a class="btn btn-outline-secondary <?= $page <= 1 ? 'disabled' : '' ?>" href="<?= $mkHref(max(1, $page-1)) ?>">Prev</a>
<span class="btn btn-outline-secondary disabled">Page <?= $page ?> / <?= $totalPages ?></span>
<a class="btn btn-outline-secondary <?= $page >= $totalPages ? 'disabled' : '' ?>" href="<?= $mkHref(min($totalPages, $page+1)) ?>">Next</a>
<a class="btn btn-outline-secondary <?= $page >= $totalPages ? 'disabled' : '' ?>" href="<?= $mkHref($totalPages) ?>">Last</a>
</div>
</div>
<?= $this->section('styles') ?>
<style>
/* Header is handled by JS polyfill to avoid covering content; keep a light bg */
.table-sticky thead th { background: var(--bs-light, #f8f9fa) !important; }
.table-sticky .sticky-col { position: sticky; left: 0; z-index: 5; background: var(--bs-table-bg, #fff); }
/* Use a CSS variable for the second sticky column's left offset so it can adapt to content width */
.table-sticky .sticky-col-2 { position: sticky; left: var(--sticky-left-2, 240px); z-index: 5; background: var(--bs-table-bg, #fff); }
.table-sticky th.sticky-col, .table-sticky th.sticky-col-2 { z-index: 8; }
.table-sticky td, .table-sticky th { white-space: nowrap; }
.bg-future { background-color: #e7f1ff !important; }
</style>
<?= $this->endSection() ?>
<div class="table-responsive">
<table id="homeworkTrackTable" class="table table-bordered align-middle table-sticky no-mgmt-sticky">
<thead class="table-light">
<tr>
<th class="sticky-col">Grade</th>
<th class="sticky-col-2" style="min-width:300px; width:300px;">Teacher &amp; TAs</th>
<?php foreach (($headerDates ?? []) as $i => $label): $ymd = $sundays[$i] ?? ''; ?>
<?php $isCal = !empty($eventDays[$ymd]); $isFuture = ($ymd > $todayYmd); ?>
<th class="text-center <?= $isCal ? 'bg-warning' : ($isFuture ? 'bg-future' : '') ?>" title="<?= esc($ymd) ?>"><?= esc($label) ?></th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php foreach ($teachers as $row): ?>
<?php
$cid = (int)($row['class_id'] ?? 0);
$csid = (int)($row['class_section_id'] ?? 0);
$cname = (string)($row['class_section_name'] ?? '');
$teacherNames = (array)($row['teachers'] ?? []);
$taNames = (array)($row['tas'] ?? []);
?>
<tr>
<td class="sticky-col" style="background: var(--bs-table-bg, #fff);">
<?= esc($labelFor($cid)) ?>
<?php if (!empty($cname)): ?>
<div class="text-muted small">Section: <?= esc($cname) ?></div>
<?php endif; ?>
</td>
<td class="sticky-col-2" style="min-width:300px; width:300px; background: var(--bs-table-bg, #fff);">
<?php if (!empty($teacherNames)): ?>
<div><strong>Teacher<?= count($teacherNames) > 1 ? 's' : '' ?>:</strong> <?= esc(implode(', ', $teacherNames)) ?></div>
<?php else: ?>
<div><strong>Teacher:</strong> N/A</div>
<?php endif; ?>
<div><strong>TA<?= count($taNames) !== 1 ? 's' : '' ?>:</strong> <?= !empty($taNames) ? esc(implode(', ', $taNames)) : '—' ?></div>
</td>
<?php foreach (($sundays ?? []) as $ymd): ?>
<?php if (!empty($eventDays[$ymd])): ?>
<td class="bg-warning text-center">—</td>
<?php else: ?>
<?php $isFuture = ($ymd > $todayYmd); ?>
<?php if ($isFuture): ?>
<td class="text-center bg-future">—</td>
<?php else: ?>
<?php $ok = !empty($hasHomeworkByDate[$csid][$ymd] ?? null); ?>
<?php if ($ok): ?>
<?php
$rawDate = $hwEnteredAtByDate[$csid][$ymd] ?? '';
$disp = '';
if ($rawDate) { $disp = local_date($rawDate, 'm-d-Y'); }
?>
<td class="text-center text-white" style="background-color:#198754;"><?= esc($disp ?: '✓') ?></td>
<?php else: ?>
<td class="text-center text-white" style="background-color:#dc3545;">✖</td>
<?php endif; ?>
<?php endif; ?>
<?php endif; ?>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?= $this->section('scripts') ?>
<script>
(function(){
function updateStickyOffsets(){
document.querySelectorAll('table.table-sticky').forEach(function(tbl){
const firstHeader = tbl.querySelector('thead th.sticky-col');
const firstCell = tbl.querySelector('tbody td.sticky-col');
const ref = firstHeader || firstCell;
if (!ref) return;
const width = ref.offsetWidth || ref.getBoundingClientRect().width || 240;
tbl.style.setProperty('--sticky-left-2', width + 'px');
});
}
window.addEventListener('load', updateStickyOffsets);
window.addEventListener('resize', updateStickyOffsets);
})();
</script>
<script>
// JS sticky header polyfill for homework tracking
(function(){
function getFixedHeaderOffset(){
var total = 0;
try {
var sels = ['header.navbar.sticky-top','header.navbar.fixed-top','#navbarManagement.navbar.sticky-top','#navbarManagement.navbar.fixed-top'];
var seen = [];
sels.forEach(function(s){ document.querySelectorAll(s).forEach(function(el){ if (seen.indexOf(el) === -1) seen.push(el); }); });
document.querySelectorAll('.navbar.sticky-top, .navbar.fixed-top').forEach(function(el){ if (seen.indexOf(el) === -1) seen.push(el); });
seen.forEach(function(el){ var h = el.offsetHeight || (el.getBoundingClientRect && el.getBoundingClientRect().height) || 0; total += Math.max(0, Math.round(h)); });
} catch(_) {}
return total;
}
function installStickyPolyfill(tbl){
if (!tbl || tbl.dataset.stickyPolyfill === '1') return;
var thead = tbl.tHead; if (!thead) return;
var cloneTable = document.createElement('table');
cloneTable.className = tbl.className;
cloneTable.style.position = 'fixed';
cloneTable.style.top = getFixedHeaderOffset() + 'px';
cloneTable.style.left = '0px';
cloneTable.style.zIndex = '1045';
cloneTable.style.display = 'none';
cloneTable.style.background = 'var(--bs-light, #f8f9fa)';
cloneTable.style.borderCollapse = 'separate';
var cloneThead = thead.cloneNode(true);
cloneTable.appendChild(cloneThead);
document.body.appendChild(cloneTable);
function syncSizes(){
var rect = tbl.getBoundingClientRect();
var width = Math.round(rect.width);
cloneTable.style.width = width + 'px';
var origThs = thead.querySelectorAll('tr:first-child th');
var cloneThs = cloneThead.querySelectorAll('tr:first-child th');
for (var i=0; i<cloneThs.length; i++){
var w = (origThs[i] && origThs[i].getBoundingClientRect().width) || 0;
cloneThs[i].style.width = Math.round(w) + 'px';
}
}
function update(){
var rect = tbl.getBoundingClientRect();
var off = getFixedHeaderOffset();
var headH = (thead.getBoundingClientRect && thead.getBoundingClientRect().height) || 40;
if (rect.top < off && rect.bottom > off + headH){
syncSizes();
cloneTable.style.left = Math.round(rect.left) + 'px';
cloneTable.style.top = off + 'px';
cloneTable.style.display = '';
} else {
cloneTable.style.display = 'none';
}
}
window.addEventListener('scroll', update, { passive: true });
window.addEventListener('resize', function(){ syncSizes(); update(); });
setTimeout(function(){ syncSizes(); update(); }, 300);
tbl.dataset.stickyPolyfill = '1';
}
document.addEventListener('DOMContentLoaded', function(){
var tbl = document.getElementById('homeworkTrackTable');
if (tbl) installStickyPolyfill(tbl);
});
})();
</script>
<?= $this->endSection() ?>
<div class="d-flex justify-content-end mt-2 gap-2">
<div class="btn-group" role="group" aria-label="Pagination bottom">
<a class="btn btn-outline-secondary <?= $page <= 1 ? 'disabled' : '' ?>" href="<?= $mkHref(1) ?>">First</a>
<a class="btn btn-outline-secondary <?= $page <= 1 ? 'disabled' : '' ?>" href="<?= $mkHref(max(1, $page-1)) ?>">Prev</a>
<span class="btn btn-outline-secondary disabled">Page <?= $page ?> / <?= $totalPages ?></span>
<a class="btn btn-outline-secondary <?= $page >= $totalPages ? 'disabled' : '' ?>" href="<?= $mkHref(min($totalPages, $page+1)) ?>">Next</a>
<a class="btn btn-outline-secondary <?= $page >= $totalPages ? 'disabled' : '' ?>" href="<?= $mkHref($totalPages) ?>">Last</a>
</div>
</div>
<?php endif; ?>
</div>
<div class="mt-2 small text-muted">Dates are Sundays within the current school term. Yellow indicates a No School day; light blue indicates a future Sunday.</div>
<div class="small text-muted">Homework indices are mapped in order to non-event Sundays.</div>
</div>
<?= $this->endSection() ?>