exam draft fix

This commit is contained in:
root
2026-04-11 15:33:12 -04:00
parent 89e95b7851
commit 7bc999643a
13 changed files with 582 additions and 180 deletions
+206 -162
View File
@@ -6,6 +6,9 @@ $drafts = $drafts ?? [];
$legacyByClass = $legacyByClass ?? [];
$classSections = $classSections ?? [];
$examTypes = $examTypes ?? [];
$visibleClasses = $visibleClasses ?? [];
$classDraftGroups = $classDraftGroups ?? [];
$newSubmissionClasses = $newSubmissionClasses ?? [];
$schoolYear = $schoolYear ?? '';
$semester = $semester ?? '';
$maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024);
@@ -56,172 +59,207 @@ $fileAccept = implode(',', array_map(static fn ($x) => '.' . $x, $allowedExtensi
<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 if (empty($visibleClasses)): ?>
<p class="text-muted mb-0">No classes with enrolled students are available for this term.</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 & AuthorNote</th>
<th>Version</th>
<th>Date-Time</th>
<th>Status</th>
<th>Files</th>
<th>Reviewer Action</th>
<th>Final version</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');
}
$st = strtolower((string) ($draft['status'] ?? ''));
$adminName = trim(($draft['admin_first'] ?? '') . ' ' . ($draft['admin_last'] ?? ''));
$authorNote = $draft['author_comment'] ?? $draft['description'] ?? '';
$reviewerNote = $draft['reviewer_comment'] ?? $draft['admin_comments'] ?? '';
?>
<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 ($authorNote !== ''): ?>
<div class="small mt-1">
<span class="text-muted fw-semibold">Author:</span>
<?= esc($authorNote) ?>
</div>
<?php endif; ?>
<?php if ($reviewerNote !== ''): ?>
<div class="small mt-1 border-top pt-1">
<span class="text-muted fw-semibold">Reviewer:</span>
<?= esc($reviewerNote) ?>
</div>
<?php endif; ?>
</td>
<td class="text-nowrap">v<?= esc((string) ($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>
<?php
$badgeData = $statusBadges[$st] ?? ['label' => $st, 'class' => 'bg-secondary text-white'];
$badgeStyle = !empty($badgeData['style']) ? ' style="' . esc($badgeData['style']) . '"' : '';
?>
<span class="badge <?= esc($badgeData['class']) ?> js-status-badge" data-status="<?= esc($st) ?>"<?= $badgeStyle ?>>
<?= esc($badgeData['label']) ?>
</span>
<div class="small text-muted mt-1 js-acceptance-note">
<?php if ($st === 'accepted' && !empty($draft['acceptance_type'])): ?>
<?= $draft['acceptance_type'] === 'minor_edits' ? 'Accepted w/ minor edits' : 'Accepted as is' ?>
<?php endif; ?>
</div>
<?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) ?>
<div class="accordion exam-drafts-accordion" id="examDraftsAccordion">
<?php foreach ($visibleClasses as $classSectionId => $classInfo): ?>
<?php $classDraftsForSection = $classDraftGroups[$classSectionId] ?? []; ?>
<?php $collapseId = 'classDraftGroup_' . $classSectionId; ?>
<div class="accordion-item mb-3">
<h2 class="accordion-header" id="heading_<?= esc($collapseId) ?>">
<button class="accordion-button collapsed px-3" type="button" data-bs-toggle="collapse" data-bs-target="#<?= esc($collapseId) ?>" aria-expanded="false" aria-controls="<?= esc($collapseId) ?>">
<div class="d-flex flex-column flex-lg-row w-100 justify-content-between gap-2">
<div>
<strong><?= esc($classInfo['class_section_name'] ?? ('Class ' . $classSectionId)) ?></strong>
<div class="small text-muted">
<?= esc($classInfo['student_count'] ?? 0) ?> students · <?= esc(count($classDraftsForSection)) ?> submissions
<?php if (!empty($newSubmissionClasses[$classSectionId])): ?>
<span class="badge bg-info text-dark ms-2">New submission</span>
<?php endif; ?>
</div>
<?php endif; ?>
</td>
<td>
<?php $formId = 'review_form_' . (int) ($draft['id'] ?? 0); ?>
<?php $revNumber = max(1, (int) ($draft['version'] ?? 1)); ?>
<?php if (!empty($draft['teacher_file'])): ?>
<div>
<a href="<?= base_url('exam-drafts/files/teacher/' . $draft['teacher_file']) ?>" target="_blank" rel="noopener">
<?= esc('Ver' . $revNumber . ' ' . ($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']) && strtolower((string) ($draft['status'] ?? '')) === 'accepted'): ?>
<div class="mt-2">
<a href="<?= base_url('exam-drafts/files/final/' . $draft['final_file']) ?>" target="_blank" rel="noopener" class="link-success small">
<?= esc('Final ' . ($draft['final_filename'] ?? 'Final draft')) ?>
</a>
</div>
<?php endif; ?>
<?php if (!empty($draft['review_files'])): ?>
<div class="mt-2">
<?php
$reviewLinks = [];
foreach ($draft['review_files'] as $rf) {
$rev = max(1, (int) ($rf['review_revision'] ?? 1));
$name = $rf['final_filename'] ?? 'Review file';
$file = $rf['final_file'] ?? '';
if ($file !== '') {
$reviewLinks[] = '<a href="' . esc(base_url('exam-drafts/files/final/' . $file)) . '" target="_blank" rel="noopener" class="link-success small">' . esc('Ver' . $revNumber . '_' . $rev . ' ' . $name) . '</a>';
}
}
?>
<?= !empty($reviewLinks) ? implode(' | ', $reviewLinks) : '' ?>
</div>
<?php endif; ?>
<div class="mt-2">
<?php $fileInputId = 'review_file_' . (int) ($draft['id'] ?? 0); ?>
<input type="file" name="final_file" id="<?= esc($fileInputId) ?>" class="form-control form-control-sm" accept="<?= esc($fileAccept) ?>" form="<?= esc($formId) ?>">
<button type="submit" class="btn btn-sm btn-outline-primary mt-2" form="<?= esc($formId) ?>">Upload review file</button>
</div>
</td>
<td style="min-width: 280px;">
<?= form_open_multipart(base_url('administrator/exam-drafts/review'), ['class' => 'vstack gap-2', 'id' => $formId]) ?>
<?= csrf_field() ?>
<input type="hidden" name="draft_id" value="<?= (int) ($draft['id'] ?? 0) ?>">
<?php $selectedStatus = ''; ?>
<select name="review_status" class="form-select form-select-sm" required>
<option value="" <?= $selectedStatus === '' ? 'selected' : '' ?> disabled>— Select status —</option>
<option value="accepted" <?= $selectedStatus === 'accepted' ? 'selected' : '' ?>>Accepted</option>
<option value="legacy" <?= $selectedStatus === 'legacy' ? 'selected' : '' ?>>Legacy</option>
<option value="canceled" <?= $selectedStatus === 'canceled' ? 'selected' : '' ?>>Canceled</option>
<option value="rejected" <?= $selectedStatus === 'rejected' ? 'selected' : '' ?>>Rejected</option>
<option value="review needed" <?= $selectedStatus === 'review needed' ? 'selected' : '' ?>>Review needed</option>
<option value="under review" <?= $selectedStatus === 'under review' ? 'selected' : '' ?>>Under review</option>
</select>
<div class="small js-acceptance-group d-none">
<span class="d-block mb-1">As is / Minor edits</span>
<?php $acc = (string) ($draft['acceptance_type'] ?? ''); ?>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="acceptance_type" value="as_is" id="as_is_<?= (int) ($draft['id'] ?? 0) ?>" <?= $acc !== 'minor_edits' ? 'checked' : '' ?>>
<label class="form-check-label" for="as_is_<?= (int) ($draft['id'] ?? 0) ?>">As is</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="acceptance_type" value="minor_edits" id="minor_<?= (int) ($draft['id'] ?? 0) ?>" <?= $acc === 'minor_edits' ? 'checked' : '' ?>>
<label class="form-check-label" for="minor_<?= (int) ($draft['id'] ?? 0) ?>">Minor edits</label>
</div>
</div>
<textarea name="reviewer_comment" class="form-control form-control-sm js-review-comment" rows="3" placeholder="Feedback for the teacher"><?= esc($draft['reviewer_comment'] ?? $draft['reviewer_comments'] ?? $draft['admin_comments'] ?? '') ?></textarea>
<div class="d-flex flex-wrap gap-2">
<span class="text-muted small js-review-status"></span>
</div>
<?= form_close() ?>
</td>
<td class="text-nowrap" style="min-width: 220px;">
<?php
$pdfFile = $draft['final_pdf_file'] ?? null;
if (empty($pdfFile) && !empty($draft['final_file']) && strtolower(pathinfo($draft['final_file'], PATHINFO_EXTENSION)) === 'pdf') {
$pdfFile = $draft['final_file'];
}
?>
<?php if (!empty($pdfFile)): ?>
<div class="mt-2 d-flex flex-wrap gap-2">
<a class="btn btn-sm btn-outline-primary" href="<?= base_url('exam-drafts/files/final/' . $pdfFile) ?>" target="_blank" rel="noopener">View</a>
<a class="btn btn-sm btn-outline-secondary" href="<?= base_url('exam-drafts/files/final/' . $pdfFile) ?>" target="_blank" rel="noopener" download>Download</a>
</div>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<span class="badge bg-secondary align-self-start">
<?= esc(count($classDraftsForSection)) ?>
<?= count($classDraftsForSection) === 1 ? 'submission' : 'submissions' ?>
</span>
</div>
</button>
</h2>
<div id="<?= esc($collapseId) ?>" class="accordion-collapse collapse" aria-labelledby="heading_<?= esc($collapseId) ?>">
<div class="accordion-body pt-0 px-3">
<?php if (empty($classDraftsForSection)): ?>
<p class="text-muted mb-0">No submissions yet for this class.</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 & AuthorNote</th>
<th>Version</th>
<th>Date-Time</th>
<th>Status</th>
<th>Files</th>
<th>Reviewer Action</th>
<th>Final version</th>
</tr>
</thead>
<tbody>
<?php foreach ($classDraftsForSection 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');
}
$st = strtolower((string) ($draft['status'] ?? ''));
$adminName = trim(($draft['admin_first'] ?? '') . ' ' . ($draft['admin_last'] ?? ''));
$authorNote = $draft['author_comment'] ?? $draft['description'] ?? '';
$reviewerNote = $draft['reviewer_comment'] ?? $draft['admin_comments'] ?? '';
?>
<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 ($authorNote !== ''): ?>
<div class="small mt-1">
<span class="text-muted fw-semibold">Author:</span>
<?= esc($authorNote) ?>
</div>
<?php endif; ?>
<?php if ($reviewerNote !== ''): ?>
<div class="small mt-1 border-top pt-1">
<span class="text-muted fw-semibold">Reviewer:</span>
<?= esc($reviewerNote) ?>
</div>
<?php endif; ?>
</td>
<td class="text-nowrap">v<?= esc((string) ($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>
<?php
$badgeData = $statusBadges[$st] ?? ['label' => $st, 'class' => 'bg-secondary text-white'];
$badgeStyle = !empty($badgeData['style']) ? ' style="' . esc($badgeData['style']) . '"' : '';
?>
<span class="badge <?= esc($badgeData['class']) ?> js-status-badge" data-status="<?= esc($st) ?>"<?= $badgeStyle ?>>
<?= esc($badgeData['label']) ?>
</span>
<div class="small text-muted mt-1 js-acceptance-note">
<?php if ($st === 'accepted' && !empty($draft['acceptance_type'])): ?>
<?= $draft['acceptance_type'] === 'minor_edits' ? 'Accepted w/ minor edits' : 'Accepted as is' ?>
<?php endif; ?>
</div>
<?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 $formId = 'review_form_' . (int) ($draft['id'] ?? 0); ?>
<?php $revNumber = max(1, (int) ($draft['version'] ?? 1)); ?>
<?php if (!empty($draft['teacher_file'])): ?>
<div>
<a href="<?= base_url('exam-drafts/files/teacher/' . $draft['teacher_file']) ?>" target="_blank" rel="noopener">
<?= esc('Ver' . $revNumber . ' ' . ($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']) && strtolower((string) ($draft['status'] ?? '')) === 'accepted'): ?>
<div class="mt-2">
<a href="<?= base_url('exam-drafts/files/final/' . $draft['final_file']) ?>" target="_blank" rel="noopener" class="link-success small">
<?= esc('Final ' . ($draft['final_filename'] ?? 'Final draft')) ?>
</a>
</div>
<?php endif; ?>
<?php if (!empty($draft['review_files'])): ?>
<div class="mt-2">
<?php
$reviewLinks = [];
foreach ($draft['review_files'] as $rf) {
$rev = max(1, (int) ($rf['review_revision'] ?? 1));
$name = $rf['final_filename'] ?? 'Review file';
$file = $rf['final_file'] ?? '';
if ($file !== '') {
$reviewLinks[] = '<a href="' . esc(base_url('exam-drafts/files/final/' . $file)) . '" target="_blank" rel="noopener" class="link-success small">' . esc('Ver' . $revNumber . '_' . $rev . ' ' . $name) . '</a>';
}
}
?>
<?= !empty($reviewLinks) ? implode(' | ', $reviewLinks) : '' ?>
</div>
<?php endif; ?>
<div class="mt-2">
<?php $fileInputId = 'review_file_' . (int) ($draft['id'] ?? 0); ?>
<input type="file" name="final_file" id="<?= esc($fileInputId) ?>" class="form-control form-control-sm" accept="<?= esc($fileAccept) ?>" form="<?= esc($formId) ?>">
<button type="submit" class="btn btn-sm btn-outline-primary mt-2" form="<?= esc($formId) ?>">Upload review file</button>
</div>
</td>
<td style="min-width: 280px;">
<?= form_open_multipart(base_url('administrator/exam-drafts/review'), ['class' => 'vstack gap-2', 'id' => $formId]) ?>
<?= csrf_field() ?>
<input type="hidden" name="draft_id" value="<?= (int) ($draft['id'] ?? 0) ?>">
<?php $selectedStatus = ''; ?>
<select name="review_status" class="form-select form-select-sm" required>
<option value="" <?= $selectedStatus === '' ? 'selected' : '' ?> disabled>— Select status —</option>
<option value="accepted" <?= $selectedStatus === 'accepted' ? 'selected' : '' ?>>Accepted</option>
<option value="legacy" <?= $selectedStatus === 'legacy' ? 'selected' : '' ?>>Legacy</option>
<option value="canceled" <?= $selectedStatus === 'canceled' ? 'selected' : '' ?>>Canceled</option>
<option value="rejected" <?= $selectedStatus === 'rejected' ? 'selected' : '' ?>>Rejected</option>
<option value="review needed" <?= $selectedStatus === 'review needed' ? 'selected' : '' ?>>Review needed</option>
<option value="under review" <?= $selectedStatus === 'under review' ? 'selected' : '' ?>>Under review</option>
</select>
<div class="small js-acceptance-group d-none">
<span class="d-block mb-1">As is / Minor edits</span>
<?php $acc = (string) ($draft['acceptance_type'] ?? ''); ?>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="acceptance_type" value="as_is" id="as_is_<?= (int) ($draft['id'] ?? 0) ?>" <?= $acc !== 'minor_edits' ? 'checked' : '' ?>>
<label class="form-check-label" for="as_is_<?= (int) ($draft['id'] ?? 0) ?>">As is</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="acceptance_type" value="minor_edits" id="minor_<?= (int) ($draft['id'] ?? 0) ?>" <?= $acc === 'minor_edits' ? 'checked' : '' ?>>
<label class="form-check-label" for="minor_<?= (int) ($draft['id'] ?? 0) ?>">Minor edits</label>
</div>
</div>
<textarea name="reviewer_comment" class="form-control form-control-sm js-review-comment" rows="3" placeholder="Feedback for the teacher"><?= esc($draft['reviewer_comment'] ?? $draft['reviewer_comments'] ?? $draft['admin_comments'] ?? '') ?></textarea>
<div class="d-flex flex-wrap gap-2">
<span class="text-muted small js-review-status"></span>
</div>
<?= form_close() ?>
</td>
<td class="text-nowrap" style="min-width: 220px;">
<?php
$pdfFile = $draft['final_pdf_file'] ?? null;
if (empty($pdfFile) && !empty($draft['final_file']) && strtolower(pathinfo($draft['final_file'], PATHINFO_EXTENSION)) === 'pdf') {
$pdfFile = $draft['final_file'];
}
?>
<?php if (!empty($pdfFile)): ?>
<div class="mt-2 d-flex flex-wrap gap-2">
<a class="btn btn-sm btn-outline-primary" href="<?= base_url('exam-drafts/files/final/' . $pdfFile) ?>" target="_blank" rel="noopener">View</a>
<a class="btn btn-sm btn-outline-secondary" href="<?= base_url('exam-drafts/files/final/' . $pdfFile) ?>" target="_blank" rel="noopener" download>Download</a>
</div>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
@@ -319,6 +357,12 @@ $fileAccept = implode(',', array_map(static fn ($x) => '.' . $x, $allowedExtensi
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const submissionsTab = document.getElementById('submissions-tab');
if (submissionsTab) {
submissionsTab.addEventListener('click', () => {
window.location.reload();
});
}
const statusBadges = <?= json_encode($statusBadges, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) ?>;
const csrfTokenName = <?= json_encode(csrf_token(), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) ?>;
const csrfCookieNames = <?= json_encode(array_values(array_filter([