update code
This commit is contained in:
@@ -1,8 +1,21 @@
|
||||
<?= $this->extend('layout/management_layout') ?>
|
||||
<?= $this->section('content') ?>
|
||||
<style>
|
||||
#adminProgressTable thead th {
|
||||
.admin-progress-table thead th {
|
||||
position: static !important;
|
||||
top: auto !important;
|
||||
z-index: auto !important;
|
||||
background: #f8f9fa;
|
||||
white-space: nowrap;
|
||||
line-height: 1.2;
|
||||
padding-top: .75rem;
|
||||
padding-bottom: .75rem;
|
||||
}
|
||||
.admin-progress-table td {
|
||||
vertical-align: top;
|
||||
}
|
||||
.bg-orange {
|
||||
background-color: #fd7e14 !important;
|
||||
}
|
||||
</style>
|
||||
<div class="container py-4">
|
||||
@@ -56,79 +69,132 @@
|
||||
</form>
|
||||
|
||||
<div class="card shadow-sm mt-5">
|
||||
<div class="card-body pt-4 px-0">
|
||||
<div class="table-responsive px-4" style="margin-top: 1.5rem;">
|
||||
<table id="adminProgressTable" class="table table-hover align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Week</th>
|
||||
<th>Subjects</th>
|
||||
<th>Teacher</th>
|
||||
<th class="text-end">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<div class="card-body pt-4">
|
||||
<?php
|
||||
$reportGroupsBySection = $reportGroupsBySection ?? [];
|
||||
$subjectSections = $subjectSections ?? [];
|
||||
$classSections = $classSections ?? [];
|
||||
$sectionStats = $sectionStats ?? [];
|
||||
$expectedDays = (int) ($expectedDays ?? 0);
|
||||
?>
|
||||
<?php if (empty($classSections)): ?>
|
||||
<div class="text-center text-muted py-4">No class sections found.</div>
|
||||
<?php else: ?>
|
||||
<div class="accordion" id="adminProgressAccordion">
|
||||
<?php foreach ($classSections as $index => $cs): ?>
|
||||
<?php
|
||||
$reportGroups = $reportGroups ?? [];
|
||||
$subjectSections = $subjectSections ?? [];
|
||||
$sectionId = (int) ($cs['class_section_id'] ?? 0);
|
||||
$sectionName = $cs['class_section_name'] ?? 'Unknown Section';
|
||||
$sectionGroups = $reportGroupsBySection[$sectionId] ?? [];
|
||||
$hasReports = ! empty($sectionGroups);
|
||||
$collapseId = 'progress-section-' . $sectionId;
|
||||
$headingId = 'progress-heading-' . $sectionId;
|
||||
$stat = $sectionStats[$sectionId] ?? null;
|
||||
if (! $stat) {
|
||||
if ($expectedDays === 0) {
|
||||
$stat = [
|
||||
'submitted' => 0,
|
||||
'expected' => 0,
|
||||
'percent' => 0,
|
||||
'badgeClass' => 'bg-secondary',
|
||||
'labelClass' => 'text-muted',
|
||||
];
|
||||
} else {
|
||||
$stat = [
|
||||
'submitted' => 0,
|
||||
'expected' => $expectedDays,
|
||||
'percent' => 0,
|
||||
'badgeClass' => 'bg-danger',
|
||||
'labelClass' => 'text-danger',
|
||||
];
|
||||
}
|
||||
}
|
||||
$percentLabel = $expectedDays > 0 ? number_format((float) $stat['percent'], 1) . '%' : 'N/A';
|
||||
$submissionLabel = $expectedDays > 0
|
||||
? ('Submitted: ' . (int) $stat['submitted'] . ' / ' . (int) $expectedDays . ' (' . $percentLabel . ')')
|
||||
: 'Submitted: N/A';
|
||||
?>
|
||||
<?php if (empty($reportGroups)): ?>
|
||||
<tr><td colspan="4" class="text-center text-muted py-4">No reports found.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($reportGroups as $group): ?>
|
||||
<?php
|
||||
$weekLabel = $group['week_start'] ? date('M d, Y', strtotime($group['week_start'])) : '-';
|
||||
if (!empty($group['week_end'])) {
|
||||
$weekLabel .= ' – ' . date('M d, Y', strtotime($group['week_end']));
|
||||
}
|
||||
$reports = $group['reports'] ?? [];
|
||||
$teacherName = '';
|
||||
foreach ($reports as $report) {
|
||||
if (!empty($report['teacher_name'])) {
|
||||
$teacherName = $report['teacher_name'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$exampleId = $reports ? reset($reports)['id'] : null;
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fw-semibold"><?= esc($weekLabel) ?></div>
|
||||
<?php if (!empty($group['class_section_name'])): ?>
|
||||
<div class="text-muted small">Class: <?= esc($group['class_section_name']) ?></div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<?php foreach ($subjectSections as $slug => $section): ?>
|
||||
<?php
|
||||
$subjectName = $section['db_subject'] ?? $section['label'] ?? $slug;
|
||||
$report = $reports[$subjectName] ?? null;
|
||||
$statusTag = $report ? ($report['status_label'] ?? 'Unknown') : 'No entry';
|
||||
$badgeClass = $report ? 'bg-secondary' : 'bg-light text-muted';
|
||||
?>
|
||||
<div class="border rounded-3 p-2">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<strong class="small mb-0"><?= esc($section['label'] ?? $subjectName) ?></strong>
|
||||
<span class="badge <?= $badgeClass ?>"><?= esc($statusTag) ?></span>
|
||||
</div>
|
||||
<div class="small text-muted"><?= $report ? esc($report['unit_title'] ?: '-') : 'No submission' ?></div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<div class="accordion-item mb-2">
|
||||
<h2 class="accordion-header" id="<?= esc($headingId) ?>">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#<?= esc($collapseId) ?>" aria-expanded="false" aria-controls="<?= esc($collapseId) ?>">
|
||||
<?= esc($sectionName) ?>
|
||||
<span class="badge <?= esc($stat['badgeClass']) ?> ms-2"><?= esc($submissionLabel) ?></span>
|
||||
<?php if ($hasReports): ?>
|
||||
<span class="badge bg-secondary ms-2"><?= count($sectionGroups) ?> weeks</span>
|
||||
<?php endif; ?>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="<?= esc($collapseId) ?>" class="accordion-collapse collapse" aria-labelledby="<?= esc($headingId) ?>" data-bs-parent="#adminProgressAccordion">
|
||||
<div class="accordion-body">
|
||||
<?php if (! $hasReports): ?>
|
||||
<div class="text-muted">No reports found for this section.</div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0 admin-progress-table no-mgmt-sticky" data-no-mgmt-sticky>
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Week</th>
|
||||
<th>Subjects</th>
|
||||
<th>Teacher</th>
|
||||
<th class="text-end">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($sectionGroups as $group): ?>
|
||||
<?php
|
||||
$weekLabel = $group['week_start'] ? date('M d, Y', strtotime($group['week_start'])) : '-';
|
||||
if (! empty($group['week_end'])) {
|
||||
$weekLabel .= ' – ' . date('M d, Y', strtotime($group['week_end']));
|
||||
}
|
||||
$reports = $group['reports'] ?? [];
|
||||
$teacherName = '';
|
||||
foreach ($reports as $report) {
|
||||
if (! empty($report['teacher_name'])) {
|
||||
$teacherName = $report['teacher_name'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$exampleId = $reports ? reset($reports)['id'] : null;
|
||||
?>
|
||||
<tr>
|
||||
<td><div class="fw-semibold"><?= esc($weekLabel) ?></div></td>
|
||||
<td>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<?php foreach ($subjectSections as $slug => $section): ?>
|
||||
<?php
|
||||
$subjectName = $section['db_subject'] ?? $section['label'] ?? $slug;
|
||||
$report = $reports[$subjectName] ?? null;
|
||||
$statusTag = $report ? ($report['status_label'] ?? 'Unknown') : 'No entry';
|
||||
$badgeClass = $report ? 'bg-secondary' : 'bg-light text-muted';
|
||||
?>
|
||||
<div class="border rounded-3 p-2">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<strong class="small mb-0"><?= esc($section['label'] ?? $subjectName) ?></strong>
|
||||
<span class="badge <?= $badgeClass ?>"><?= esc($statusTag) ?></span>
|
||||
</div>
|
||||
<div class="small text-muted"><?= $report ? esc($report['unit_title'] ?: '-') : 'No submission' ?></div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</td>
|
||||
<td><?= esc($teacherName ?: '-') ?></td>
|
||||
<td class="text-end">
|
||||
<?php if ($exampleId): ?>
|
||||
<a class="btn btn-sm btn-outline-primary" href="<?= base_url('admin/progress/view/' . $exampleId) ?>">View</a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
<td><?= esc($teacherName ?: '-') ?></td>
|
||||
<td class="text-end">
|
||||
<?php if ($exampleId): ?>
|
||||
<a class="btn btn-sm btn-outline-primary" href="<?= base_url('admin/progress/view/' . $exampleId) ?>">View</a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -122,15 +122,40 @@
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<?php
|
||||
$cutoffDate = '';
|
||||
$cutoffTime = '';
|
||||
$cutoffThreshold = (string) ($cutoffThreshold ?? '09:00');
|
||||
$cutoffTzAbbr = '';
|
||||
try {
|
||||
$tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone());
|
||||
$tz = new DateTimeZone($tzName ?: 'UTC');
|
||||
$nowTz = new DateTime('now', $tz);
|
||||
$cutoffDate = $nowTz->format('Y-m-d');
|
||||
$cutoffTime = $nowTz->format('H:i');
|
||||
$cutoffTzAbbr = $nowTz->format('T');
|
||||
} catch (\Throwable $e) {
|
||||
$cutoffDate = '';
|
||||
$cutoffTime = '';
|
||||
$cutoffTzAbbr = '';
|
||||
}
|
||||
?>
|
||||
<form id="reportForm"
|
||||
method="post"
|
||||
action="<?= site_url('parent/report-attendance') ?>"
|
||||
data-csrf-name="<?= esc(csrf_token()) ?>"
|
||||
data-csrf-value="<?= esc(csrf_hash()) ?>"
|
||||
data-check-url="<?= site_url('api/parent/report-attendance/check') ?>">
|
||||
data-check-url="<?= site_url('api/parent/report-attendance/check') ?>"
|
||||
data-cutoff-date="<?= esc($cutoffDate) ?>"
|
||||
data-cutoff-time="<?= esc($cutoffTime) ?>"
|
||||
data-cutoff-threshold="<?= esc($cutoffThreshold) ?>"
|
||||
data-cutoff-blocked="0">
|
||||
<input type="hidden" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>">
|
||||
|
||||
<div id="clientCheckAlert" class="alert d-none" role="alert"></div>
|
||||
<div id="cutoffAlert" class="alert alert-warning d-none" role="alert">
|
||||
Reporting closes at <?= esc($cutoffThreshold) ?><?= $cutoffTzAbbr !== '' ? ' ' . esc($cutoffTzAbbr) : '' ?> on the report date.
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">Select Child(ren)</label>
|
||||
@@ -352,7 +377,10 @@
|
||||
const dates = selectedDates();
|
||||
if (!checkUrl || !dateSel || !typeSel) return;
|
||||
if (!ids.length || !dates.length) {
|
||||
if (submitBtn) submitBtn.disabled = false;
|
||||
if (submitBtn) {
|
||||
const cutoffBlocked = form.getAttribute('data-cutoff-blocked') === '1';
|
||||
submitBtn.disabled = cutoffBlocked;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -395,7 +423,10 @@
|
||||
}
|
||||
|
||||
if (!data || !data.ok) {
|
||||
if (submitBtn) submitBtn.disabled = false;
|
||||
if (submitBtn) {
|
||||
const cutoffBlocked = form.getAttribute('data-cutoff-blocked') === '1';
|
||||
submitBtn.disabled = cutoffBlocked;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -454,10 +485,16 @@
|
||||
setAlert('', '');
|
||||
}
|
||||
|
||||
if (submitBtn) submitBtn.disabled = (selectedIds().length === 0);
|
||||
if (submitBtn) {
|
||||
const cutoffBlocked = form.getAttribute('data-cutoff-blocked') === '1';
|
||||
submitBtn.disabled = cutoffBlocked || (selectedIds().length === 0);
|
||||
}
|
||||
} catch (e) {
|
||||
setAlert('warn', 'Could not verify submissions right now. You may proceed; server will validate.');
|
||||
if (submitBtn) submitBtn.disabled = false;
|
||||
if (submitBtn) {
|
||||
const cutoffBlocked = form.getAttribute('data-cutoff-blocked') === '1';
|
||||
submitBtn.disabled = cutoffBlocked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,4 +509,55 @@
|
||||
runCheck();
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
// Cutoff enforcement helper (9:00 AM on the selected Sunday).
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const form = document.getElementById('reportForm');
|
||||
if (!form) return;
|
||||
const dateSel = form.querySelector('select[name="dates[]"]');
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
const cutoffAlert = document.getElementById('cutoffAlert');
|
||||
const cutoffDate = form.getAttribute('data-cutoff-date') || '';
|
||||
const cutoffTime = form.getAttribute('data-cutoff-time') || '';
|
||||
const cutoffThreshold = form.getAttribute('data-cutoff-threshold') || '09:00';
|
||||
|
||||
function parseTimeToMinutes(val) {
|
||||
const parts = (val || '').split(':');
|
||||
if (parts.length < 2) return null;
|
||||
const hh = parseInt(parts[0], 10);
|
||||
const mm = parseInt(parts[1], 10);
|
||||
if (Number.isNaN(hh) || Number.isNaN(mm)) return null;
|
||||
return (hh * 60) + mm;
|
||||
}
|
||||
|
||||
function updateCutoffState() {
|
||||
if (!dateSel || !cutoffDate || !cutoffTime) return;
|
||||
const dates = Array.from(dateSel.selectedOptions || [])
|
||||
.map(opt => opt.value)
|
||||
.filter(v => v);
|
||||
const nowMinutes = parseTimeToMinutes(cutoffTime);
|
||||
const cutoffMinutes = parseTimeToMinutes(cutoffThreshold);
|
||||
const isTodaySelected = dates.includes(cutoffDate);
|
||||
const blocked = isTodaySelected && nowMinutes !== null && cutoffMinutes !== null && nowMinutes >= cutoffMinutes;
|
||||
|
||||
form.setAttribute('data-cutoff-blocked', blocked ? '1' : '0');
|
||||
if (cutoffAlert) {
|
||||
cutoffAlert.classList.toggle('d-none', !blocked);
|
||||
}
|
||||
if (submitBtn && blocked) {
|
||||
submitBtn.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
form.addEventListener('change', function(e) {
|
||||
const tgt = e.target;
|
||||
if (!tgt) return;
|
||||
if (tgt.matches('select[name="dates[]"], input[name="student_ids[]"], #reportType')) {
|
||||
updateCutoffState();
|
||||
}
|
||||
});
|
||||
|
||||
updateCutoffState();
|
||||
});
|
||||
</script>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="container py-4">
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<div>
|
||||
<h3 class="mb-0">My Progress Reports</h3>
|
||||
<h3 class="mb-0">Class Progress Reports</h3>
|
||||
<div class="text-muted">Review weekly submissions and compare Islamic Studies with Quran/Arabic.</div>
|
||||
</div>
|
||||
<a href="<?= base_url('teacher/progress/submit') ?>" class="btn btn-outline-secondary">Submit New Report</a>
|
||||
@@ -78,6 +78,11 @@
|
||||
<div class="small text-muted">
|
||||
<?= $report ? esc($report['unit_title'] ?: '-') : 'No submission' ?>
|
||||
</div>
|
||||
<?php if ($report): ?>
|
||||
<div class="small text-muted">
|
||||
Submitted by: <?= esc(trim((string) ($report['teacher_name'] ?? '')) ?: 'Unknown') ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
@@ -175,10 +175,13 @@
|
||||
<?php for ($i = 0; $i < $rowsCount; $i++): ?>
|
||||
<div class="row g-2 align-items-end mb-2 unit-chapter-entry">
|
||||
<div class="col">
|
||||
<input type="text" name="unit_<?= esc($slug) ?>[]" class="form-control form-control-sm" placeholder="Unit name" value="<?= esc($unitValues[$i] ?? '') ?>">
|
||||
<input type="text" name="unit_<?= esc($slug) ?>[]" class="form-control form-control-sm" placeholder="Unit name" value="<?= esc($unitValues[$i] ?? '') ?>" readonly>
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type="text" name="chapter_<?= esc($slug) ?>[]" class="form-control form-control-sm" placeholder="Chapter" value="<?= esc($chapterValues[$i] ?? '') ?>">
|
||||
<input type="text" name="chapter_<?= esc($slug) ?>[]" class="form-control form-control-sm" placeholder="Chapter" value="<?= esc($chapterValues[$i] ?? '') ?>" readonly>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" data-remove-unit-chapter aria-label="Remove entry">X</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endfor; ?>
|
||||
@@ -263,10 +266,13 @@
|
||||
row.className = 'row g-2 align-items-end mb-2 unit-chapter-entry';
|
||||
row.innerHTML = `
|
||||
<div class="col">
|
||||
<input type="text" name="unit_${slug}[]" class="form-control form-control-sm" placeholder="Unit name" />
|
||||
<input type="text" name="unit_${slug}[]" class="form-control form-control-sm" placeholder="Unit name" readonly />
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type="text" name="chapter_${slug}[]" class="form-control form-control-sm" placeholder="Chapter" />
|
||||
<input type="text" name="chapter_${slug}[]" class="form-control form-control-sm" placeholder="Chapter" readonly />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" data-remove-unit-chapter aria-label="Remove entry">X</button>
|
||||
</div>
|
||||
`;
|
||||
const inputs = row.querySelectorAll('input');
|
||||
@@ -338,6 +344,15 @@
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('[data-remove-unit-chapter]');
|
||||
if (!button) return;
|
||||
const row = button.closest('.unit-chapter-entry');
|
||||
if (row) {
|
||||
row.remove();
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-custom-entry]').forEach(button => {
|
||||
button.addEventListener('click', event => {
|
||||
event.stopPropagation();
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
<?php if (! $report): ?>
|
||||
<div class="text-muted">No entry submitted for this subject this week.</div>
|
||||
<?php else: ?>
|
||||
<div class="mb-2"><strong>Submitted by:</strong> <?= esc(trim((string) ($report['teacher_name'] ?? '')) ?: 'Unknown') ?></div>
|
||||
<div class="mb-2"><strong><?= $unitLabel ?>:</strong> <?= esc($report['unit_title'] ?: '-') ?></div>
|
||||
<div class="mb-3"><strong>Activities</strong><div class="mt-1"><?= nl2br(esc($report['covered'] ?? '-')) ?></div></div>
|
||||
<div class="mb-3">
|
||||
|
||||
Reference in New Issue
Block a user