This commit is contained in:
@@ -418,6 +418,7 @@ $routes->get('exam-drafts/files/final/(:segment)', 'View\FilesController::examDr
|
||||
|
||||
|
||||
|
||||
$routes->get('teacher/progress', 'ClassProgressController::history', ['filter' => 'auth:teacher,teacher_assistant']);
|
||||
$routes->get('teacher/progress/submit', 'ClassProgressController::create', ['filter' => 'auth:teacher,teacher_assistant']);
|
||||
$routes->post('teacher/progress/store', 'ClassProgressController::store', ['filter' => 'auth:teacher,teacher_assistant']);
|
||||
$routes->get('teacher/progress/history', 'ClassProgressController::history', ['filter' => 'auth:teacher,teacher_assistant']);
|
||||
|
||||
@@ -289,7 +289,7 @@ class ClassProgressController extends BaseController
|
||||
}
|
||||
|
||||
$row['status_label'] = self::STATUS_OPTIONS[$row['status']] ?? 'Unknown';
|
||||
$weeklyReports = $this->reportModel
|
||||
$weeklyReportsQuery = $this->reportModel
|
||||
->select('class_progress_reports.*, cs.class_section_name, CONCAT(IFNULL(u.firstname, ""), " ", IFNULL(u.lastname, "")) AS teacher_name')
|
||||
->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left')
|
||||
->join('users u', 'u.id = class_progress_reports.teacher_id', 'left')
|
||||
@@ -346,7 +346,7 @@ class ClassProgressController extends BaseController
|
||||
throw new PageNotFoundException('Progress report not found.');
|
||||
}
|
||||
|
||||
$weeklyReports = $this->reportModel
|
||||
$weeklyReportsQuery = $this->reportModel
|
||||
->select('class_progress_reports.*')
|
||||
->whereIn('teacher_id', $allowedTeacherIds)
|
||||
->where('class_section_id', $row['class_section_id'])
|
||||
@@ -513,7 +513,7 @@ class ClassProgressController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
$weeklyReports = $this->reportModel
|
||||
$weeklyReportsQuery = $this->reportModel
|
||||
->select('class_progress_reports.*')
|
||||
->whereIn('teacher_id', $allowedTeacherIds)
|
||||
->where('class_section_id', $classSectionId)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Controllers\View;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
use App\Exceptions\SchoolYear\SchoolYearWriteConflictException;
|
||||
use App\Models\ClassSectionModel;
|
||||
use App\Models\InventoryItemModel;
|
||||
use App\Models\InventoryCategoryModel;
|
||||
@@ -713,6 +714,8 @@ class InventoryController extends BaseController
|
||||
*/
|
||||
public function teacherDistributeForm()
|
||||
{
|
||||
$schoolYearContext = $this->resolveSchoolYearContext();
|
||||
|
||||
// 1) Build teacher/class context (auth/role/no-class handled inside)
|
||||
$ctx = $this->buildTeacherClassContext();
|
||||
if ($ctx instanceof \CodeIgniter\HTTP\RedirectResponse) {
|
||||
@@ -858,11 +861,19 @@ class InventoryController extends BaseController
|
||||
|
||||
'schoolYear' => $ctx['schoolYear'],
|
||||
'semester' => $ctx['semester'],
|
||||
'isEditable' => ! $schoolYearContext->isReadonly(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function teacherDistributeStore()
|
||||
{
|
||||
$schoolYearContext = $this->resolveSchoolYearContext();
|
||||
try {
|
||||
$this->assertSchoolYearWritable($schoolYearContext);
|
||||
} catch (SchoolYearWriteConflictException $e) {
|
||||
return redirect()->back()->withInput()->with('error', $e->getMessage());
|
||||
}
|
||||
|
||||
$ctx = $this->buildTeacherClassContext();
|
||||
if ($ctx instanceof \CodeIgniter\HTTP\RedirectResponse) {
|
||||
return $ctx;
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
<?= $this->extend('layout/main_layout') ?>
|
||||
<?= $this->section('content') ?>
|
||||
<?php
|
||||
$isEditable = (bool) ($isEditable ?? true);
|
||||
$disabledAttr = $isEditable ? '' : ' disabled';
|
||||
?>
|
||||
|
||||
<div class="container my-4">
|
||||
<h4 class="text-dark mb-3" style="font-family: Arial, sans-serif;">Distribute Books to Students</h4>
|
||||
<?= view('partials/flash_messages') ?>
|
||||
<?php if (!$isEditable): ?>
|
||||
<div class="alert alert-info">
|
||||
You are viewing <?= esc($schoolYear ?? 'a closed school year') ?>. Book distribution actions are read-only for closed years.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Filter bar (GET) — no class section shown -->
|
||||
<form id="filterForm" class="row g-3 mb-3" method="get" action="<?= site_url('inventory/books/distribute') ?>">
|
||||
@@ -43,7 +52,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Submission form (POST) -->
|
||||
<form method="post" action="<?= site_url('inventory/books/distribute') ?>">
|
||||
<form method="post" action="<?= site_url('inventory/books/distribute') ?>" data-readonly="<?= $isEditable ? '0' : '1' ?>">
|
||||
<?= csrf_field() ?>
|
||||
<!-- Keep class section hidden -->
|
||||
<input type="hidden" name="class_section_id" value="<?= esc($class_section_id) ?>">
|
||||
@@ -53,8 +62,8 @@
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>Students</span>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-sm btn-info" id="checkAllBtn">Check All</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="uncheckAllBtn">Uncheck All</button>
|
||||
<button type="button" class="btn btn-sm btn-info" id="checkAllBtn"<?= $disabledAttr ?>>Check All</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="uncheckAllBtn"<?= $disabledAttr ?>>Uncheck All</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -87,7 +96,7 @@
|
||||
class="form-check-input student-box"
|
||||
name="student_ids[]"
|
||||
value="<?= esc($sid) ?>"
|
||||
<?= $given ? 'checked' : '' ?>>
|
||||
<?= $given ? 'checked' : '' ?><?= $disabledAttr ?>>
|
||||
</td>
|
||||
<td><?= esc($student['school_id'] ?? '-') ?></td>
|
||||
<td><?= esc($student['firstname'] ?? '') ?></td>
|
||||
@@ -111,11 +120,11 @@
|
||||
<div class="card-footer">
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Note (optional)</label>
|
||||
<textarea class="form-control" rows="2" name="note" placeholder="e.g., Distributed in class today"></textarea>
|
||||
<textarea class="form-control" rows="2" name="note" placeholder="e.g., Distributed in class today"<?= $disabledAttr ?>></textarea>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-success">Deduct & Save</button>
|
||||
<button type="button" class="btn btn-secondary" id="clearSelectionBtn">Cancel</button>
|
||||
<button type="button" class="btn btn-success"<?= $disabledAttr ?>>Deduct & Save</button>
|
||||
<button type="button" class="btn btn-secondary" id="clearSelectionBtn"<?= $disabledAttr ?>>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,15 +138,20 @@
|
||||
|
||||
<?= $this->section('scripts') ?>
|
||||
<script>
|
||||
const distributeForm = document.querySelector('form[data-readonly]');
|
||||
const distributeReadonly = distributeForm?.getAttribute('data-readonly') === '1';
|
||||
|
||||
// Auto-submit filter when book changes
|
||||
document.getElementById('bookSelect')?.addEventListener('change', () => {
|
||||
document.getElementById('filterForm').submit();
|
||||
});
|
||||
|
||||
document.getElementById('checkAllBtn')?.addEventListener('click', () => {
|
||||
if (distributeReadonly) return;
|
||||
document.querySelectorAll('.student-box').forEach(cb => cb.checked = true);
|
||||
});
|
||||
document.getElementById('uncheckAllBtn')?.addEventListener('click', () => {
|
||||
if (distributeReadonly) return;
|
||||
document.querySelectorAll('.student-box').forEach(cb => cb.checked = false);
|
||||
});
|
||||
</script>
|
||||
@@ -149,17 +163,20 @@
|
||||
});
|
||||
|
||||
document.getElementById('checkAllBtn')?.addEventListener('click', () => {
|
||||
if (distributeReadonly) return;
|
||||
document.querySelectorAll('.student-box').forEach(cb => cb.checked = true);
|
||||
});
|
||||
|
||||
document.getElementById('uncheckAllBtn')?.addEventListener('click', () => {
|
||||
if (distributeReadonly) return;
|
||||
document.querySelectorAll('.student-box').forEach(cb => cb.checked = false);
|
||||
});
|
||||
|
||||
// Cancel button clears all checkboxes
|
||||
document.getElementById('clearSelectionBtn')?.addEventListener('click', () => {
|
||||
if (distributeReadonly) return;
|
||||
document.querySelectorAll('.student-box').forEach(cb => cb.checked = false);
|
||||
});
|
||||
</script>
|
||||
|
||||
<?= $this->endSection() ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<link rel="stylesheet" href="<?= base_url('assets/css/style.css') ?>">
|
||||
<link rel="stylesheet" href="<?= base_url('assets/css/landing_page.css?v=1.1') ?>">
|
||||
<link rel="stylesheet" href="<?= base_url('assets/css/custom.css') ?>">
|
||||
<?= $this->renderSection('styles') ?>
|
||||
|
||||
<?php
|
||||
// Accent + menu palettes (shared with management layout)
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<?= $this->extend('layout/main_layout') ?>
|
||||
<?= $this->section('styles') ?>
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.10/css/jquery.dataTables.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.10/css/dataTables.bootstrap5.min.css">
|
||||
<?= $this->endSection() ?>
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<style>
|
||||
@@ -131,10 +135,6 @@
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="9" class="text-center">No students found in this class.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -154,17 +154,33 @@
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('scripts') ?>
|
||||
<script src="https://cdn.datatables.net/1.13.10/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.10/js/dataTables.bootstrap5.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
if (!(window.jQuery && jQuery.fn && jQuery.fn.DataTable)) {
|
||||
return;
|
||||
}
|
||||
<?php if (!empty($classes)): ?>
|
||||
<?php foreach ($classes as $index => $class): ?>
|
||||
$('#classTable<?= $index ?>').DataTable({
|
||||
const table<?= $index ?> = $('#classTable<?= $index ?>');
|
||||
const expectedColumns<?= $index ?> = table<?= $index ?>.find('thead th').length;
|
||||
const hasInvalidRows<?= $index ?> = table<?= $index ?>.find('tbody tr').toArray().some(function(row) {
|
||||
return $(row).children('td, th').length !== expectedColumns<?= $index ?>;
|
||||
});
|
||||
if (hasInvalidRows<?= $index ?>) {
|
||||
return;
|
||||
}
|
||||
table<?= $index ?>.DataTable({
|
||||
// Keep columns visible and avoid squeezing
|
||||
responsive: false, // prevent column hiding that can make table look small
|
||||
autoWidth: true, // let DT calculate widths, we'll still force 100% via CSS
|
||||
scrollX: true, // allow horizontal scroll when needed
|
||||
pageLength: 25,
|
||||
lengthChange: false,
|
||||
language: {
|
||||
emptyTable: 'No students found in this class.'
|
||||
},
|
||||
columnDefs: [
|
||||
{ targets: '_all', className: 'text-nowrap' } // avoid wrapping labels
|
||||
]
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<?= $this->extend('layout/main_layout') ?>
|
||||
|
||||
<?= $this->section('styles') ?>
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.10/css/jquery.dataTables.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.10/css/dataTables.bootstrap5.min.css">
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
<div class="container-xxl py-5">
|
||||
<div class="container">
|
||||
@@ -282,6 +287,8 @@
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('scripts') ?>
|
||||
<script src="https://cdn.datatables.net/1.13.10/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.10/js/dataTables.bootstrap5.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded",()=>{
|
||||
const form=document.getElementById("attendanceForm");
|
||||
@@ -316,16 +323,34 @@ document.addEventListener("DOMContentLoaded",()=>{
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
$('#teachersTable').DataTable({
|
||||
pageLength:100,
|
||||
order:[],
|
||||
columnDefs:[{targets:[0,3,4,5,6],className:'text-center'}]
|
||||
});
|
||||
$('#studentsTable').DataTable({
|
||||
pageLength:100,
|
||||
order:[],
|
||||
columnDefs:[{targets:[0,4,5,6,7],className:'text-center'}]
|
||||
});
|
||||
if (!(window.jQuery && jQuery.fn && jQuery.fn.DataTable)) {
|
||||
return;
|
||||
}
|
||||
|
||||
function hasValidRows($table) {
|
||||
const expectedColumns = $table.find('thead th').length;
|
||||
return !$table.find('tbody tr').toArray().some(function(row) {
|
||||
return $(row).children('td, th').length !== expectedColumns;
|
||||
});
|
||||
}
|
||||
|
||||
const $teachersTable = $('#teachersTable');
|
||||
if ($teachersTable.length && hasValidRows($teachersTable)) {
|
||||
$teachersTable.DataTable({
|
||||
pageLength:100,
|
||||
order:[],
|
||||
columnDefs:[{targets:[0,3,4,5,6],className:'text-center'}]
|
||||
});
|
||||
}
|
||||
|
||||
const $studentsTable = $('#studentsTable');
|
||||
if ($studentsTable.length && hasValidRows($studentsTable)) {
|
||||
$studentsTable.DataTable({
|
||||
pageLength:100,
|
||||
order:[],
|
||||
columnDefs:[{targets:[0,4,5,6,7],className:'text-center'}]
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
Reference in New Issue
Block a user