fix below 60 pages
This commit is contained in:
@@ -1070,14 +1070,15 @@ class GradingController extends Controller
|
||||
|
||||
public function belowSixty()
|
||||
{
|
||||
$configuredSemester = (string) $this->semester;
|
||||
$configuredYear = (string) $this->schoolYear;
|
||||
|
||||
$requestedSemester = trim((string)($this->request->getGet('semester') ?? ''));
|
||||
$requestedSemester = strtolower(trim((string)($this->request->getGet('semester') ?? '')));
|
||||
$requestedYear = trim((string)($this->request->getGet('school_year') ?? ''));
|
||||
|
||||
$isYearMode = (strtolower($requestedSemester) === 'year');
|
||||
$semester = $isYearMode ? 'year' : ($requestedSemester !== '' ? $requestedSemester : ($configuredSemester !== '' ? $configuredSemester : 'Fall'));
|
||||
// This page intentionally supports only Fall and Whole Year.
|
||||
// Spring is still used internally for the Whole Year calculation, but it is not selectable here.
|
||||
$isYearMode = ($requestedSemester === 'year');
|
||||
$semester = $isYearMode ? 'year' : 'Fall';
|
||||
$schoolYear = $requestedYear !== '' ? $requestedYear : $configuredYear;
|
||||
|
||||
$schoolYears = $this->getSchoolYearsForScores($schoolYear);
|
||||
@@ -1092,10 +1093,12 @@ class GradingController extends Controller
|
||||
'schoolYears' => $schoolYears,
|
||||
'canViewGrading' => $canViewGrading,
|
||||
'isYearMode' => $isYearMode,
|
||||
'semesterOptions' => ['Fall'],
|
||||
'showAllSemesterOption' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function editBelowSixtyEmail()
|
||||
{
|
||||
$studentId = (int)$this->request->getGet('student_id');
|
||||
@@ -1766,7 +1769,95 @@ class GradingController extends Controller
|
||||
private function fetchBelowSixtyRows(string $schoolYear, string $semester): array
|
||||
{
|
||||
$isYearMode = strtolower(trim($semester)) === 'year';
|
||||
$semesterKey = $isYearMode ? '' : strtolower(trim($semester));
|
||||
|
||||
if ($isYearMode) {
|
||||
$rows = $this->db->table('semester_scores ss')
|
||||
->select('s.id AS student_id')
|
||||
->select('s.school_id')
|
||||
->select('s.firstname')
|
||||
->select('s.lastname')
|
||||
->select('cs.class_section_name')
|
||||
->select("'year' AS semester", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.homework_avg END) AS fall_homework_avg", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.homework_avg END) AS spring_homework_avg", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.project_avg END) AS fall_project_avg", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.project_avg END) AS spring_project_avg", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.participation_score END) AS fall_participation_score", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.participation_score END) AS spring_participation_score", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN COALESCE(ss.test_avg, ss.quiz_avg) END) AS fall_test_avg", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN COALESCE(ss.test_avg, ss.quiz_avg) END) AS spring_test_avg", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.ptap_score END) AS fall_ptap_score", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.ptap_score END) AS spring_ptap_score", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.attendance_score END) AS fall_attendance_score", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.attendance_score END) AS spring_attendance_score", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.midterm_exam_score END) AS fall_midterm_exam_score", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.midterm_exam_score END) AS spring_midterm_exam_score", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.final_exam_score END) AS fall_final_exam_score", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.final_exam_score END) AS spring_final_exam_score", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.semester_score END) AS fall_score", false)
|
||||
->select("MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.semester_score END) AS spring_score", false)
|
||||
->select("(
|
||||
MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.semester_score END)
|
||||
+ MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.semester_score END)
|
||||
) / 2 AS semester_score", false)
|
||||
->select("(
|
||||
MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.homework_avg END)
|
||||
+ MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.homework_avg END)
|
||||
) / 2 AS homework_avg", false)
|
||||
->select("(
|
||||
MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.project_avg END)
|
||||
+ MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.project_avg END)
|
||||
) / 2 AS project_avg", false)
|
||||
->select("(
|
||||
MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.participation_score END)
|
||||
+ MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.participation_score END)
|
||||
) / 2 AS participation_score", false)
|
||||
->select("(
|
||||
MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN COALESCE(ss.test_avg, ss.quiz_avg) END)
|
||||
+ MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN COALESCE(ss.test_avg, ss.quiz_avg) END)
|
||||
) / 2 AS test_avg", false)
|
||||
->select("(
|
||||
MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.ptap_score END)
|
||||
+ MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.ptap_score END)
|
||||
) / 2 AS ptap_score", false)
|
||||
->select("(
|
||||
MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.attendance_score END)
|
||||
+ MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.attendance_score END)
|
||||
) / 2 AS attendance_score", false)
|
||||
->select("(
|
||||
MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.midterm_exam_score END)
|
||||
+ MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.midterm_exam_score END)
|
||||
) / 2 AS midterm_exam_score", false)
|
||||
->select("(
|
||||
MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'fall' THEN ss.final_exam_score END)
|
||||
+ MAX(CASE WHEN LOWER(TRIM(ss.semester)) = 'spring' THEN ss.final_exam_score END)
|
||||
) / 2 AS final_exam_score", false)
|
||||
->join('students s', 's.id = ss.student_id', 'inner')
|
||||
->join('classSection cs', 'cs.class_section_id = ss.class_section_id', 'left')
|
||||
->where('s.is_active', 1)
|
||||
->where('ss.school_year', $schoolYear)
|
||||
->where('ss.semester_score IS NOT NULL', null, false)
|
||||
->where("LOWER(TRIM(ss.semester)) IN ('fall', 'spring')", null, false)
|
||||
->groupBy('s.id, s.school_id, s.firstname, s.lastname, ss.class_section_id, cs.class_section_name')
|
||||
->having('fall_score IS NOT NULL', null, false)
|
||||
->having('spring_score IS NOT NULL', null, false)
|
||||
->having('semester_score <', 60)
|
||||
->orderBy('s.lastname', 'ASC')
|
||||
->orderBy('s.firstname', 'ASC')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
foreach ($rows as &$row) {
|
||||
$row['comment'] = '';
|
||||
$row['status'] = 'Open';
|
||||
$row['note'] = '';
|
||||
}
|
||||
unset($row);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
$semesterKey = 'fall';
|
||||
$builder = $this->db->table('semester_scores ss')
|
||||
->select([
|
||||
's.id AS student_id',
|
||||
@@ -1790,11 +1881,8 @@ class GradingController extends Controller
|
||||
->where('s.is_active', 1)
|
||||
->where('ss.school_year', $schoolYear)
|
||||
->where('ss.semester_score IS NOT NULL', null, false)
|
||||
->where('ss.semester_score <', 60);
|
||||
|
||||
if (!$isYearMode) {
|
||||
$builder->where("LOWER(TRIM(ss.semester))", $semesterKey);
|
||||
}
|
||||
->where('ss.semester_score <', 60)
|
||||
->where("LOWER(TRIM(ss.semester))", $semesterKey);
|
||||
|
||||
$rows = $builder
|
||||
->orderBy('s.lastname', 'ASC')
|
||||
@@ -1815,16 +1903,15 @@ class GradingController extends Controller
|
||||
|
||||
$commentMap = [];
|
||||
if (!empty($studentIds)) {
|
||||
$commentBuilder = $this->db->table('score_comments')
|
||||
$commentRows = $this->db->table('score_comments')
|
||||
->select('student_id, semester, comment, created_at')
|
||||
->where('score_type', 'general')
|
||||
->where('school_year', $schoolYear)
|
||||
->whereIn('student_id', $studentIds)
|
||||
->orderBy('created_at', 'DESC');
|
||||
if (!$isYearMode) {
|
||||
$commentBuilder->where("LOWER(TRIM(semester))", $semesterKey);
|
||||
}
|
||||
$commentRows = $commentBuilder->get()->getResultArray();
|
||||
->where("LOWER(TRIM(semester))", $semesterKey)
|
||||
->orderBy('created_at', 'DESC')
|
||||
->get()
|
||||
->getResultArray();
|
||||
foreach ($commentRows as $row) {
|
||||
$sid = (int)($row['student_id'] ?? 0);
|
||||
$sem = strtolower(trim((string)($row['semester'] ?? '')));
|
||||
@@ -1838,15 +1925,14 @@ class GradingController extends Controller
|
||||
$statusMap = [];
|
||||
$noteMap = [];
|
||||
if (!empty($studentIds)) {
|
||||
$flagBuilder = $this->db->table('current_flag')
|
||||
$flagRows = $this->db->table('current_flag')
|
||||
->select('student_id, semester, flag_state, open_description, close_description')
|
||||
->where('flag', 'grade')
|
||||
->where('school_year', $schoolYear)
|
||||
->whereIn('student_id', $studentIds);
|
||||
if (!$isYearMode) {
|
||||
$flagBuilder->where("LOWER(TRIM(semester))", $semesterKey);
|
||||
}
|
||||
$flagRows = $flagBuilder->get()->getResultArray();
|
||||
->whereIn('student_id', $studentIds)
|
||||
->where("LOWER(TRIM(semester))", $semesterKey)
|
||||
->get()
|
||||
->getResultArray();
|
||||
foreach ($flagRows as $row) {
|
||||
$sid = (int)($row['student_id'] ?? 0);
|
||||
if ($sid <= 0) continue;
|
||||
@@ -1884,6 +1970,7 @@ class GradingController extends Controller
|
||||
return $rows;
|
||||
}
|
||||
|
||||
|
||||
private function fetchBelowSixtyEmailRow(int $studentId, string $schoolYear, string $semester): array
|
||||
{
|
||||
$semesterKey = strtolower(trim($semester));
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
|
||||
<div class="text-muted">
|
||||
<?= !empty($isYearMode) ? 'Whole Year' : esc(ucfirst($semester ?? '')) ?> • <?= esc($schoolYear ?? '') ?>
|
||||
<?= !empty($isYearMode) ? 'Whole Year' : 'Fall' ?> • <?= esc($schoolYear ?? '') ?>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<?php if (empty($isYearMode)): ?>
|
||||
<a class="btn btn-outline-primary btn-sm"
|
||||
href="<?= site_url('grading/below-60/decisions?' . http_build_query(['semester' => $semester ?? '', 'school_year' => $schoolYear ?? ''])) ?>">
|
||||
href="<?= site_url('grading/below-60/decisions?' . http_build_query(['semester' => 'fall', 'school_year' => $schoolYear ?? ''])) ?>">
|
||||
Decisions
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
@@ -36,6 +36,10 @@
|
||||
}
|
||||
return esc($value);
|
||||
};
|
||||
|
||||
// Fall mode displays Fall semester_score.
|
||||
// Whole Year mode displays the controller-provided annual score: (fall_score + spring_score) / 2.
|
||||
$actionSemester = !empty($isYearMode) ? 'year' : 'fall';
|
||||
?>
|
||||
|
||||
<?php if (empty($rows)): ?>
|
||||
@@ -49,20 +53,10 @@
|
||||
<tr>
|
||||
<th>Student Name</th>
|
||||
<th>Section</th>
|
||||
<?php if (!empty($isYearMode)): ?><th>Semester</th><?php endif; ?>
|
||||
<th>Hwk Avg</th>
|
||||
<th>Project Avg</th>
|
||||
<th>Participation</th>
|
||||
<th>Test Avg</th>
|
||||
<th>PTAP Score</th>
|
||||
<th>Attendance</th>
|
||||
<th>Midterm Score</th>
|
||||
<th><?= !empty($isYearMode) ? 'Semester Score' : (strcasecmp($semester ?? '', 'fall') === 0 ? '1st Semester Score' : 'Semester Score') ?></th>
|
||||
<?php if (empty($isYearMode)): ?>
|
||||
<th class="text-center">Score</th>
|
||||
<th>Status</th>
|
||||
<th>Email Parent</th>
|
||||
<th>Schedule Meeting</th>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -79,29 +73,28 @@
|
||||
}
|
||||
}
|
||||
$studentName = trim((string)($row['firstname'] ?? '') . ' ' . (string)($row['lastname'] ?? ''));
|
||||
$studentLabel = $studentName !== '' ? $studentName : 'N/A';
|
||||
$isClosed = ($row['status'] ?? 'Open') === 'Closed';
|
||||
?>
|
||||
<?php $isClosed = ($row['status'] ?? 'Open') === 'Closed'; ?>
|
||||
<?php $rowSemester = ucfirst(strtolower(trim((string)($row['semester'] ?? ($semester ?? ''))))); ?>
|
||||
<tr class="<?= esc($scoreClass) ?>">
|
||||
<td><?= esc($studentName !== '' ? $studentName : 'N/A') ?></td>
|
||||
<td><?= esc($studentLabel) ?></td>
|
||||
<td><?= esc($row['class_section_name'] ?? '—') ?></td>
|
||||
<?php if (!empty($isYearMode)): ?>
|
||||
<td class="text-center"><?= esc($rowSemester) ?></td>
|
||||
<?php endif; ?>
|
||||
<td class="text-center"><?= $displayScore($row['homework_avg'] ?? null) ?></td>
|
||||
<td class="text-center"><?= $displayScore($row['project_avg'] ?? null) ?></td>
|
||||
<td class="text-center"><?= $displayScore($row['participation_score'] ?? null) ?></td>
|
||||
<td class="text-center"><?= $displayScore($row['test_avg'] ?? null) ?></td>
|
||||
<td class="text-center"><?= $displayScore($row['ptap_score'] ?? null) ?></td>
|
||||
<td class="text-center"><?= $displayScore($row['attendance_score'] ?? null) ?></td>
|
||||
<td class="text-center"><?= $displayScore($row['midterm_exam_score'] ?? null) ?></td>
|
||||
<td class="text-center"><?= $displayScore($row['semester_score'] ?? null) ?></td>
|
||||
<?php if (empty($isYearMode)): ?>
|
||||
<td class="text-center">
|
||||
<div class="fw-semibold"><?= $displayScore($scoreRaw) ?></div>
|
||||
<button type="button"
|
||||
class="btn btn-outline-secondary btn-xs mt-1 btn-show-details"
|
||||
style="font-size:0.72rem;padding:1px 7px;"
|
||||
data-student-id="<?= (int)($row['student_id'] ?? 0) ?>"
|
||||
data-student-name="<?= esc($studentLabel) ?>"
|
||||
data-school-year="<?= esc((string)($schoolYear ?? '')) ?>">
|
||||
Details
|
||||
</button>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<form method="post" action="<?= site_url('grading/below-60/status') ?>" class="d-flex align-items-center gap-2 justify-content-center">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="student_id" value="<?= esc((string)($row['student_id'] ?? '')) ?>">
|
||||
<input type="hidden" name="semester" value="<?= esc((string)($semester ?? '')) ?>">
|
||||
<input type="hidden" name="semester" value="<?= esc($actionSemester) ?>">
|
||||
<input type="hidden" name="school_year" value="<?= esc((string)($schoolYear ?? '')) ?>">
|
||||
<select name="status" class="form-select form-select-sm" style="width: 110px;">
|
||||
<option value="Open" <?= ($row['status'] ?? 'Open') === 'Open' ? 'selected' : '' ?>>Open</option>
|
||||
@@ -116,7 +109,7 @@
|
||||
<button type="button" class="btn btn-sm btn-secondary" disabled>Send Email</button>
|
||||
<?php else: ?>
|
||||
<a class="btn btn-sm btn-outline-primary"
|
||||
href="<?= site_url('grading/below-60/email/edit?student_id=' . (int)($row['student_id'] ?? 0) . '&semester=' . rawurlencode((string)($semester ?? '')) . '&school_year=' . rawurlencode((string)($schoolYear ?? ''))) ?>">
|
||||
href="<?= site_url('grading/below-60/email/edit?student_id=' . (int)($row['student_id'] ?? 0) . '&semester=' . rawurlencode($actionSemester) . '&school_year=' . rawurlencode((string)($schoolYear ?? ''))) ?>">
|
||||
Send Email
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
@@ -126,12 +119,11 @@
|
||||
<button class="btn btn-sm btn-secondary" disabled>Schedule</button>
|
||||
<?php else: ?>
|
||||
<a class="btn btn-sm btn-outline-secondary"
|
||||
href="<?= site_url('grading/below-60/schedule?student_id=' . (int)($row['student_id'] ?? 0) . '&semester=' . rawurlencode((string)($semester ?? '')) . '&school_year=' . rawurlencode((string)($schoolYear ?? ''))) ?>">
|
||||
href="<?= site_url('grading/below-60/schedule?student_id=' . (int)($row['student_id'] ?? 0) . '&semester=' . rawurlencode($actionSemester) . '&school_year=' . rawurlencode((string)($schoolYear ?? ''))) ?>">
|
||||
Schedule
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
@@ -141,6 +133,26 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Score details modal -->
|
||||
<div class="modal fade" id="detailsModal" tabindex="-1" aria-labelledby="detailsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="detailsModalLabel">Score Details</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="detailsModalBody">
|
||||
<div class="text-center py-4">
|
||||
<div class="spinner-border text-primary" role="status"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('scripts') ?>
|
||||
@@ -150,22 +162,176 @@
|
||||
.below-sixty-wrapper { padding-top: 0.75rem; }
|
||||
.below-sixty-title { position: relative; z-index: 1; margin-bottom: 1.25rem; }
|
||||
.below-sixty-table { margin-top: 0.75rem; }
|
||||
.below-sixty-dt td { vertical-align: top; }
|
||||
</style>
|
||||
<script>
|
||||
(function() {
|
||||
if (!window.$ || !$.fn || !$.fn.DataTable) return;
|
||||
$(function() {
|
||||
const table = $('.below-sixty-dt');
|
||||
if (!table.length) return;
|
||||
try {
|
||||
const semesterOffset = <?= !empty($isYearMode) ? '1' : '0' ?>;
|
||||
table.DataTable({
|
||||
order: [[8 + semesterOffset, 'asc']],
|
||||
pageLength: 100,
|
||||
lengthMenu: [10, 25, 50, 100, 200]
|
||||
function normalizeSemesterFilter() {
|
||||
const semesterSelect = document.querySelector('select[name="semester"]');
|
||||
if (!semesterSelect) return;
|
||||
|
||||
const wholeYearSelected = <?= !empty($isYearMode) ? 'true' : 'false' ?>;
|
||||
semesterSelect.innerHTML = '';
|
||||
semesterSelect.add(new Option('Fall', 'fall', !wholeYearSelected, !wholeYearSelected));
|
||||
semesterSelect.add(new Option('Whole Year', 'year', wholeYearSelected, wholeYearSelected));
|
||||
semesterSelect.value = wholeYearSelected ? 'year' : 'fall';
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', normalizeSemesterFilter);
|
||||
|
||||
if (window.$ && $.fn && $.fn.DataTable) {
|
||||
$(function() {
|
||||
const table = $('.below-sixty-dt');
|
||||
if (!table.length) return;
|
||||
try {
|
||||
table.DataTable({
|
||||
order: [[2, 'asc']],
|
||||
pageLength: 100,
|
||||
lengthMenu: [10, 25, 50, 100, 200],
|
||||
columnDefs: [{ orderable: false, targets: [3, 4, 5] }]
|
||||
});
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
|
||||
const detailsModal = document.getElementById('detailsModal');
|
||||
const detailsModalBody = document.getElementById('detailsModalBody');
|
||||
const detailsModalTitle = document.getElementById('detailsModalLabel');
|
||||
|
||||
const SCORE_LABELS = {
|
||||
homework_avg: 'Homework Avg',
|
||||
project_avg: 'Project Avg',
|
||||
participation_score: 'Participation',
|
||||
test_avg: 'Test Avg',
|
||||
ptap_score: 'PTAP Score',
|
||||
attendance_score: 'Attendance',
|
||||
midterm_exam_score: 'Midterm Score',
|
||||
final_exam_score: 'Final Exam',
|
||||
semester_score: 'Semester Score'
|
||||
};
|
||||
|
||||
const COMMENT_TYPE_LABELS = {
|
||||
general: 'General',
|
||||
attendance: 'Attendance',
|
||||
attendance_comment: 'Attendance',
|
||||
midterm: 'Midterm',
|
||||
final: 'Final Exam',
|
||||
ptap: 'PTAP'
|
||||
};
|
||||
|
||||
function fmtScore(value) {
|
||||
if (value === null || value === '' || value === undefined) return '—';
|
||||
const parsed = parseFloat(value);
|
||||
return isNaN(parsed) ? value : parsed.toFixed(2);
|
||||
}
|
||||
|
||||
function esc(str) {
|
||||
return String(str)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function buildDetailsHtml(semesters) {
|
||||
if (!semesters || semesters.length === 0) {
|
||||
return '<div class="alert alert-warning mb-0">No score data found.</div>';
|
||||
}
|
||||
|
||||
let html = '';
|
||||
semesters.forEach(function(sem) {
|
||||
html += '<h6 class="fw-bold mt-3 mb-2">' + esc(sem.semester || '') + ' Semester';
|
||||
if (sem.class_section_name) {
|
||||
html += ' <span class="text-muted fw-normal fs-6">— ' + esc(sem.class_section_name) + '</span>';
|
||||
}
|
||||
html += '</h6>';
|
||||
|
||||
html += '<table class="table table-sm table-bordered mb-2">';
|
||||
html += '<thead class="table-light"><tr><th>Item</th><th class="text-center">Score</th></tr></thead><tbody>';
|
||||
|
||||
let hasScoreRow = false;
|
||||
Object.entries(SCORE_LABELS).forEach(function(entry) {
|
||||
const key = entry[0];
|
||||
const label = entry[1];
|
||||
const value = sem[key];
|
||||
if (value === null || value === '' || value === undefined) return;
|
||||
const bold = key === 'semester_score' ? ' fw-bold' : '';
|
||||
html += '<tr><td>' + esc(label) + '</td><td class="text-center' + bold + '">' + esc(fmtScore(value)) + '</td></tr>';
|
||||
hasScoreRow = true;
|
||||
});
|
||||
} catch (_) {}
|
||||
});
|
||||
|
||||
if (!hasScoreRow) {
|
||||
html += '<tr><td colspan="2" class="text-muted">No scores recorded.</td></tr>';
|
||||
}
|
||||
html += '</tbody></table>';
|
||||
|
||||
const comments = sem.comments || {};
|
||||
const commentEntries = Object.entries(comments).filter(function(entry) {
|
||||
return entry[1] && String(entry[1]).trim();
|
||||
});
|
||||
|
||||
const seen = {};
|
||||
const deduped = [];
|
||||
commentEntries.forEach(function(entry) {
|
||||
const type = entry[0];
|
||||
const text = String(entry[1]);
|
||||
const label = COMMENT_TYPE_LABELS[type] || type;
|
||||
const key = label + '|' + text.trim();
|
||||
if (!seen[key]) {
|
||||
seen[key] = true;
|
||||
deduped.push([label, text]);
|
||||
}
|
||||
});
|
||||
|
||||
if (deduped.length > 0) {
|
||||
html += '<div class="mb-3">';
|
||||
html += '<p class="fw-semibold mb-1" style="font-size:0.9rem;">Comments</p>';
|
||||
deduped.forEach(function(entry) {
|
||||
const label = entry[0];
|
||||
const text = entry[1];
|
||||
html += '<div class="mb-2 p-2 bg-light rounded border-start border-3 border-secondary">';
|
||||
html += '<span class="badge bg-secondary me-1" style="font-size:0.7rem;">' + esc(label) + '</span>';
|
||||
html += '<span class="text-dark" style="font-size:0.9rem;">' + esc(text).replace(/\n/g, '<br>') + '</span>';
|
||||
html += '</div>';
|
||||
});
|
||||
html += '</div>';
|
||||
}
|
||||
});
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
if (detailsModal) {
|
||||
document.addEventListener('click', function(e) {
|
||||
const btn = e.target.closest('.btn-show-details');
|
||||
if (!btn) return;
|
||||
|
||||
const studentId = btn.dataset.studentId;
|
||||
const studentName = btn.dataset.studentName;
|
||||
const schoolYear = btn.dataset.schoolYear;
|
||||
|
||||
detailsModalTitle.textContent = studentName + ' — Score Details';
|
||||
detailsModalBody.innerHTML = '<div class="text-center py-4"><div class="spinner-border text-primary" role="status"></div></div>';
|
||||
bootstrap.Modal.getOrCreateInstance(detailsModal).show();
|
||||
|
||||
const url = '<?= site_url('grading/below-60/decisions/student-details') ?>'
|
||||
+ '?student_id=' + encodeURIComponent(studentId)
|
||||
+ '&school_year=' + encodeURIComponent(schoolYear);
|
||||
|
||||
fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } })
|
||||
.then(function(response) { return response.json(); })
|
||||
.then(function(data) {
|
||||
if (data.error) {
|
||||
detailsModalBody.innerHTML = '<div class="alert alert-danger">' + esc(data.error) + '</div>';
|
||||
return;
|
||||
}
|
||||
detailsModalBody.innerHTML = buildDetailsHtml(data.semesters);
|
||||
})
|
||||
.catch(function() {
|
||||
detailsModalBody.innerHTML = '<div class="alert alert-danger">Failed to load details.</div>';
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
Reference in New Issue
Block a user