fix parent and teacher pages to follow year filter
Tests / PHPUnit (push) Failing after 1m15s

This commit is contained in:
root
2026-07-15 20:03:36 -04:00
parent feb1b29a32
commit 5f27dccd0f
32 changed files with 582 additions and 364 deletions
+4
View File
@@ -206,7 +206,11 @@ abstract class BaseController extends Controller
'admin', 'admin',
'administrator', 'administrator',
'administrative staff', 'administrative staff',
'parent',
'principal', 'principal',
'teacher',
'teacher assistant',
'teacher_assistant',
'vice principal', 'vice principal',
]); ]);
} }
+99 -24
View File
@@ -54,6 +54,7 @@ class ClassProgressController extends BaseController
public function create() public function create()
{ {
$context = $this->resolveSchoolYearContext();
$teacherId = (int) session()->get('user_id'); $teacherId = (int) session()->get('user_id');
$assignments = $this->loadTeacherSections($teacherId); $assignments = $this->loadTeacherSections($teacherId);
$first = $assignments[0] ?? null; $first = $assignments[0] ?? null;
@@ -76,12 +77,15 @@ class ClassProgressController extends BaseController
'classId' => $classId, 'classId' => $classId,
'sundayOptions' => $sundayOptions, 'sundayOptions' => $sundayOptions,
'defaultWeekStart' => $defaultWeekStart, 'defaultWeekStart' => $defaultWeekStart,
'isSchoolYearReadonly' => $context->isReadonly(),
]; ];
return view('teacher/class_progress_submit', $data); return view('teacher/class_progress_submit', $data);
} }
public function store() public function store()
{ {
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
[, $schoolYear] = $this->resolveCurrentTerm();
$subjectSections = self::SUBJECT_SECTIONS; $subjectSections = self::SUBJECT_SECTIONS;
$rules = [ $rules = [
'class_section_id' => 'required|integer', 'class_section_id' => 'required|integer',
@@ -132,8 +136,9 @@ class ClassProgressController extends BaseController
->select('id') ->select('id')
->where('class_section_id', $classSectionId) ->where('class_section_id', $classSectionId)
->where('week_start', $weekStart) ->where('week_start', $weekStart)
->where('teacher_id', $teacherId) ->where('teacher_id', $teacherId);
->findAll(); $this->applyProgressSchoolYearScope($existingReports, $schoolYear);
$existingReports = $existingReports->findAll();
if (! $confirmOverwrite && ! empty($existingReports)) { if (! $confirmOverwrite && ! empty($existingReports)) {
return redirect()->back() return redirect()->back()
@@ -175,7 +180,7 @@ class ClassProgressController extends BaseController
'homework' => $homework ?: null, 'homework' => $homework ?: null,
'status' => $status, 'status' => $status,
'flags_json' => $this->normalizeFlags($this->request->getPost('flags')), 'flags_json' => $this->normalizeFlags($this->request->getPost('flags')),
]; ] + $this->progressReportSchoolYearData($schoolYear);
$reportId = $this->reportModel->insert($data, true); $reportId = $this->reportModel->insert($data, true);
$attachmentField = "attachment_$slug"; $attachmentField = "attachment_$slug";
@@ -198,6 +203,7 @@ class ClassProgressController extends BaseController
public function history() public function history()
{ {
$teacherId = (int) session()->get('user_id'); $teacherId = (int) session()->get('user_id');
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$assignments = $this->loadTeacherSections($teacherId); $assignments = $this->loadTeacherSections($teacherId);
$selectedSectionId = (int) $this->request->getGet('class_section_id'); $selectedSectionId = (int) $this->request->getGet('class_section_id');
$validSectionIds = array_column($assignments, 'class_section_id'); $validSectionIds = array_column($assignments, 'class_section_id');
@@ -207,7 +213,6 @@ class ClassProgressController extends BaseController
if ($selectedSectionId && ! in_array($selectedSectionId, $validSectionIds, true)) { if ($selectedSectionId && ! in_array($selectedSectionId, $validSectionIds, true)) {
$selectedSectionId = $validSectionIds[0] ?? null; $selectedSectionId = $validSectionIds[0] ?? null;
} }
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$allowedTeacherIds = $this->resolveAssignedTeacherIds($selectedSectionId, $semester, $schoolYear); $allowedTeacherIds = $this->resolveAssignedTeacherIds($selectedSectionId, $semester, $schoolYear);
if (empty($allowedTeacherIds)) { if (empty($allowedTeacherIds)) {
$allowedTeacherIds = [$teacherId]; $allowedTeacherIds = [$teacherId];
@@ -217,6 +222,7 @@ class ClassProgressController extends BaseController
->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left') ->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') ->join('users u', 'u.id = class_progress_reports.teacher_id', 'left')
->whereIn('teacher_id', $allowedTeacherIds); ->whereIn('teacher_id', $allowedTeacherIds);
$this->applyProgressSchoolYearScope($builder, $schoolYear, true);
if ($selectedSectionId) { if ($selectedSectionId) {
$builder->where('class_progress_reports.class_section_id', $selectedSectionId); $builder->where('class_progress_reports.class_section_id', $selectedSectionId);
} }
@@ -251,24 +257,27 @@ class ClassProgressController extends BaseController
'subjectSections' => self::SUBJECT_SECTIONS, 'subjectSections' => self::SUBJECT_SECTIONS,
'classSectionOptions' => $sectionOptions, 'classSectionOptions' => $sectionOptions,
'selectedSectionId' => $selectedSectionId, 'selectedSectionId' => $selectedSectionId,
'schoolYear' => $schoolYear,
'isSchoolYearReadonly' => $this->resolveSchoolYearContext()->isReadonly(),
]); ]);
} }
public function view($id) public function view($id)
{ {
$teacherId = (int) session()->get('user_id'); $teacherId = (int) session()->get('user_id');
$row = $this->reportModel [$semester, $schoolYear] = $this->resolveCurrentTerm();
$rowQuery = $this->reportModel
->select('class_progress_reports.*, cs.class_section_name, CONCAT(IFNULL(u.firstname, ""), " ", IFNULL(u.lastname, "")) AS teacher_name') ->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('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left')
->join('users u', 'u.id = class_progress_reports.teacher_id', 'left') ->join('users u', 'u.id = class_progress_reports.teacher_id', 'left')
->where('class_progress_reports.id', (int) $id) ->where('class_progress_reports.id', (int) $id);
->first(); $this->applyProgressSchoolYearScope($rowQuery, $schoolYear, true);
$row = $rowQuery->first();
if (! $row) { if (! $row) {
throw new PageNotFoundException('Progress report not found.'); throw new PageNotFoundException('Progress report not found.');
} }
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear); $allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear);
if (empty($allowedTeacherIds)) { if (empty($allowedTeacherIds)) {
if ($teacherId !== (int) $row['teacher_id']) { if ($teacherId !== (int) $row['teacher_id']) {
@@ -286,7 +295,9 @@ class ClassProgressController extends BaseController
->join('users u', 'u.id = class_progress_reports.teacher_id', 'left') ->join('users u', 'u.id = class_progress_reports.teacher_id', 'left')
->whereIn('teacher_id', $allowedTeacherIds) ->whereIn('teacher_id', $allowedTeacherIds)
->where('class_progress_reports.class_section_id', $row['class_section_id']) ->where('class_progress_reports.class_section_id', $row['class_section_id'])
->where('week_start', $row['week_start']) ->where('week_start', $row['week_start']);
$this->applyProgressSchoolYearScope($weeklyReportsQuery, $schoolYear);
$weeklyReports = $weeklyReportsQuery
->orderBy('subject', 'ASC') ->orderBy('subject', 'ASC')
->findAll(); ->findAll();
@@ -313,17 +324,18 @@ class ClassProgressController extends BaseController
public function edit($id) public function edit($id)
{ {
$teacherId = (int) session()->get('user_id'); $teacherId = (int) session()->get('user_id');
$row = $this->reportModel [$semester, $schoolYear] = $this->resolveCurrentTerm();
$rowQuery = $this->reportModel
->select('class_progress_reports.*, cs.class_section_name') ->select('class_progress_reports.*, cs.class_section_name')
->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left') ->join('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left')
->where('class_progress_reports.id', (int) $id) ->where('class_progress_reports.id', (int) $id);
->first(); $this->applyProgressSchoolYearScope($rowQuery, $schoolYear);
$row = $rowQuery->first();
if (! $row) { if (! $row) {
throw new PageNotFoundException('Progress report not found.'); throw new PageNotFoundException('Progress report not found.');
} }
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear); $allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear);
if (empty($allowedTeacherIds)) { if (empty($allowedTeacherIds)) {
if ($teacherId !== (int) $row['teacher_id']) { if ($teacherId !== (int) $row['teacher_id']) {
@@ -338,7 +350,9 @@ class ClassProgressController extends BaseController
->select('class_progress_reports.*') ->select('class_progress_reports.*')
->whereIn('teacher_id', $allowedTeacherIds) ->whereIn('teacher_id', $allowedTeacherIds)
->where('class_section_id', $row['class_section_id']) ->where('class_section_id', $row['class_section_id'])
->where('week_start', $row['week_start']) ->where('week_start', $row['week_start']);
$this->applyProgressSchoolYearScope($weeklyReportsQuery, $schoolYear);
$weeklyReports = $weeklyReportsQuery
->orderBy('subject', 'ASC') ->orderBy('subject', 'ASC')
->findAll(); ->findAll();
@@ -405,18 +419,23 @@ class ClassProgressController extends BaseController
'isEdit' => true, 'isEdit' => true,
'formAction' => base_url('teacher/progress/update/' . (int) $row['id']), 'formAction' => base_url('teacher/progress/update/' . (int) $row['id']),
'submitLabel' => 'Update Progress', 'submitLabel' => 'Update Progress',
'isSchoolYearReadonly' => $this->resolveSchoolYearContext()->isReadonly(),
]); ]);
} }
public function update($id) public function update($id)
{ {
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$teacherId = (int) session()->get('user_id'); $teacherId = (int) session()->get('user_id');
$row = $this->reportModel->find((int) $id); [$semester, $schoolYear] = $this->resolveCurrentTerm();
$rowQuery = $this->reportModel
->where('class_progress_reports.id', (int) $id);
$this->applyProgressSchoolYearScope($rowQuery, $schoolYear);
$row = $rowQuery->first();
if (! $row) { if (! $row) {
throw new PageNotFoundException('Progress report not found.'); throw new PageNotFoundException('Progress report not found.');
} }
[$semester, $schoolYear] = $this->resolveCurrentTerm();
$allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear); $allowedTeacherIds = $this->resolveAssignedTeacherIds((int) $row['class_section_id'], $semester, $schoolYear);
if (empty($allowedTeacherIds)) { if (empty($allowedTeacherIds)) {
if ($teacherId !== (int) $row['teacher_id']) { if ($teacherId !== (int) $row['teacher_id']) {
@@ -473,8 +492,9 @@ class ClassProgressController extends BaseController
->select('id') ->select('id')
->where('class_section_id', $classSectionId) ->where('class_section_id', $classSectionId)
->where('week_start', $weekStart) ->where('week_start', $weekStart)
->where('teacher_id', $teacherId) ->where('teacher_id', $teacherId);
->findAll(); $this->applyProgressSchoolYearScope($conflicts, $schoolYear);
$conflicts = $conflicts->findAll();
if (! $confirmOverwrite && ! empty($conflicts)) { if (! $confirmOverwrite && ! empty($conflicts)) {
return redirect()->back() return redirect()->back()
->withInput() ->withInput()
@@ -497,7 +517,9 @@ class ClassProgressController extends BaseController
->select('class_progress_reports.*') ->select('class_progress_reports.*')
->whereIn('teacher_id', $allowedTeacherIds) ->whereIn('teacher_id', $allowedTeacherIds)
->where('class_section_id', $classSectionId) ->where('class_section_id', $classSectionId)
->where('week_start', $row['week_start']) ->where('week_start', $row['week_start']);
$this->applyProgressSchoolYearScope($weeklyReportsQuery, $schoolYear);
$weeklyReports = $weeklyReportsQuery
->orderBy('subject', 'ASC') ->orderBy('subject', 'ASC')
->findAll(); ->findAll();
@@ -533,7 +555,7 @@ class ClassProgressController extends BaseController
'unit_title' => $unitTitle, 'unit_title' => $unitTitle,
'covered' => $covered, 'covered' => $covered,
'homework' => $homework ?: null, 'homework' => $homework ?: null,
]; ] + $this->progressReportSchoolYearData($schoolYear);
if ($flagsInput !== null) { if ($flagsInput !== null) {
$data['flags_json'] = $this->normalizeFlags($flagsInput); $data['flags_json'] = $this->normalizeFlags($flagsInput);
} }
@@ -698,8 +720,7 @@ class ClassProgressController extends BaseController
protected function loadTeacherSections(int $teacherId): array protected function loadTeacherSections(int $teacherId): array
{ {
$schoolYear = (string) ($this->configModel->getConfig('school_year') ?? ''); [$semester, $schoolYear] = $this->resolveCurrentTerm();
$semester = (string) ($this->configModel->getConfig('semester') ?? '');
return $this->teacherClassModel->getClassAssignmentsByUserId($teacherId, $schoolYear, $semester); return $this->teacherClassModel->getClassAssignmentsByUserId($teacherId, $schoolYear, $semester);
} }
@@ -878,7 +899,7 @@ class ClassProgressController extends BaseController
protected function resolveProgressDateRange(): ?array protected function resolveProgressDateRange(): ?array
{ {
$schoolYear = (string) ($this->configModel->getConfig('school_year') ?? ''); $schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
if ($schoolYear === '') { if ($schoolYear === '') {
return null; return null;
} }
@@ -946,11 +967,65 @@ class ClassProgressController extends BaseController
protected function resolveCurrentTerm(): array protected function resolveCurrentTerm(): array
{ {
$schoolYear = (string) ($this->configModel->getConfig('school_year') ?? ''); $schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
$semester = (string) ($this->configModel->getConfig('semester') ?? ''); $semester = (string) ($this->configModel->getConfig('semester') ?? '');
return [$semester, $schoolYear]; return [$semester, $schoolYear];
} }
protected function applyProgressSchoolYearScope($builder, string $schoolYear, bool $canScopeBySection = false): void
{
if ($schoolYear === '') {
return;
}
$db = db_connect();
$hasReportYear = $db->fieldExists('school_year', 'class_progress_reports');
$hasSectionYear = $db->fieldExists('school_year', 'classSection');
$semesterResolver = new SemesterRangeService($this->configModel);
[$rangeStart, $rangeEnd] = $semesterResolver->getSchoolYearRange($schoolYear);
if ($hasReportYear) {
if ($rangeStart !== '' && $rangeEnd !== '') {
$builder
->groupStart()
->where('class_progress_reports.school_year', $schoolYear)
->orGroupStart()
->groupStart()
->where('class_progress_reports.school_year IS NULL', null, false)
->orWhere('class_progress_reports.school_year', '')
->groupEnd()
->where('class_progress_reports.week_start >=', $rangeStart)
->where('class_progress_reports.week_start <=', $rangeEnd)
->groupEnd()
->groupEnd();
return;
}
$builder->where('class_progress_reports.school_year', $schoolYear);
return;
}
if ($canScopeBySection && $hasSectionYear) {
$builder->where('cs.school_year', $schoolYear);
return;
}
if ($rangeStart !== '' && $rangeEnd !== '') {
$builder
->where('class_progress_reports.week_start >=', $rangeStart)
->where('class_progress_reports.week_start <=', $rangeEnd);
}
}
protected function progressReportSchoolYearData(string $schoolYear): array
{
if ($schoolYear === '' || ! db_connect()->fieldExists('school_year', 'class_progress_reports')) {
return [];
}
return ['school_year' => $schoolYear];
}
protected function resolveAssignedTeacherIds(?int $classSectionId, string $semester, string $schoolYear): array protected function resolveAssignedTeacherIds(?int $classSectionId, string $semester, string $schoolYear): array
{ {
if (! $classSectionId || $schoolYear === '') { if (! $classSectionId || $schoolYear === '') {
+8 -1
View File
@@ -29,6 +29,7 @@ class ParentProgressController extends BaseController
public function index() public function index()
{ {
$schoolYear = $this->currentSchoolYearName();
$students = $this->getParentStudents(); $students = $this->getParentStudents();
$sectionIds = array_values(array_unique(array_filter(array_map( $sectionIds = array_values(array_unique(array_filter(array_map(
static fn (array $student): int => (int) ($student['class_section_id'] ?? 0), static fn (array $student): int => (int) ($student['class_section_id'] ?? 0),
@@ -42,7 +43,8 @@ class ParentProgressController extends BaseController
->select('class_progress_reports.*, cs.class_section_name, CONCAT(IFNULL(u.firstname, ""), " ", IFNULL(u.lastname, "")) AS teacher_name') ->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('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left')
->join('users u', 'u.id = class_progress_reports.teacher_id', 'left') ->join('users u', 'u.id = class_progress_reports.teacher_id', 'left')
->whereIn('class_progress_reports.class_section_id', $sectionIds); ->whereIn('class_progress_reports.class_section_id', $sectionIds)
->where('class_progress_reports.school_year', $schoolYear);
$rows = $builder $rows = $builder
->orderBy('week_start', 'DESC') ->orderBy('week_start', 'DESC')
@@ -75,10 +77,12 @@ class ParentProgressController extends BaseController
public function view($id) public function view($id)
{ {
$schoolYear = $this->currentSchoolYearName();
$row = $this->reportModel $row = $this->reportModel
->select('class_progress_reports.*, cs.class_section_name, CONCAT(IFNULL(u.firstname, ""), " ", IFNULL(u.lastname, "")) AS teacher_name') ->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('classSection cs', 'cs.class_section_id = class_progress_reports.class_section_id', 'left')
->join('users u', 'u.id = class_progress_reports.teacher_id', 'left') ->join('users u', 'u.id = class_progress_reports.teacher_id', 'left')
->where('class_progress_reports.school_year', $schoolYear)
->find((int) $id); ->find((int) $id);
if (! $row || ! $this->isSectionAccessible($row['class_section_id'] ?? null)) { if (! $row || ! $this->isSectionAccessible($row['class_section_id'] ?? null)) {
@@ -92,6 +96,7 @@ class ParentProgressController extends BaseController
->select('class_progress_reports.*') ->select('class_progress_reports.*')
->where('class_section_id', $row['class_section_id']) ->where('class_section_id', $row['class_section_id'])
->where('week_start', $row['week_start']) ->where('week_start', $row['week_start'])
->where('school_year', $schoolYear)
->orderBy('subject', 'ASC') ->orderBy('subject', 'ASC')
->findAll(); ->findAll();
@@ -165,6 +170,7 @@ class ParentProgressController extends BaseController
$rows = $this->enrollmentModel $rows = $this->enrollmentModel
->select('class_section_id') ->select('class_section_id')
->where('parent_id', $parentId) ->where('parent_id', $parentId)
->where('school_year', $this->currentSchoolYearName())
->where('is_withdrawn', 0) ->where('is_withdrawn', 0)
->groupBy('class_section_id') ->groupBy('class_section_id')
->findAll(); ->findAll();
@@ -213,6 +219,7 @@ class ParentProgressController extends BaseController
->join('students s', 's.id = e.student_id') ->join('students s', 's.id = e.student_id')
->join('classSection cs', 'cs.class_section_id = e.class_section_id', 'left') ->join('classSection cs', 'cs.class_section_id = e.class_section_id', 'left')
->where('e.parent_id', $parentId) ->where('e.parent_id', $parentId)
->where('e.school_year', $this->currentSchoolYearName())
->where('e.is_withdrawn', 0) ->where('e.is_withdrawn', 0)
->orderBy('e.updated_at', 'DESC') ->orderBy('e.updated_at', 'DESC')
->orderBy('e.created_at', 'DESC') ->orderBy('e.created_at', 'DESC')
@@ -31,7 +31,7 @@ class ParentReportCardController extends BaseController
return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.'); return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.');
} }
$schoolYear = trim((string) ($this->request->getGet('school_year') ?? $this->currentSchoolYearName())); $schoolYear = trim((string) $this->currentSchoolYearName());
$semester = trim((string) ($this->request->getGet('semester') ?? $this->configModel->getConfig('semester') ?? '')); $semester = trim((string) ($this->request->getGet('semester') ?? $this->configModel->getConfig('semester') ?? ''));
$builder = $this->db->table('students s') $builder = $this->db->table('students s')
@@ -82,7 +82,7 @@ class ParentReportCardController extends BaseController
throw new PageNotFoundException('Student not found.'); throw new PageNotFoundException('Student not found.');
} }
$schoolYear = trim((string) ($this->request->getGet('school_year') ?? $this->currentSchoolYearName())); $schoolYear = trim((string) $this->currentSchoolYearName());
$semester = trim((string) ($this->request->getGet('semester') ?? $this->configModel->getConfig('semester') ?? '')); $semester = trim((string) ($this->request->getGet('semester') ?? $this->configModel->getConfig('semester') ?? ''));
$this->touchAcknowledgement($parentId, (int) $studentId, $schoolYear, $semester, [ $this->touchAcknowledgement($parentId, (int) $studentId, $schoolYear, $semester, [
+115 -6
View File
@@ -12,6 +12,7 @@ use App\Models\TeacherClassModel;
use App\Models\AdminNotificationSubjectModel; use App\Models\AdminNotificationSubjectModel;
use App\Models\NotificationModel; use App\Models\NotificationModel;
use App\Models\UserNotificationModel; use App\Models\UserNotificationModel;
use App\Services\SemesterRangeService;
use CodeIgniter\Exceptions\PageNotFoundException; use CodeIgniter\Exceptions\PageNotFoundException;
class PrintRequests extends BaseController class PrintRequests extends BaseController
@@ -46,18 +47,26 @@ class PrintRequests extends BaseController
public function teacher_index() public function teacher_index()
{ {
$teacher_id = session()->get('user_id'); $teacher_id = session()->get('user_id');
$context = $this->resolveSchoolYearContext();
$schoolYear = $context->yearName();
$data['print_requests'] = $this->printRequestModel $printRequestsQuery = $this->printRequestModel
->select('print_requests.*, admins.firstname as admin_firstname, admins.lastname as admin_lastname') ->select('print_requests.*, admins.firstname as admin_firstname, admins.lastname as admin_lastname')
->join('users as admins', 'admins.id = print_requests.admin_id', 'left') ->join('users as admins', 'admins.id = print_requests.admin_id', 'left')
->where('print_requests.teacher_id', $teacher_id) ->join('classSection cs', 'cs.class_section_id = print_requests.class_id', 'left')
->where('print_requests.teacher_id', $teacher_id);
$this->applyPrintRequestSchoolYearScope($printRequestsQuery, $schoolYear);
$data['print_requests'] = $printRequestsQuery
->orderBy('print_requests.required_by', 'DESC')
->orderBy('print_requests.id', 'DESC')
->findAll(); ->findAll();
$teacher_classes = $this->teacherClassModel->getClassByTeacherId($teacher_id); $teacher_classes = $this->teacherClassModel->getClassAssignmentsByUserId((int) $teacher_id, $schoolYear);
$data['class_id'] = !empty($teacher_classes) ? $teacher_classes[0]['class_section_id'] : null; $data['class_id'] = !empty($teacher_classes) ? $teacher_classes[0]['class_section_id'] : null;
$dateOptions = $this->buildRequiredByOptions(); $dateOptions = $this->buildRequiredByOptions();
$data['sundays'] = $dateOptions['sundays']; $data['sundays'] = $dateOptions['sundays'];
$data['times'] = $dateOptions['times']; $data['times'] = $dateOptions['times'];
$data['isSchoolYearReadonly'] = $context->isReadonly();
return view('print_requests/teacher_index', $data); return view('print_requests/teacher_index', $data);
} }
@@ -94,6 +103,9 @@ class PrintRequests extends BaseController
public function create() public function create()
{ {
$context = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($context);
$schoolYear = $context->yearName();
$validationRules = [ $validationRules = [
'file' => 'uploaded[file]|max_size[file,5120]|ext_in[file,pdf,jpg,png,jpeg,doc,docx,txt]', 'file' => 'uploaded[file]|max_size[file,5120]|ext_in[file,pdf,jpg,png,jpeg,doc,docx,txt]',
'page_selection' => 'permit_empty|regex_match[/^\\s*\\d+(?:\\s*-\\s*\\d+)?(?:\\s*,\\s*\\d+(?:\\s*-\\s*\\d+)?)*\\s*$/]', 'page_selection' => 'permit_empty|regex_match[/^\\s*\\d+(?:\\s*-\\s*\\d+)?(?:\\s*,\\s*\\d+(?:\\s*-\\s*\\d+)?)*\\s*$/]',
@@ -125,7 +137,7 @@ class PrintRequests extends BaseController
'required_by' => $this->request->getPost('required_by'), 'required_by' => $this->request->getPost('required_by'),
'pickup_method' => $this->request->getPost('pickup_method'), 'pickup_method' => $this->request->getPost('pickup_method'),
'status' => 'not_assigned', 'status' => 'not_assigned',
]; ] + $this->printRequestSchoolYearData($schoolYear);
$printRequestId = (int) $this->printRequestModel->insert($data, true); $printRequestId = (int) $this->printRequestModel->insert($data, true);
if ($printRequestId > 0) { if ($printRequestId > 0) {
@@ -175,6 +187,7 @@ class PrintRequests extends BaseController
// Case 2: Teacher edit // Case 2: Teacher edit
if ($this->request->getPost('num_copies')) { if ($this->request->getPost('num_copies')) {
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$user_id = session()->get('user_id'); $user_id = session()->get('user_id');
if ($request['teacher_id'] != $user_id) { if ($request['teacher_id'] != $user_id) {
return redirect()->to('teacher/print-requests')->with('error', 'You are not authorized to edit this request.'); return redirect()->to('teacher/print-requests')->with('error', 'You are not authorized to edit this request.');
@@ -230,6 +243,7 @@ class PrintRequests extends BaseController
public function delete($id) public function delete($id)
{ {
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$teacher_id = session()->get('user_id'); $teacher_id = session()->get('user_id');
$request = $this->printRequestModel->find($id); $request = $this->printRequestModel->find($id);
@@ -260,6 +274,9 @@ class PrintRequests extends BaseController
public function copy($id) public function copy($id)
{ {
$teacher_id = session()->get('user_id'); $teacher_id = session()->get('user_id');
$context = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($context);
$schoolYear = $context->yearName();
$request = $this->printRequestModel->find($id); $request = $this->printRequestModel->find($id);
if (!$request) { if (!$request) {
@@ -296,7 +313,7 @@ class PrintRequests extends BaseController
'required_by' => $request['required_by'], 'required_by' => $request['required_by'],
'pickup_method' => $request['pickup_method'], 'pickup_method' => $request['pickup_method'],
'status' => 'not_assigned', 'status' => 'not_assigned',
]; ] + $this->printRequestSchoolYearData($schoolYear);
$copiedId = (int) $this->printRequestModel->insert($data, true); $copiedId = (int) $this->printRequestModel->insert($data, true);
if ($copiedId > 0) { if ($copiedId > 0) {
@@ -308,6 +325,9 @@ class PrintRequests extends BaseController
public function createCopy() public function createCopy()
{ {
$context = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($context);
$schoolYear = $context->yearName();
$validationRules = [ $validationRules = [
'num_copies' => 'required|integer|greater_than[0]', 'num_copies' => 'required|integer|greater_than[0]',
'required_by' => 'required|valid_date', 'required_by' => 'required|valid_date',
@@ -329,7 +349,7 @@ class PrintRequests extends BaseController
'required_by' => $this->request->getPost('required_by'), 'required_by' => $this->request->getPost('required_by'),
'pickup_method' => $this->request->getPost('pickup_method'), 'pickup_method' => $this->request->getPost('pickup_method'),
'status' => 'not_assigned', 'status' => 'not_assigned',
]; ] + $this->printRequestSchoolYearData($schoolYear);
$printRequestId = (int) $this->printRequestModel->insert($data, true); $printRequestId = (int) $this->printRequestModel->insert($data, true);
if ($printRequestId > 0) { if ($printRequestId > 0) {
@@ -374,6 +394,95 @@ class PrintRequests extends BaseController
]; ];
} }
private function applyPrintRequestSchoolYearScope($query, string $schoolYear): void
{
if ($schoolYear === '') {
return;
}
$hasPrintRequestYear = $this->printRequestsHaveSchoolYearColumn();
$hasClassSectionYear = db_connect()->fieldExists('school_year', 'classSection');
$range = (new SemesterRangeService($this->configModel))->getSchoolYearRange($schoolYear);
[$rangeStart, $rangeEnd] = $range;
if ($hasPrintRequestYear && $hasClassSectionYear) {
$query->groupStart()
->where('print_requests.school_year', $schoolYear)
->orGroupStart()
->groupStart()
->where('print_requests.school_year IS NULL', null, false)
->orWhere('print_requests.school_year', '')
->groupEnd()
->where('cs.school_year', $schoolYear)
->groupEnd();
if ($rangeStart !== '' && $rangeEnd !== '') {
$query
->orGroupStart()
->groupStart()
->where('print_requests.school_year IS NULL', null, false)
->orWhere('print_requests.school_year', '')
->groupEnd()
->groupStart()
->where('cs.school_year IS NULL', null, false)
->orWhere('cs.school_year', '')
->groupEnd()
->where('print_requests.required_by >=', $rangeStart . ' 00:00:00')
->where('print_requests.required_by <=', $rangeEnd . ' 23:59:59')
->groupEnd();
}
$query->groupEnd();
return;
}
if (! $hasPrintRequestYear && $hasClassSectionYear) {
$query->where('cs.school_year', $schoolYear);
return;
}
if ($rangeStart !== '' && $rangeEnd !== '') {
if ($hasPrintRequestYear) {
$query
->groupStart()
->where('print_requests.school_year', $schoolYear)
->orGroupStart()
->groupStart()
->where('print_requests.school_year IS NULL', null, false)
->orWhere('print_requests.school_year', '')
->groupEnd()
->where('print_requests.required_by >=', $rangeStart . ' 00:00:00')
->where('print_requests.required_by <=', $rangeEnd . ' 23:59:59')
->groupEnd()
->groupEnd();
return;
}
$query
->where('print_requests.required_by >=', $rangeStart . ' 00:00:00')
->where('print_requests.required_by <=', $rangeEnd . ' 23:59:59');
return;
}
if ($hasPrintRequestYear) {
$query->where('print_requests.school_year', $schoolYear);
}
}
private function printRequestSchoolYearData(string $schoolYear): array
{
if ($schoolYear === '' || ! $this->printRequestsHaveSchoolYearColumn()) {
return [];
}
return ['school_year' => $schoolYear];
}
private function printRequestsHaveSchoolYearColumn(): bool
{
return db_connect()->fieldExists('school_year', 'print_requests');
}
public function serveFile(string $filename, string $mode = 'inline') public function serveFile(string $filename, string $mode = 'inline')
{ {
$safeName = basename(trim($filename)); $safeName = basename(trim($filename));
+30 -10
View File
@@ -91,6 +91,9 @@ class ExamDraftController extends BaseController
public function teacherIndex() public function teacherIndex()
{ {
$this->syncAcademicContext();
$context = $this->resolveSchoolYearContext();
$teacherId = (int) (session()->get('user_id') ?? 0); $teacherId = (int) (session()->get('user_id') ?? 0);
if ($teacherId <= 0) { if ($teacherId <= 0) {
return redirect()->to('/login'); return redirect()->to('/login');
@@ -175,11 +178,15 @@ class ExamDraftController extends BaseController
'maxUploadBytes' => self::MAX_UPLOAD_BYTES, 'maxUploadBytes' => self::MAX_UPLOAD_BYTES,
'validation' => $validation, 'validation' => $validation,
'printableDraftIds' => $printableIds, 'printableDraftIds' => $printableIds,
'isSchoolYearReadonly' => $context->isReadonly(),
]); ]);
} }
public function teacherStore() public function teacherStore()
{ {
$this->syncAcademicContext();
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$teacherId = (int) (session()->get('user_id') ?? 0); $teacherId = (int) (session()->get('user_id') ?? 0);
if ($teacherId <= 0) { if ($teacherId <= 0) {
return redirect()->to('/login'); return redirect()->to('/login');
@@ -193,10 +200,13 @@ class ExamDraftController extends BaseController
return redirect()->back()->withInput()->with('error', 'Select a class section before submitting.'); return redirect()->back()->withInput()->with('error', 'Select a class section before submitting.');
} }
$assignment = $this->teacherClassModel $assignmentQuery = $this->teacherClassModel
->where('teacher_id', $teacherId) ->where('teacher_id', $teacherId)
->where('class_section_id', $classSectionId) ->where('class_section_id', $classSectionId);
->first(); if ($this->schoolYear !== '') {
$assignmentQuery->where('school_year', $this->schoolYear);
}
$assignment = $assignmentQuery->first();
if (empty($assignment)) { if (empty($assignment)) {
return redirect()->back()->withInput()->with('error', 'You are not assigned to the selected class section.'); return redirect()->back()->withInput()->with('error', 'You are not assigned to the selected class section.');
@@ -785,6 +795,8 @@ class ExamDraftController extends BaseController
public function teacherStatusFeed() public function teacherStatusFeed()
{ {
$this->syncAcademicContext();
$teacherId = (int) (session()->get('user_id') ?? 0); $teacherId = (int) (session()->get('user_id') ?? 0);
if ($teacherId <= 0) { if ($teacherId <= 0) {
return $this->response->setStatusCode(401); return $this->response->setStatusCode(401);
@@ -848,6 +860,8 @@ class ExamDraftController extends BaseController
->join('classSection cs', 'cs.class_section_id = exam_drafts.class_section_id', 'left') ->join('classSection cs', 'cs.class_section_id = exam_drafts.class_section_id', 'left')
->join('users u', 'u.id = exam_drafts.' . $this->authorIdColumn, 'left'); ->join('users u', 'u.id = exam_drafts.' . $this->authorIdColumn, 'left');
$this->applyExamDraftYearScope($query);
if (empty($classSectionIds)) { if (empty($classSectionIds)) {
return $query->where('exam_drafts.' . $this->authorIdColumn, $teacherId); return $query->where('exam_drafts.' . $this->authorIdColumn, $teacherId);
} }
@@ -859,19 +873,25 @@ class ExamDraftController extends BaseController
->where('exam_drafts.' . $this->authorIdColumn . ' !=', $teacherId) ->where('exam_drafts.' . $this->authorIdColumn . ' !=', $teacherId)
->where('exam_drafts.status !=', 'draft'); ->where('exam_drafts.status !=', 'draft');
if ($this->schoolYear !== '') {
$query->where('exam_drafts.school_year', $this->schoolYear);
}
if ($this->semester !== '') {
$query->where('exam_drafts.semester', $this->semester);
}
$query->groupEnd() $query->groupEnd()
->groupEnd(); ->groupEnd();
return $query; return $query;
} }
private function syncAcademicContext(): void
{
$this->schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
$this->semester = (string) ($this->configModel->getConfig('semester') ?? '');
}
private function applyExamDraftYearScope($query): void
{
if ($this->schoolYear !== '') {
$query->where('exam_drafts.school_year', $this->schoolYear);
}
}
private function attachTeacherDraftContext(array $row, int $viewerId): array private function attachTeacherDraftContext(array $row, int $viewerId): array
{ {
$isOwn = $this->draftTeacherId($row) === $viewerId; $isOwn = $this->draftTeacherId($row) === $viewerId;
+11 -7
View File
@@ -1065,13 +1065,7 @@ class GradingController extends Controller
public function belowSixty() public function belowSixty()
{ {
$configuredYear = (string) $this->schoolYear; $schoolYear = $this->currentSchoolYearName((string) $this->schoolYear);
$schoolYear = trim((string)($this->request->getGet('school_year') ?? ''));
if ($schoolYear === '') {
$schoolYear = $configuredYear;
}
// This page is Fall only. // This page is Fall only.
$semester = 'fall'; $semester = 'fall';
@@ -1124,6 +1118,16 @@ public function belowSixty()
'canViewGrading' => $canViewGrading, 'canViewGrading' => $canViewGrading,
]); ]);
} }
private function currentSchoolYearName(?string $fallback = null): string
{
try {
return service('schoolYearContext')->resolve($this->request)->yearName();
} catch (\Throwable) {
return trim((string) ($fallback ?? ''));
}
}
public function editBelowSixtyEmail() public function editBelowSixtyEmail()
{ {
$studentId = (int)$this->request->getGet('student_id'); $studentId = (int)$this->request->getGet('student_id');
+34 -15
View File
@@ -5,7 +5,7 @@ namespace App\Controllers\View;
use App\Models\HomeworkModel; use App\Models\HomeworkModel;
use App\Models\StudentModel; use App\Models\StudentModel;
use App\Models\StudentClassModel; use App\Models\StudentClassModel;
use CodeIgniter\Controller; use App\Controllers\BaseController;
use App\Models\TeacherClassModel; use App\Models\TeacherClassModel;
use App\Models\ConfigurationModel; use App\Models\ConfigurationModel;
use App\Models\UserModel; use App\Models\UserModel;
@@ -16,7 +16,7 @@ use App\Controllers\View\GradingController;
use App\Models\GradingLockModel; use App\Models\GradingLockModel;
use App\Models\MissingScoreOverrideModel; use App\Models\MissingScoreOverrideModel;
class HomeworkController extends Controller class HomeworkController extends BaseController
{ {
protected $db; protected $db;
protected $semesterScoreService; protected $semesterScoreService;
@@ -59,11 +59,12 @@ class HomeworkController extends Controller
} }
} }
public function updateHomeworkScores(array $scores = null, int $updatedBy = null, int $classSectionId = null) public function updateHomeworkScores(?array $scores = null, ?int $updatedBy = null, ?int $classSectionId = null)
{ {
$scores = $this->request->getPost('scores'); $scores = $this->request->getPost('scores');
$semester = $this->request->getPost('semester') ?? $this->semester; $semester = $this->request->getPost('semester') ?? $this->semester;
$schoolYear = $this->request->getPost('school_year') ?? $this->schoolYear; $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
if ($updatedBy === null) { if ($updatedBy === null) {
$updatedBy = session()->get('user_id'); $updatedBy = session()->get('user_id');
@@ -195,6 +196,8 @@ class HomeworkController extends Controller
$selectedSemester = $this->getSelectedSemester(); $selectedSemester = $this->getSelectedSemester();
$normalized = $this->normalizeSemesterSelection($selectedSemester); $normalized = $this->normalizeSemesterSelection($selectedSemester);
$semesterLabel = $normalized !== '' ? ucfirst($normalized) : $selectedSemester; $semesterLabel = $normalized !== '' ? ucfirst($normalized) : $selectedSemester;
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
// Step 1: Get the highest existing homework_index // Step 1: Get the highest existing homework_index
$existingIndexes = $this->homeworkModel $existingIndexes = $this->homeworkModel
@@ -202,7 +205,7 @@ class HomeworkController extends Controller
->where('class_section_id', $classSectionId) ->where('class_section_id', $classSectionId)
//->where('teacher_id', $updatedBy) //->where('teacher_id', $updatedBy)
->whereIn('semester', $this->getSemesterVariants($semesterLabel)) ->whereIn('semester', $this->getSemesterVariants($semesterLabel))
->where('school_year', $this->schoolYear) ->where('school_year', $schoolYear)
->groupBy('homework_index') ->groupBy('homework_index')
->orderBy('homework_index', 'DESC') ->orderBy('homework_index', 'DESC')
->findAll(); ->findAll();
@@ -219,6 +222,7 @@ class HomeworkController extends Controller
// Step 2: Get all students in the class // Step 2: Get all students in the class
$students = $this->studentClassModel $students = $this->studentClassModel
->where('class_section_id', $classSectionId) ->where('class_section_id', $classSectionId)
->where('school_year', $schoolYear)
->findAll(); ->findAll();
// Step 3: Insert a new homework row for each student if not already exists // Step 3: Insert a new homework row for each student if not already exists
@@ -233,7 +237,7 @@ class HomeworkController extends Controller
->where('class_section_id', $classSectionId) ->where('class_section_id', $classSectionId)
//->where('teacher_id', $updatedBy) //->where('teacher_id', $updatedBy)
->whereIn('semester', $this->getSemesterVariants($semesterLabel)) ->whereIn('semester', $this->getSemesterVariants($semesterLabel))
->where('school_year', $this->schoolYear) ->where('school_year', $schoolYear)
->first(); ->first();
if ($existing) continue; if ($existing) continue;
@@ -246,7 +250,7 @@ class HomeworkController extends Controller
'homework_index' => $nextIndex, 'homework_index' => $nextIndex,
'score' => null, 'score' => null,
'semester' => $semesterLabel, 'semester' => $semesterLabel,
'school_year' => $this->schoolYear, 'school_year' => $schoolYear,
'created_at' => utc_now(), 'created_at' => utc_now(),
'updated_at' => utc_now() 'updated_at' => utc_now()
]; ];
@@ -269,15 +273,23 @@ class HomeworkController extends Controller
public function showHomework() public function showHomework()
{ {
$updatedBy = session()->get('user_id'); $updatedBy = session()->get('user_id');
$classSectionId = $this->getClassSectionIdForTeacher($updatedBy); $semester = $this->getSelectedSemester();
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$classSectionId = (int) (
$this->request->getGet('class_section_id')
?? $this->request->getPost('class_section_id')
?? 0
);
if ($classSectionId <= 0) {
$classSectionId = (int) ($this->getClassSectionIdForTeacher($updatedBy, $schoolYear, $semester) ?? 0);
}
if (!$classSectionId) { if (!$classSectionId) {
return redirect()->back()->with('status', 'No class section found for the current teacher.'); return redirect()->back()->with('status', 'No class section found for the current teacher.');
} }
session()->set('class_section_id', $classSectionId); session()->set('class_section_id', $classSectionId);
$semester = $this->getSelectedSemester(); $homeworkHeaders = $this->getHomeworkHeaders($classSectionId, $semester, $schoolYear);
$homeworkHeaders = $this->getHomeworkHeaders($classSectionId, $semester, $this->schoolYear);
if (empty($homeworkHeaders)) { if (empty($homeworkHeaders)) {
$homeworkHeaders = [1]; $homeworkHeaders = [1];
} }
@@ -285,15 +297,15 @@ class HomeworkController extends Controller
$classSectionId, $classSectionId,
$homeworkHeaders, $homeworkHeaders,
$semester, $semester,
$this->schoolYear $schoolYear
); );
$missingOkMap = $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $this->schoolYear, 'homework'); $missingOkMap = $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $schoolYear, 'homework');
return view('teacher/add_homework', [ return view('teacher/add_homework', [
'students' => $students, 'students' => $students,
'homeworkHeaders' => $homeworkHeaders, 'homeworkHeaders' => $homeworkHeaders,
'semester' => $semester, 'semester' => $semester,
'schoolYear' => $this->schoolYear, 'schoolYear' => $schoolYear,
'class_section_id' => $classSectionId, 'class_section_id' => $classSectionId,
'missingOkMap' => $missingOkMap, 'missingOkMap' => $missingOkMap,
]); ]);
@@ -503,9 +515,16 @@ class HomeworkController extends Controller
return $students; return $students;
} }
private function getClassSectionIdForTeacher($updatedBy) private function getClassSectionIdForTeacher($updatedBy, ?string $schoolYear = null, ?string $semester = null)
{ {
$class = $this->teacherClassModel->where('teacher_id', $updatedBy)->first(); $builder = $this->teacherClassModel->where('teacher_id', $updatedBy);
if ($schoolYear !== null && $schoolYear !== '') {
$builder->where('school_year', $schoolYear);
}
if ($semester !== null && $semester !== '') {
$builder->where('semester', $semester);
}
$class = $builder->first();
return $class['class_section_id'] ?? null; return $class['class_section_id'] ?? null;
} }
+2 -18
View File
@@ -1644,23 +1644,8 @@ private function getGradeLevel($grade): array
return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.'); return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.');
} }
$currentSchoolYear = $this->schoolYear; $currentSchoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$selectedYear = $currentSchoolYear;
// Get available school years from invoices
$schoolYears = $this->invoiceModel
->select('school_year')
->distinct()
->where('parent_id', $parentId)
->orderBy('school_year', 'DESC')
->findAll();
// Determine which year to show
$selectedYear = $this->request->getGet('school_year');
if (empty($selectedYear)) {
$hasCurrentYear = in_array($currentSchoolYear, array_column($schoolYears, 'school_year'));
$selectedYear = $hasCurrentYear ? $currentSchoolYear : (!empty($schoolYears) ? $schoolYears[0]['school_year'] : null);
}
// Fetch invoices for the selected year // Fetch invoices for the selected year
$invoices = []; $invoices = [];
@@ -1718,7 +1703,6 @@ private function getGradeLevel($grade): array
return view('/parent/invoice_payment', [ return view('/parent/invoice_payment', [
'invoices' => $invoices, 'invoices' => $invoices,
'schoolYears' => $schoolYears,
'selectedYear' => $selectedYear, 'selectedYear' => $selectedYear,
'currentSchoolYear' => $currentSchoolYear, 'currentSchoolYear' => $currentSchoolYear,
'dueDate' => $this->dueDate, 'dueDate' => $this->dueDate,
@@ -58,7 +58,7 @@ class ParentAttendanceReportController extends BaseController
[$sundays, $defaultDate] = $this->computeSundays(); [$sundays, $defaultDate] = $this->computeSundays();
// Load upcoming reports for preview/edit (today and forward within this school year) // Load upcoming reports for preview/edit (today and forward within this school year)
$schoolYear = (string) $this->configModel->getConfig('school_year'); $schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
$todayYmd = local_date(utc_now(), 'Y-m-d'); $todayYmd = local_date(utc_now(), 'Y-m-d');
$previewRows = $this->reportModel->builder() $previewRows = $this->reportModel->builder()
->select('parent_attendance_reports.*, s.firstname, s.lastname') ->select('parent_attendance_reports.*, s.firstname, s.lastname')
@@ -141,6 +141,7 @@ class ParentAttendanceReportController extends BaseController
$dismissTime = $post['dismiss_time'] ?? null; $dismissTime = $post['dismiss_time'] ?? null;
$reasonRaw = $post['reason'] ?? null; $reasonRaw = $post['reason'] ?? null;
$reason = is_string($reasonRaw) ? trim($reasonRaw) : null; $reason = is_string($reasonRaw) ? trim($reasonRaw) : null;
$schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
// Enforce Sunday-only and future-or-today selection // Enforce Sunday-only and future-or-today selection
$todayCheck = new \DateTime('today'); $todayCheck = new \DateTime('today');
@@ -157,7 +158,7 @@ class ParentAttendanceReportController extends BaseController
} }
$validDates = []; $validDates = [];
$cap = $this->firstSundayOfJune((string) $this->configModel->getConfig('school_year')); $cap = $this->firstSundayOfJune($schoolYear);
$lateCutoff = null; $lateCutoff = null;
$lateNow = null; $lateNow = null;
@@ -236,7 +237,6 @@ class ParentAttendanceReportController extends BaseController
// Upper bound already enforced by $validDates building // Upper bound already enforced by $validDates building
$semester = (string) $this->configModel->getConfig('semester'); $semester = (string) $this->configModel->getConfig('semester');
$schoolYear = (string) $this->configModel->getConfig('school_year');
$semesterResolver = new SemesterRangeService($this->configModel); $semesterResolver = new SemesterRangeService($this->configModel);
$inserted = 0; $inserted = 0;
@@ -493,7 +493,7 @@ class ParentAttendanceReportController extends BaseController
} }
// Upper bound: first Sunday of June (school year end) // Upper bound: first Sunday of June (school year end)
$schoolYear = (string) $this->configModel->getConfig('school_year'); $schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
$cap = $this->firstSundayOfJune($schoolYear); $cap = $this->firstSundayOfJune($schoolYear);
$dates = []; $dates = [];
@@ -1458,12 +1458,15 @@ class ParentAttendanceReportController extends BaseController
return $this->response->setJSON(['ok' => true, 'students' => []]); return $this->response->setJSON(['ok' => true, 'students' => []]);
} }
$schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
// Fetch existing submissions for these students on this date // Fetch existing submissions for these students on this date
$rows = $this->reportModel->builder() $rows = $this->reportModel->builder()
->select('parent_attendance_reports.student_id, parent_attendance_reports.type, parent_attendance_reports.report_date, s.firstname, s.lastname') ->select('parent_attendance_reports.student_id, parent_attendance_reports.type, parent_attendance_reports.report_date, s.firstname, s.lastname')
->join('students s', 's.id = parent_attendance_reports.student_id', 'left') ->join('students s', 's.id = parent_attendance_reports.student_id', 'left')
->whereIn('parent_attendance_reports.report_date', $dateValues) ->whereIn('parent_attendance_reports.report_date', $dateValues)
->whereIn('parent_attendance_reports.student_id', $studentIds) ->whereIn('parent_attendance_reports.student_id', $studentIds)
->where('parent_attendance_reports.school_year', $schoolYear)
->get()->getResultArray(); ->get()->getResultArray();
$map = []; $map = [];
+4 -31
View File
@@ -164,10 +164,7 @@ class ParentController extends BaseController
return redirect()->back()->with('error', 'Parent session not found.'); return redirect()->back()->with('error', 'Parent session not found.');
} }
$currentSchoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? '')); $selectedYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
// Get selected school year (no semester filter on parent view)
$selectedYear = $this->request->getVar('school_year') ?? $currentSchoolYear;
// Build query to retrieve attendance // Build query to retrieve attendance
$builder = $this->db->table('attendance_data'); $builder = $this->db->table('attendance_data');
@@ -180,19 +177,10 @@ class ParentController extends BaseController
$query = $builder->get(); $query = $builder->get();
$attendanceResults = $query->getResultArray(); $attendanceResults = $query->getResultArray();
// Get list of available school years
$schoolYears = $this->db->table('attendance_data')
->select('school_year')
->distinct()
->orderBy('school_year', 'DESC')
->get()
->getResultArray();
// If no records found, set a flag to show message in the view // If no records found, set a flag to show message in the view
if (empty($attendanceResults)) { if (empty($attendanceResults)) {
return view('/parent/attendance', [ return view('/parent/attendance', [
'attendance' => null, 'attendance' => null,
'schoolYears' => $schoolYears,
'selectedYear' => $selectedYear, 'selectedYear' => $selectedYear,
'selectedSemester' => null, 'selectedSemester' => null,
//'error' => 'No attendance records found for the selected school year and semester.' //'error' => 'No attendance records found for the selected school year and semester.'
@@ -202,7 +190,6 @@ class ParentController extends BaseController
// Return view with attendance results // Return view with attendance results
return view('/parent/attendance', [ return view('/parent/attendance', [
'attendance' => $attendanceResults, 'attendance' => $attendanceResults,
'schoolYears' => $schoolYears,
'selectedYear' => $selectedYear, 'selectedYear' => $selectedYear,
'selectedSemester' => null, 'selectedSemester' => null,
'error' => null 'error' => null
@@ -212,7 +199,6 @@ class ParentController extends BaseController
return view('/parent/attendance', [ return view('/parent/attendance', [
'attendance' => null, 'attendance' => null,
'schoolYears' => [],
'selectedYear' => $selectedYear ?? null, 'selectedYear' => $selectedYear ?? null,
'selectedSemester' => null, 'selectedSemester' => null,
'error' => 'Failed to retrieve attendance data. Please try again later.' 'error' => 'Failed to retrieve attendance data. Please try again later.'
@@ -269,9 +255,9 @@ class ParentController extends BaseController
return redirect()->back()->with('error', 'Configuration error: School year missing.'); return redirect()->back()->with('error', 'Configuration error: School year missing.');
} }
// Get selected school year from request or fallback $context = $this->resolveSchoolYearContext();
$selectedYear = $this->request->getGet('school_year') ?? $this->schoolYear; $selectedYear = $context->yearName();
$isEditable = ($selectedYear === $this->schoolYear); $isEditable = ! $context->isReadonly();
// Get parent ID from session // Get parent ID from session
$parentId = session()->get('user_id'); $parentId = session()->get('user_id');
@@ -370,22 +356,9 @@ class ParentController extends BaseController
); );
} }
// Fetch available school years
$schoolYears = $this->db->table('enrollments')
->select('school_year')
->distinct()
->orderBy('school_year', 'DESC')
->get()
->getResultArray();
if (empty($schoolYears)) {
$schoolYears[] = ['school_year' => $this->schoolYear];
}
// Render view // Render view
return view('/parent/enroll_classes', [ return view('/parent/enroll_classes', [
'students' => $students, 'students' => $students,
'schoolYears' => $schoolYears,
'selectedYear' => $selectedYear, 'selectedYear' => $selectedYear,
'isEditable' => $isEditable, 'isEditable' => $isEditable,
'withdrawalDeadline' => $this->withdrawalDeadline, 'withdrawalDeadline' => $this->withdrawalDeadline,
+28 -13
View File
@@ -5,7 +5,7 @@ namespace App\Controllers\View;
use App\Models\ProjectModel; use App\Models\ProjectModel;
use App\Models\StudentModel; use App\Models\StudentModel;
use App\Models\StudentClassModel; use App\Models\StudentClassModel;
use CodeIgniter\Controller; use App\Controllers\BaseController;
use App\Models\TeacherClassModel; use App\Models\TeacherClassModel;
use App\Models\ConfigurationModel; use App\Models\ConfigurationModel;
use RuntimeException; use RuntimeException;
@@ -15,7 +15,7 @@ use App\Models\GradingLockModel;
use App\Models\MissingScoreOverrideModel; use App\Models\MissingScoreOverrideModel;
class ProjectController extends Controller class ProjectController extends BaseController
{ {
protected $db; protected $db;
protected ConfigurationModel $configModel; protected ConfigurationModel $configModel;
@@ -42,16 +42,26 @@ class ProjectController extends Controller
$studentModel = new StudentModel(); $studentModel = new StudentModel();
$projectModel = new ProjectModel(); $projectModel = new ProjectModel();
$teacherClass = $teacherClassModel->where('teacher_id', $updatedBy)->first();
if (!$teacherClass) {
return redirect()->back()->with('status', 'No class section found for the current teacher.');
}
$classSectionId = $teacherClass['class_section_id'];
session()->set('class_section_id', $classSectionId);
$semester = $this->getTeacherSelectedSemester(); $semester = $this->getTeacherSelectedSemester();
$schoolYear = $this->getTeacherSchoolYear(); $schoolYear = $this->getTeacherSchoolYear();
$classSectionId = (int) (
$this->request->getGet('class_section_id')
?? $this->request->getPost('class_section_id')
?? 0
);
if ($classSectionId <= 0) {
$teacherClass = $teacherClassModel
->where('teacher_id', $updatedBy)
->where('school_year', $schoolYear)
->where('semester', $semester)
->first();
if (!$teacherClass) {
return redirect()->back()->with('status', 'No class section found for the current teacher.');
}
$classSectionId = (int) $teacherClass['class_section_id'];
}
session()->set('class_section_id', $classSectionId);
$projectRows = $projectModel $projectRows = $projectModel
->select('id, project_index') ->select('id, project_index')
@@ -71,6 +81,7 @@ class ProjectController extends Controller
$studentsClasses = $studentClassModel $studentsClasses = $studentClassModel
->active() ->active()
->where('student_class.class_section_id', $classSectionId) ->where('student_class.class_section_id', $classSectionId)
->where('student_class.school_year', $schoolYear)
->findAll(); ->findAll();
$students = []; $students = [];
@@ -115,7 +126,7 @@ class ProjectController extends Controller
]); ]);
} }
public function updateProjectScores(array $scores = null, int $updatedBy = null, int $classSectionId = null) public function updateProjectScores(?array $scores = null, ?int $updatedBy = null, ?int $classSectionId = null)
{ {
$scores = $this->request->getPost('scores'); $scores = $this->request->getPost('scores');
$projectModel = new ProjectModel(); $projectModel = new ProjectModel();
@@ -127,6 +138,7 @@ class ProjectController extends Controller
$classSectionId = session()->get('class_section_id'); $classSectionId = session()->get('class_section_id');
$semester = $this->getTeacherSelectedSemester(); $semester = $this->getTeacherSelectedSemester();
$schoolYear = $this->getTeacherSchoolYear(); $schoolYear = $this->getTeacherSchoolYear();
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$classSectionId = (int) ($classSectionId ?? 0); $classSectionId = (int) ($classSectionId ?? 0);
if ($classSectionId > 0 && $this->isScoresLocked($classSectionId, $semester, $schoolYear)) { if ($classSectionId > 0 && $this->isScoresLocked($classSectionId, $semester, $schoolYear)) {
return redirect()->back()->with('error', 'Scores are locked for this class. Unlock to edit.'); return redirect()->back()->with('error', 'Scores are locked for this class. Unlock to edit.');
@@ -190,7 +202,8 @@ class ProjectController extends Controller
'student_id' => $studentId, 'student_id' => $studentId,
'project_index' => $index, 'project_index' => $index,
'class_section_id' => $classSectionId, 'class_section_id' => $classSectionId,
'semester' => $semester 'semester' => $semester,
'school_year' => $schoolYear,
])->first(); ])->first();
$data = [ $data = [
@@ -231,6 +244,7 @@ class ProjectController extends Controller
$classSectionId = session()->get('class_section_id'); $classSectionId = session()->get('class_section_id');
$semester = $this->getTeacherSelectedSemester(); $semester = $this->getTeacherSelectedSemester();
$schoolYear = $this->getTeacherSchoolYear(); $schoolYear = $this->getTeacherSchoolYear();
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$projectModel = new ProjectModel(); $projectModel = new ProjectModel();
$studentClassModel = new StudentClassModel(); $studentClassModel = new StudentClassModel();
@@ -257,6 +271,7 @@ class ProjectController extends Controller
$students = $studentClassModel $students = $studentClassModel
->active() ->active()
->where('student_class.class_section_id', $classSectionId) ->where('student_class.class_section_id', $classSectionId)
->where('student_class.school_year', $schoolYear)
->findAll(); ->findAll();
foreach ($students as $student) { foreach ($students as $student) {
@@ -533,6 +548,6 @@ class ProjectController extends Controller
private function getTeacherSchoolYear(): string private function getTeacherSchoolYear(): string
{ {
return (string) $this->schoolYear; return $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
} }
} }
+13 -10
View File
@@ -2,7 +2,7 @@
namespace App\Controllers\View; namespace App\Controllers\View;
use CodeIgniter\Controller; use App\Controllers\BaseController;
use App\Models\TeacherClassModel; use App\Models\TeacherClassModel;
use App\Models\StudentClassModel; use App\Models\StudentClassModel;
use App\Models\StudentModel; use App\Models\StudentModel;
@@ -17,7 +17,7 @@ use Config\Services;
use App\Models\GradingLockModel; use App\Models\GradingLockModel;
use App\Models\MissingScoreOverrideModel; use App\Models\MissingScoreOverrideModel;
class QuizController extends Controller class QuizController extends BaseController
{ {
protected $db; protected $db;
protected $semesterScoreService; protected $semesterScoreService;
@@ -50,7 +50,7 @@ class QuizController extends Controller
$this->missingScoreOverrideModel = new MissingScoreOverrideModel(); $this->missingScoreOverrideModel = new MissingScoreOverrideModel();
} }
public function updateQuizScores(array $scores = null, int $updatedBy = null, int $classSectionId = null) public function updateQuizScores(?array $scores = null, ?int $updatedBy = null, ?int $classSectionId = null)
{ {
$scores = $this->request->getPost('scores'); $scores = $this->request->getPost('scores');
log_message('error', '✅ Raw Scores: ' . print_r($scores, true)); log_message('error', '✅ Raw Scores: ' . print_r($scores, true));
@@ -60,7 +60,8 @@ class QuizController extends Controller
} }
$classSectionId = $this->request->getPost('class_section_id') ?? session()->get('class_section_id'); $classSectionId = $this->request->getPost('class_section_id') ?? session()->get('class_section_id');
$semester = $this->request->getPost('semester') ?? $this->getTeacherSelectedSemester(); $semester = $this->request->getPost('semester') ?? $this->getTeacherSelectedSemester();
$schoolYear = $this->request->getPost('school_year') ?? $this->schoolYear; $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$classSectionId = (int) ($classSectionId ?? 0); $classSectionId = (int) ($classSectionId ?? 0);
if ($classSectionId > 0 && $this->isScoresLocked($classSectionId, $semester, $schoolYear)) { if ($classSectionId > 0 && $this->isScoresLocked($classSectionId, $semester, $schoolYear)) {
return redirect()->back()->with('error', 'Scores are locked for this class. Unlock to edit.'); return redirect()->back()->with('error', 'Scores are locked for this class. Unlock to edit.');
@@ -206,7 +207,7 @@ class QuizController extends Controller
// 3) Headers: distinct quiz_index for this class/term // 3) Headers: distinct quiz_index for this class/term
$semester = $this->getTeacherSelectedSemester(); $semester = $this->getTeacherSelectedSemester();
$schoolYear = $this->schoolYear; $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$quizHeaderRows = $this->quizModel->select('quiz_index') $quizHeaderRows = $this->quizModel->select('quiz_index')
->where('class_section_id', $classSectionId) ->where('class_section_id', $classSectionId)
->where('school_year', $schoolYear) ->where('school_year', $schoolYear)
@@ -272,9 +273,9 @@ class QuizController extends Controller
'students' => $students, 'students' => $students,
'quizHeaders' => $quizHeaders, // e.g., [1,2,3] 'quizHeaders' => $quizHeaders, // e.g., [1,2,3]
'semester' => $semester, 'semester' => $semester,
'schoolYear' => $this->schoolYear, 'schoolYear' => $schoolYear,
'classSectionId' => $classSectionId, 'classSectionId' => $classSectionId,
'missingOkMap' => $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $this->schoolYear, 'quiz'), 'missingOkMap' => $this->missingScoreOverrideModel->getOverridesMap($classSectionId, $semester, $schoolYear, 'quiz'),
]); ]);
} }
@@ -294,7 +295,8 @@ class QuizController extends Controller
try { try {
$semester = $this->getTeacherSelectedSemester(); $semester = $this->getTeacherSelectedSemester();
$schoolYear = $this->schoolYear; $schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$this->assertSchoolYearWritable($this->resolveSchoolYearContext());
$existingQuizNumbers = $this->quizModel $existingQuizNumbers = $this->quizModel
->select('quiz_index') ->select('quiz_index')
->where('class_section_id', $classSectionId) ->where('class_section_id', $classSectionId)
@@ -323,6 +325,7 @@ class QuizController extends Controller
$students = $this->studentClassModel $students = $this->studentClassModel
->where('class_section_id', $classSectionId) ->where('class_section_id', $classSectionId)
->where('school_year', $schoolYear)
->findAll(); ->findAll();
if (empty($students)) { if (empty($students)) {
@@ -341,8 +344,8 @@ class QuizController extends Controller
'quiz_index' => $nextQuizNumber, 'quiz_index' => $nextQuizNumber,
'class_section_id' => $classSectionId, 'class_section_id' => $classSectionId,
'updated_by' => $updatedBy, 'updated_by' => $updatedBy,
'semester' => $this->semester, 'semester' => $semester,
'school_year' => $this->schoolYear, 'school_year' => $schoolYear,
]) ])
->first(); ->first();
@@ -46,9 +46,11 @@ class ScoreCommentController extends BaseController
public function saveComments() public function saveComments()
{ {
$schoolYearContext = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($schoolYearContext);
$comments = $this->request->getPost('comments'); $comments = $this->request->getPost('comments');
$classSectionId = (int)($this->request->getPost('class_section_id') ?? session()->get('class_section_id') ?? 0); $classSectionId = (int)($this->request->getPost('class_section_id') ?? session()->get('class_section_id') ?? 0);
$schoolYear = (string) ($this->request->getPost('school_year') ?? $this->schoolYear); $schoolYear = $schoolYearContext->yearName();
$rawSelectedSemester = $this->request->getPost('selected_semester'); $rawSelectedSemester = $this->request->getPost('selected_semester');
$normalizedSelected = $this->normalizeSemesterSelection($rawSelectedSemester); $normalizedSelected = $this->normalizeSemesterSelection($rawSelectedSemester);
if ($normalizedSelected !== '') { if ($normalizedSelected !== '') {
+21 -35
View File
@@ -2,7 +2,7 @@
namespace App\Controllers\View; namespace App\Controllers\View;
use CodeIgniter\Controller; use App\Controllers\BaseController;
use App\Models\TeacherClassModel; use App\Models\TeacherClassModel;
use App\Models\StudentClassModel; use App\Models\StudentClassModel;
use App\Models\StudentModel; use App\Models\StudentModel;
@@ -28,7 +28,7 @@ use App\Models\MissingScoreOverrideModel;
class ScoreController extends Controller class ScoreController extends BaseController
{ {
protected $semesterScoreService; protected $semesterScoreService;
protected $db; protected $db;
@@ -88,6 +88,8 @@ class ScoreController extends Controller
public function index() public function index()
{ {
log_message('debug', 'ScoreController::index invoked'); log_message('debug', 'ScoreController::index invoked');
$schoolYearContext = $this->resolveSchoolYearContext();
$this->schoolYear = $schoolYearContext->yearName();
$semesterChoiceParam = $this->request->getGet('semester_choice'); $semesterChoiceParam = $this->request->getGet('semester_choice');
$session = session(); $session = session();
if ($this->request->getGet('choose_semester') !== null) { if ($this->request->getGet('choose_semester') !== null) {
@@ -139,25 +141,13 @@ class ScoreController extends Controller
); );
if (empty($assignments)) { if (empty($assignments)) {
// Fallback: any class regardless of term log_message(
$fallback = $this->teacherClassModel 'info',
->where('teacher_id', $teacherId) "No {$effectiveSchoolYear} {$effectiveSemester} class section assigned to teacher ID: {$teacherId}"
->first(); );
if ($fallback) { return redirect()
$assignments = [[ ->to('no-classes')
'class_section_id' => $fallback['class_section_id'], ->with('message', 'You do not have an assigned class for the selected school year and semester. Please contact the administration.');
'class_section_name' => null,
'school_year' => $fallback['school_year'] ?? $effectiveSchoolYear,
'semester' => $fallback['semester'] ?? $effectiveSemester,
]];
$effectiveSemester = $fallback['semester'] ?? $effectiveSemester;
$effectiveSchoolYear = $fallback['school_year'] ?? $effectiveSchoolYear;
} else {
log_message('info', 'No class section assigned to teacher ID: ' . $teacherId);
return redirect()
->to('no-classes')
->with('message', 'You do not have an assigned class yet. Please contact the administration.');
}
} }
$allowedIds = array_map(static fn($a) => (int)($a['class_section_id'] ?? 0), $assignments); $allowedIds = array_map(static fn($a) => (int)($a['class_section_id'] ?? 0), $assignments);
@@ -222,6 +212,7 @@ class ScoreController extends Controller
$csRow = $this->db->table('classSection') $csRow = $this->db->table('classSection')
->select('class_section_name') ->select('class_section_name')
->where('class_section_id', $classSectionId) ->where('class_section_id', $classSectionId)
->where('school_year', $effectiveSchoolYear)
->get()->getRowArray(); ->get()->getRowArray();
$classSectionName = $csRow['class_section_name'] ?? ''; $classSectionName = $csRow['class_section_name'] ?? '';
log_message('debug', "ScoreController::index teacher {$teacherId} classSection {$classSectionId} semester {$effectiveSemester}"); log_message('debug', "ScoreController::index teacher {$teacherId} classSection {$classSectionId} semester {$effectiveSemester}");
@@ -281,6 +272,7 @@ class ScoreController extends Controller
'focusTarget' => $focusTarget, 'focusTarget' => $focusTarget,
'schoolYear' => $effectiveSchoolYear, 'schoolYear' => $effectiveSchoolYear,
'scoresLocked' => $scoresLocked, 'scoresLocked' => $scoresLocked,
'isSchoolYearReadonly' => $schoolYearContext->isReadonly(),
]; ];
return view('/teacher/scores', $data); return view('/teacher/scores', $data);
@@ -288,11 +280,13 @@ class ScoreController extends Controller
public function submitScoresLock() public function submitScoresLock()
{ {
$schoolYearContext = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($schoolYearContext);
$classSectionId = (int) ($this->request->getPost('class_section_id') ?? session()->get('class_section_id') ?? 0); $classSectionId = (int) ($this->request->getPost('class_section_id') ?? session()->get('class_section_id') ?? 0);
$semesterRaw = (string) ($this->request->getPost('semester') ?? $this->request->getPost('selected_semester') ?? ''); $semesterRaw = (string) ($this->request->getPost('semester') ?? $this->request->getPost('selected_semester') ?? '');
$semesterNormalized = $this->normalizeSemester($semesterRaw); $semesterNormalized = $this->normalizeSemester($semesterRaw);
$semester = $semesterNormalized !== '' ? ucfirst($semesterNormalized) : $this->semester; $semester = $semesterNormalized !== '' ? ucfirst($semesterNormalized) : $this->semester;
$schoolYear = (string) ($this->request->getPost('school_year') ?? $this->schoolYear); $schoolYear = $schoolYearContext->yearName();
if ($classSectionId <= 0 || $semester === '' || $schoolYear === '') { if ($classSectionId <= 0 || $semester === '' || $schoolYear === '') {
return redirect()->back()->with('error', 'Missing class section or term.'); return redirect()->back()->with('error', 'Missing class section or term.');
@@ -496,6 +490,7 @@ class ScoreController extends Controller
$studentClasses = $this->studentClassModel $studentClasses = $this->studentClassModel
->active() ->active()
->where('student_class.class_section_id', $classSectionId) ->where('student_class.class_section_id', $classSectionId)
->where('student_class.school_year', $schoolYear)
->findAll(); ->findAll();
foreach ($studentClasses as $studentClass) { foreach ($studentClasses as $studentClass) {
@@ -1086,6 +1081,9 @@ class ScoreController extends Controller
public function updateScores(string $table, ?string $redirectUrl = null) public function updateScores(string $table, ?string $redirectUrl = null)
{ {
$schoolYearContext = $this->resolveSchoolYearContext();
$this->assertSchoolYearWritable($schoolYearContext);
$this->schoolYear = $schoolYearContext->yearName();
$builder = $this->db->table($table); $builder = $this->db->table($table);
$scores = $this->request->getPost('final_score'); // name must match input form $scores = $this->request->getPost('final_score'); // name must match input form
@@ -1322,18 +1320,7 @@ public function viewStudentScore()
return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.'); return redirect()->back()->with('error', 'Unable to retrieve student data. Please contact support.');
} }
// Handle selected school year (parents can view scores for any available year) $selectedYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
$selectedYear = $this->request->getVar('school_year') ?? $this->schoolYear;
// Fetch distinct school years only from semester_scores
$query = $this->db->table('semester_scores')
->select('school_year')
->distinct()
->get();
$schoolYears = array_column($query->getResultArray(), 'school_year');
$schoolYears = array_unique($schoolYears);
rsort($schoolYears);
// Initialize scores array // Initialize scores array
$scores = []; $scores = [];
@@ -1402,7 +1389,6 @@ $students = $this->db->table('students')
return view('/parent/scores', [ return view('/parent/scores', [
'organizedScores' => $scores, 'organizedScores' => $scores,
'schoolYears' => $schoolYears,
'selectedYear' => $selectedYear, 'selectedYear' => $selectedYear,
'semesterOrder' => ['Fall', 'Spring'], 'semesterOrder' => ['Fall', 'Spring'],
'showExamScores' => $releaseAny, 'showExamScores' => $releaseAny,
+2
View File
@@ -21,6 +21,7 @@ class ClassProgressReportModel extends Model
protected $allowedFields = [ protected $allowedFields = [
'class_section_id', 'class_section_id',
'teacher_id', 'teacher_id',
'school_year',
'week_start', 'week_start',
'week_end', 'week_end',
'subject', 'subject',
@@ -50,6 +51,7 @@ class ClassProgressReportModel extends Model
protected $validationRules = [ protected $validationRules = [
'class_section_id' => 'required|integer', 'class_section_id' => 'required|integer',
'teacher_id' => 'required|integer', 'teacher_id' => 'required|integer',
'school_year' => 'permit_empty|string|max_length[9]',
'week_start' => 'required|valid_date[Y-m-d]', 'week_start' => 'required|valid_date[Y-m-d]',
'week_end' => 'required|valid_date[Y-m-d]', 'week_end' => 'required|valid_date[Y-m-d]',
'subject' => 'required|string|max_length[160]', 'subject' => 'required|string|max_length[160]',
+1
View File
@@ -15,6 +15,7 @@ class PrintRequestModel extends Model
'teacher_id', 'teacher_id',
'admin_id', 'admin_id',
'class_id', 'class_id',
'school_year',
'file_path', 'file_path',
'page_selection', 'page_selection',
'num_copies', 'num_copies',
+79 -3
View File
@@ -24,11 +24,87 @@ class SubjectCurriculumModel extends Model
public function getOptionsForClass(int $classId, string $subject): array public function getOptionsForClass(int $classId, string $subject): array
{ {
return $this->where('class_id', $classId) $rows = $this->orderedOptionsBuilder()
->where('subject', $subject) ->where('subject_curriculum_items.class_id', $classId)
->where('subject_curriculum_items.subject', $subject)
->get()
->getResultArray();
if (! empty($rows)) {
return $rows;
}
$fallbackClassIds = $this->classIdsWithSameName($classId);
if (empty($fallbackClassIds)) {
return [];
}
$rows = $this->orderedOptionsBuilder()
->whereIn('subject_curriculum_items.class_id', $fallbackClassIds)
->where('subject_curriculum_items.subject', $subject)
->get()
->getResultArray();
return $this->uniqueCurriculumRows($rows);
}
private function orderedOptionsBuilder()
{
return $this->db->table($this->table)
->select('subject_curriculum_items.*')
->orderBy('unit_number', 'ASC') ->orderBy('unit_number', 'ASC')
->orderBy("CAST(SUBSTRING_INDEX(chapter_name, '.', 1) AS UNSIGNED)", 'ASC', false) ->orderBy("CAST(SUBSTRING_INDEX(chapter_name, '.', 1) AS UNSIGNED)", 'ASC', false)
->orderBy('chapter_name', 'ASC') ->orderBy('chapter_name', 'ASC')
->findAll(); ->orderBy('id', 'ASC');
}
private function classIdsWithSameName(int $classId): array
{
$class = $this->db->table('classes')
->select('class_name')
->where('id', $classId)
->get()
->getRowArray();
$className = trim((string) ($class['class_name'] ?? ''));
if ($className === '') {
return [];
}
$rows = $this->db->table('classes')
->select('id')
->where('class_name', $className)
->where('id !=', $classId)
->get()
->getResultArray();
return array_values(array_filter(array_map(
static fn (array $row): int => (int) ($row['id'] ?? 0),
$rows
)));
}
private function uniqueCurriculumRows(array $rows): array
{
$seen = [];
$unique = [];
foreach ($rows as $row) {
$key = implode('|', [
(string) ($row['subject'] ?? ''),
(string) ($row['unit_number'] ?? ''),
(string) ($row['unit_title'] ?? ''),
(string) ($row['chapter_name'] ?? ''),
]);
if (isset($seen[$key])) {
continue;
}
$seen[$key] = true;
$unique[] = $row;
}
return $unique;
} }
} }
@@ -4,30 +4,6 @@
<div class="wrapper"> <div class="wrapper">
<div class="content"></div> <div class="content"></div>
<h2 class="text-center mt-4 mb-3">Classes Lists</h2> <h2 class="text-center mt-4 mb-3">Classes Lists</h2>
<form method="get" class="row g-2 align-items-center justify-content-center mb-3">
<div class="col-auto">
<label class="form-label mb-0">School year</label>
</div>
<div class="col-auto">
<select name="school_year" class="form-select form-select-sm" style="min-width: 200px;">
<?php if (!empty($schoolYears)): ?>
<?php foreach ($schoolYears as $yearOption): ?>
<option value="<?= esc($yearOption) ?>" <?= ($selectedYear === $yearOption ? 'selected' : '') ?>>
<?= esc($yearOption) ?>
</option>
<?php endforeach; ?>
<?php else: ?>
<option value="<?= esc($schoolYear ?? '') ?>" <?= ($selectedYear === ($schoolYear ?? '') ? 'selected' : '') ?>>
<?= esc($schoolYear ?? 'Current') ?>
</option>
<?php endif; ?>
</select>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-secondary btn-sm">Apply</button>
<a class="btn btn-outline-secondary btn-sm" href="<?= base_url('administrator/class_assignment') ?>">Reset</a>
</div>
</form>
<?php if (session()->getFlashdata('message')): ?> <?php if (session()->getFlashdata('message')): ?>
<div class="alert alert-success"> <div class="alert alert-success">
-41
View File
@@ -19,47 +19,6 @@ if (empty($schoolYears) && $schoolYear !== '') {
<div class="wrapper below-sixty-wrapper"> <div class="wrapper below-sixty-wrapper">
<h2 class="text-center mt-4 mb-4 below-sixty-title">Below 60 Summary</h2> <h2 class="text-center mt-4 mb-4 below-sixty-title">Below 60 Summary</h2>
<!-- School year filter only -->
<div class="card shadow-sm mb-3">
<div class="card-body py-3">
<form method="get"
action="<?= site_url('grading/below-60') ?>"
class="row g-2 align-items-end justify-content-center">
<input type="hidden" name="semester" value="fall">
<div class="col-12 col-sm-auto">
<label class="form-label mb-1 small fw-semibold">School Year</label>
<?php if (!empty($schoolYears)): ?>
<select name="school_year"
class="form-select form-select-sm"
style="min-width:160px;">
<?php foreach ($schoolYears as $yr): ?>
<option value="<?= esc($yr) ?>" <?= (string)$yr === (string)$schoolYear ? 'selected' : '' ?>>
<?= esc($yr) ?>
</option>
<?php endforeach; ?>
</select>
<?php else: ?>
<input type="text"
name="school_year"
class="form-control form-control-sm"
style="min-width:160px;"
value="<?= esc($schoolYear) ?>"
placeholder="2025-2026">
<?php endif; ?>
</div>
<div class="col-12 col-sm-auto">
<button type="submit" class="btn btn-sm btn-primary">
<i class="bi bi-funnel me-1"></i>Apply
</button>
</div>
</form>
</div>
</div>
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2"> <div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
<div class="text-muted"> <div class="text-muted">
Fall • <?= esc($schoolYear) ?> Fall • <?= esc($schoolYear) ?>
+14 -1
View File
@@ -23,7 +23,20 @@
<body> <body>
<!-- Navbar --> <!-- Navbar -->
<?php include(__DIR__ . '/../partials/navbar.php'); ?> <?php include(__DIR__ . '/../partials/navbar.php'); ?>
<?= view('partials/school_year_selector') ?> <?php
$mainSchoolYearSelectorData = [];
if (!isset($schoolYearContext, $schoolYearOptions) && session()->get('user_id')) {
try {
$mainSchoolYearSelectorData = service('schoolYearViewData')->forCurrentRequest(service('request'));
$mainSchoolYearSelectorData['schoolYearSelectorEnabled'] = true;
} catch (\Throwable $e) {
log_message('warning', 'Unable to load main school-year selector: {message}', [
'message' => $e->getMessage(),
]);
}
}
?>
<?= view('partials/school_year_selector', $mainSchoolYearSelectorData) ?>
<div class="container my-4"> <div class="container my-4">
<?= $this->renderSection('content') ?> <?= $this->renderSection('content') ?>
+14 -1
View File
@@ -165,7 +165,20 @@ html, body { overflow-x: hidden; }
<body data-app-menu-mode="<?= esc($appMenuMode) ?>"> <body data-app-menu-mode="<?= esc($appMenuMode) ?>">
<?php include(__DIR__ . '/../partials/navbar.php'); ?> <?php include(__DIR__ . '/../partials/navbar.php'); ?>
<?= view('partials/school_year_selector') ?> <?php
$mainSchoolYearSelectorData = [];
if (!isset($schoolYearContext, $schoolYearOptions) && session()->get('user_id')) {
try {
$mainSchoolYearSelectorData = service('schoolYearViewData')->forCurrentRequest(service('request'));
$mainSchoolYearSelectorData['schoolYearSelectorEnabled'] = true;
} catch (\Throwable $e) {
log_message('warning', 'Unable to load main school-year selector: {message}', [
'message' => $e->getMessage(),
]);
}
}
?>
<?= view('partials/school_year_selector', $mainSchoolYearSelectorData) ?>
<?php <?php
$uri = service('uri'); $uri = service('uri');
+4 -16
View File
@@ -2,22 +2,10 @@
<?= $this->section('content') ?> <?= $this->section('content') ?>
<div class="container my-5"> <div class="container my-5">
<h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Attendance Record</h2> <h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Attendance Record</h2>
<?php if (!empty($selectedYear)): ?>
<div class="text-center text-muted small">School Year: <?= esc($selectedYear) ?></div>
<?php endif; ?>
</div> </div>
<!-- Filter Form -->
<form action="" method="get" class="form-inline">
<div class="d-flex align-items-center">
<select name="school_year" id="school_year" class="form-control me-3"> <!-- Added me-3 (right margin) -->
<?php foreach ($schoolYears as $year): ?>
<option value="<?= esc($year['school_year']) ?>" <?= $selectedYear === $year['school_year'] ? 'selected' : '' ?>>
<?= esc($year['school_year']) ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-success">Filter</button>
</div>
</form>
<!-- Error Message --> <!-- Error Message -->
<?php if (isset($error)): ?> <?php if (isset($error)): ?>
@@ -52,7 +40,7 @@
</tbody> </tbody>
</table> </table>
<?php else: ?> <?php else: ?>
<div class="alert alert-info mb-3 d-inline-block">No attendance records found for the selected school year.</div> <div class="alert alert-info mb-3 d-inline-block">No attendance records found for this school year.</div>
<?php endif; ?> <?php endif; ?>
</main> </main>
</div> </div>
+3 -13
View File
@@ -2,21 +2,11 @@
<?= $this->section('content') ?> <?= $this->section('content') ?>
<div class="container my-5"> <div class="container my-5">
<h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Enroll in Classes</h3> <h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Enroll in Classes</h3>
<?php if (!empty($selectedYear)): ?>
<div class="text-center text-muted small">School Year: <?= esc($selectedYear) ?></div>
<?php endif; ?>
</div> </div>
<!-- School Year Filter -->
<form action="<?= base_url('/parent/enroll_classes') ?>" method="get" class="form-inline mb-3">
<div class="d-flex align-items-center">
<select name="school_year" id="school_year" class="form-control me-3">
<?php foreach ($schoolYears as $year): ?>
<option value="<?= esc($year['school_year']) ?>" <?= $selectedYear === $year['school_year'] ? 'selected' : '' ?>>
<?= esc($year['school_year']) ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-success">Filter</button>
</div>
</form>
<?php <?php
// Put this near the very top of the file (before output), or right after the extend/section lines. // Put this near the very top of the file (before output), or right after the extend/section lines.
$tz = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $tz = (string) (config('School')->attendance['timezone'] ?? user_timezone());
+4 -29
View File
@@ -63,24 +63,11 @@ if (!function_exists('parseDbDateTime')) {
<div class="container my-5"> <div class="container my-5">
<h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Invoices and Payments</h3> <h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Invoices and Payments</h3>
<?php if (!empty($selectedYear)): ?>
<div class="text-center text-muted small">School Year: <?= esc($selectedYear) ?></div>
<?php endif; ?>
</div> </div>
<!-- School Year Filter -->
<form action="<?= base_url('/parent/invoice_payment') ?>" method="get" class="form-inline mb-3">
<div class="d-flex align-items-center justify-content-center">
<select name="school_year" id="school_year" class="form-control me-3">
<?php foreach ($schoolYears as $year): ?>
<option value="<?= esc($year['school_year']) ?>"
<?= ($selectedYear === $year['school_year']) ? 'selected' : '' ?>
<?= ($currentSchoolYear === $year['school_year']) ? 'data-current="true"' : '' ?>>
<?= esc($year['school_year']) ?><?= ($currentSchoolYear === $year['school_year']) ? ' (Current)' : '' ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-success">Filter</button>
</div>
</form>
<!-- Display Payment Notice / Deadline --> <!-- Display Payment Notice / Deadline -->
<?php <?php
$displayTz = user_timezone(); $displayTz = user_timezone();
@@ -198,7 +185,7 @@ $deadline = parseDbDateTime($dueDate, 'UTC', $displayTz); // format either DATE
<?php endif; ?> <?php endif; ?>
<?php else: ?> <?php else: ?>
<div class="alert alert-info mb-3 d-inline-block"> <div class="alert alert-info mb-3 d-inline-block">
No invoice found for the selected school year. No invoice found for this school year.
</div> </div>
<?php endif; ?> <?php endif; ?>
@@ -208,18 +195,6 @@ $deadline = parseDbDateTime($dueDate, 'UTC', $displayTz); // format either DATE
<?= $this->section('scripts') ?> <?= $this->section('scripts') ?>
<script> <script>
document.addEventListener('DOMContentLoaded', function() {
const dropdown = document.getElementById('school_year');
const options = dropdown.options;
for (let i = 0; i < options.length; i++) {
if (options[i].dataset.current === "true") {
options[i].style.fontWeight = 'bold';
if (!dropdown.value) dropdown.selectedIndex = i;
break;
}
}
});
function showPaymentDevelopmentMessage(event) { function showPaymentDevelopmentMessage(event) {
event.preventDefault(); event.preventDefault();
alert('The payment method is currently under development. Please contact the school administration at alrahma.isgl@gmail.com'); alert('The payment method is currently under development. Please contact the school administration at alrahma.isgl@gmail.com');
+3 -17
View File
@@ -3,24 +3,12 @@
<div class="container my-5"> <div class="container my-5">
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2"> <div class="d-flex flex-wrap align-items-center justify-content-between gap-2">
<h3 class="text-success mb-0" style="font-family: Arial, sans-serif;">Scores</h3> <h3 class="text-success mb-0" style="font-family: Arial, sans-serif;">Scores</h3>
<?php if (!empty($selectedYear)): ?>
<div class="text-muted small">School Year: <?= esc($selectedYear) ?></div>
<?php endif; ?>
</div> </div>
</div> </div>
<!-- Filter Form -->
<form action="" method="get" class="form-inline">
<div class="d-flex align-items-center">
<select name="school_year" id="school_year" class="form-control me-3"> <!-- Added me-3 (right margin) -->
<?php foreach ($schoolYears as $year): ?>
<option value="<?= esc($year) ?>" <?= $selectedYear === $year ? 'selected' : '' ?>>
<?= esc($year) ?>
</option>
<?php endforeach; ?>
</select>
<button type="submit" class="btn btn-success">Filter</button>
</div>
</form>
<!-- Scores Table --> <!-- Scores Table -->
<?php $yearScores = $organizedScores[$selectedYear] ?? []; ?> <?php $yearScores = $organizedScores[$selectedYear] ?? []; ?>
<?php $semesterOrder = $semesterOrder ?? ['Fall', 'Spring']; ?> <?php $semesterOrder = $semesterOrder ?? ['Fall', 'Spring']; ?>
@@ -104,12 +92,10 @@ $showExamScoresBySemester = $showExamScoresBySemester ?? [];
<?php <?php
$reportStudentId = (int) ($student['student_id'] ?? 0); $reportStudentId = (int) ($student['student_id'] ?? 0);
if ($reportStudentId > 0) { if ($reportStudentId > 0) {
$reportYear = (string) ($selectedYear ?? '');
$reportSemester = (string) ($semester ?? ''); $reportSemester = (string) ($semester ?? '');
$reportDate = date('Y-m-d'); $reportDate = date('Y-m-d');
$reportUrl = base_url('parent/report-cards/view/' . $reportStudentId); $reportUrl = base_url('parent/report-cards/view/' . $reportStudentId);
$query = http_build_query([ $query = http_build_query([
'school_year' => $reportYear,
'semester' => $reportSemester, 'semester' => $reportSemester,
'report_date' => $reportDate, 'report_date' => $reportDate,
]); ]);
+11 -5
View File
@@ -1,10 +1,16 @@
<?= $this->extend('layout/main_layout') ?> <?= $this->extend('layout/main_layout') ?>
<?= $this->section('content') ?> <?= $this->section('content') ?>
<?php $isSchoolYearReadonly = (bool) ($isSchoolYearReadonly ?? false); ?>
<div class="container-fluid py-5"> <div class="container-fluid py-5">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<h2>Print/Copy Requests</h2> <h2>Print/Copy Requests</h2>
<?php if ($isSchoolYearReadonly): ?>
<div class="alert alert-warning">
This school year is read-only. Existing requests are visible, but changes are disabled.
</div>
<?php endif; ?>
<!-- Print/Copy Request Tabs --> <!-- Print/Copy Request Tabs -->
<div class="card my-4"> <div class="card my-4">
@@ -109,7 +115,7 @@
You are not assigned to any class. Please contact the administrator to be able to submit print requests. You are not assigned to any class. Please contact the administrator to be able to submit print requests.
</div> </div>
<?php endif; ?> <?php endif; ?>
<button type="submit" class="btn btn-primary mt-3" <?= (!isset($class_id) || !$class_id) ? 'disabled' : '' ?>>Submit Request</button> <button type="submit" class="btn btn-primary mt-3" <?= (!isset($class_id) || !$class_id || $isSchoolYearReadonly) ? 'disabled' : '' ?>>Submit Request</button>
</div> </div>
</div> </div>
</form> </form>
@@ -164,7 +170,7 @@
<div class="form-text mt-2"> <div class="form-text mt-2">
No file upload is required; please hand the original document to the copy staff after submitting. No file upload is required; please hand the original document to the copy staff after submitting.
</div> </div>
<button type="submit" class="btn btn-outline-primary mt-3">Submit Copy Request</button> <button type="submit" class="btn btn-outline-primary mt-3" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Submit Copy Request</button>
</form> </form>
</div> </div>
</div> </div>
@@ -241,10 +247,10 @@
</td> </td>
<td> <td>
<div class="d-flex flex-wrap gap-1"> <div class="d-flex flex-wrap gap-1">
<?php if (in_array($request['status'], ['not_assigned', 'assigned'])): ?> <?php if (!$isSchoolYearReadonly && in_array($request['status'], ['not_assigned', 'assigned'])): ?>
<button type="button" class="btn btn-sm btn-warning mb-1" onclick='editRequest(<?= json_encode($request, JSON_HEX_APOS | JSON_HEX_QUOT) ?>)'>Edit</button> <button type="button" class="btn btn-sm btn-warning mb-1" onclick='editRequest(<?= json_encode($request, JSON_HEX_APOS | JSON_HEX_QUOT) ?>)'>Edit</button>
<?php endif; ?> <?php endif; ?>
<?php if ($request['status'] === 'not_assigned'): ?> <?php if (!$isSchoolYearReadonly && $request['status'] === 'not_assigned'): ?>
<button type="button" class="btn btn-sm btn-danger mb-1" onclick="deleteRequest(<?= $request['id'] ?>)">Delete</button> <button type="button" class="btn btn-sm btn-danger mb-1" onclick="deleteRequest(<?= $request['id'] ?>)">Delete</button>
<?php endif; ?> <?php endif; ?>
</div> </div>
@@ -328,7 +334,7 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save changes</button> <button type="submit" class="btn btn-primary" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Save changes</button>
</div> </div>
</form> </form>
</div> </div>
-15
View File
@@ -7,21 +7,6 @@
<h2 class="mb-0">Student Score Card</h2> <h2 class="mb-0">Student Score Card</h2>
<div class="text-muted small">Select a student to open their score card.</div> <div class="text-muted small">Select a student to open their score card.</div>
</div> </div>
<?php
$roleRaw = session()->get('role');
if (is_array($roleRaw)) {
$roleRaw = $roleRaw[0] ?? 'guest';
}
$role = strtolower((string)($roleRaw ?? 'guest'));
if (in_array($role, ['parent', 'parent_dashboard'], true)) {
$backUrl = base_url('/parent_dashboard');
} elseif (in_array($role, ['teacher', 'teacher_assistant', 'teacher_dashboard'], true)) {
$backUrl = base_url('/teacher_dashboard');
} else {
$backUrl = base_url('/');
}
?>
<a href="<?= $backUrl ?>" class="btn btn-outline-secondary">Back</a>
</div> </div>
<?php if (empty($students)): ?> <?php if (empty($students)): ?>
+12 -2
View File
@@ -1,12 +1,17 @@
<?= $this->extend('layout/main_layout') ?> <?= $this->extend('layout/main_layout') ?>
<?= $this->section('content') ?> <?= $this->section('content') ?>
<?php $isSchoolYearReadonly = (bool) ($isSchoolYearReadonly ?? false); ?>
<div class="container py-4"> <div class="container py-4">
<div class="d-flex align-items-center justify-content-between mb-3"> <div class="d-flex align-items-center justify-content-between mb-3">
<div> <div>
<h3 class="mb-0">Class 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 class="text-muted">Review weekly submissions and compare Islamic Studies with Quran/Arabic.</div>
</div> </div>
<a href="<?= base_url('teacher/progress/submit') ?>" class="btn btn-outline-secondary">Submit New Report</a> <?php if ($isSchoolYearReadonly): ?>
<span class="btn btn-outline-secondary disabled" aria-disabled="true">Submit New Report</span>
<?php else: ?>
<a href="<?= base_url('teacher/progress/submit') ?>" class="btn btn-outline-secondary">Submit New Report</a>
<?php endif; ?>
</div> </div>
<?php <?php
@@ -15,6 +20,9 @@
$classSectionOptions = $classSectionOptions ?? []; $classSectionOptions = $classSectionOptions ?? [];
$selectedSectionId = $selectedSectionId ?? null; $selectedSectionId = $selectedSectionId ?? null;
?> ?>
<?php if ($isSchoolYearReadonly): ?>
<div class="alert alert-warning">This school year is read-only. Progress submissions can be viewed but not changed.</div>
<?php endif; ?>
<?php if (!empty($classSectionOptions)): ?> <?php if (!empty($classSectionOptions)): ?>
<form method="get" class="mb-3 row g-2 align-items-center"> <form method="get" class="mb-3 row g-2 align-items-center">
<div class="col-auto"> <div class="col-auto">
@@ -90,7 +98,9 @@
<td class="text-end"> <td class="text-end">
<?php if ($exampleReport): ?> <?php if ($exampleReport): ?>
<a href="<?= base_url('teacher/progress/view/' . $exampleReport['id']) ?>" class="btn btn-sm btn-outline-primary">View Weekly Details</a> <a href="<?= base_url('teacher/progress/view/' . $exampleReport['id']) ?>" class="btn btn-sm btn-outline-primary">View Weekly Details</a>
<a href="<?= base_url('teacher/progress/edit/' . $exampleReport['id']) ?>" class="btn btn-sm btn-outline-secondary ms-1">Edit</a> <?php if (!$isSchoolYearReadonly): ?>
<a href="<?= base_url('teacher/progress/edit/' . $exampleReport['id']) ?>" class="btn btn-sm btn-outline-secondary ms-1">Edit</a>
<?php endif; ?>
<?php endif; ?> <?php endif; ?>
</td> </td>
</tr> </tr>
+13 -7
View File
@@ -11,6 +11,7 @@
$weekStartSelected = set_value('week_start', $defaultWeekStart); $weekStartSelected = set_value('week_start', $defaultWeekStart);
$weekEndValue = set_value('week_end', $existingWeekEnd ?? ''); $weekEndValue = set_value('week_end', $existingWeekEnd ?? '');
$existingReports = $existingReports ?? []; $existingReports = $existingReports ?? [];
$isSchoolYearReadonly = (bool) ($isSchoolYearReadonly ?? false);
if (!$weekEndValue && $weekStartSelected) { if (!$weekEndValue && $weekStartSelected) {
try { try {
$dt = new \DateTime($weekStartSelected); $dt = new \DateTime($weekStartSelected);
@@ -58,6 +59,9 @@
<?php if (session()->getFlashdata('error')): ?> <?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div> <div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($isSchoolYearReadonly): ?>
<div class="alert alert-warning">This school year is read-only. Progress submissions are disabled.</div>
<?php endif; ?>
<?php if (session()->getFlashdata('errors')): ?> <?php if (session()->getFlashdata('errors')): ?>
<div class="alert alert-danger"> <div class="alert alert-danger">
<ul class="mb-0"> <ul class="mb-0">
@@ -142,7 +146,8 @@
<button type="button" <button type="button"
class="btn btn-sm btn-outline-dark" class="btn btn-sm btn-outline-dark"
data-add-unit-chapter data-add-unit-chapter
data-subject="<?= esc($slug) ?>"> data-subject="<?= esc($slug) ?>"
<?= $isSchoolYearReadonly ? 'disabled' : '' ?>>
<?= $slug === 'quran' ? '+ Add Surah, Arabic Subject' : '+ Add Unit, Chapter' ?> <?= $slug === 'quran' ? '+ Add Surah, Arabic Subject' : '+ Add Unit, Chapter' ?>
</button> </button>
<div class="subject-curriculum-list position-absolute d-none border rounded shadow-sm bg-white mt-1" id="curriculumList-<?= esc($slug) ?>" style="min-width: 220px; z-index: 5;"> <div class="subject-curriculum-list position-absolute d-none border rounded shadow-sm bg-white mt-1" id="curriculumList-<?= esc($slug) ?>" style="min-width: 220px; z-index: 5;">
@@ -170,7 +175,8 @@
data-subject="<?= esc($slug) ?>" data-subject="<?= esc($slug) ?>"
data-unit-number="<?= esc($unitNumber) ?>" data-unit-number="<?= esc($unitNumber) ?>"
data-unit-title="<?= esc($unitTitle) ?>" data-unit-title="<?= esc($unitTitle) ?>"
data-chapter="<?= esc($chapterName) ?>"> data-chapter="<?= esc($chapterName) ?>"
<?= $isSchoolYearReadonly ? 'disabled' : '' ?>>
<?= esc($display) ?> <?= esc($display) ?>
</button> </button>
<?php endforeach; ?> <?php endforeach; ?>
@@ -179,7 +185,7 @@
<div class="px-2 py-2 border-top"> <div class="px-2 py-2 border-top">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input type="text" class="form-control" placeholder="Custom Surah" data-custom-input data-subject="<?= esc($slug) ?>"> <input type="text" class="form-control" placeholder="Custom Surah" data-custom-input data-subject="<?= esc($slug) ?>">
<button type="button" class="btn btn-outline-secondary" data-custom-entry data-subject="<?= esc($slug) ?>">Add</button> <button type="button" class="btn btn-outline-secondary" data-custom-entry data-subject="<?= esc($slug) ?>" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Add</button>
</div> </div>
<div class="form-text small text-muted">Add a custom Surah or Arabic target.</div> <div class="form-text small text-muted">Add a custom Surah or Arabic target.</div>
</div> </div>
@@ -187,7 +193,7 @@
<div class="px-2 py-2 border-top"> <div class="px-2 py-2 border-top">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input type="text" class="form-control" placeholder="Type subject or topic" data-custom-input data-subject="<?= esc($slug) ?>"> <input type="text" class="form-control" placeholder="Type subject or topic" data-custom-input data-subject="<?= esc($slug) ?>">
<button type="button" class="btn btn-outline-secondary" data-custom-entry data-subject="<?= esc($slug) ?>">Add</button> <button type="button" class="btn btn-outline-secondary" data-custom-entry data-subject="<?= esc($slug) ?>" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Add</button>
</div> </div>
<div class="form-text small text-muted">Add a subject or unit not listed above (e.g. Seerah, Fiqh, Akhlaq).</div> <div class="form-text small text-muted">Add a subject or unit not listed above (e.g. Seerah, Fiqh, Akhlaq).</div>
</div> </div>
@@ -206,7 +212,7 @@
<input type="text" name="chapter_<?= esc($slug) ?>[]" class="form-control form-control-sm" placeholder="Chapter" value="<?= esc($chapterValues[$i] ?? '') ?>" readonly> <input type="text" name="chapter_<?= esc($slug) ?>[]" class="form-control form-control-sm" placeholder="Chapter" value="<?= esc($chapterValues[$i] ?? '') ?>" readonly>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<button type="button" class="btn btn-outline-danger btn-sm" data-remove-unit-chapter aria-label="Remove entry">X</button> <button type="button" class="btn btn-outline-danger btn-sm" data-remove-unit-chapter aria-label="Remove entry" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>X</button>
</div> </div>
</div> </div>
<?php endfor; ?> <?php endfor; ?>
@@ -232,7 +238,7 @@
<div class="col"> <div class="col">
<div class="card shadow-sm"> <div class="card shadow-sm">
<div class="card-body d-flex flex-column"> <div class="card-body d-flex flex-column">
<button class="btn btn-primary w-100 mt-auto" type="submit" <?= $hasClass ? '' : 'disabled' ?>><?= esc($submitLabel) ?></button> <button class="btn btn-primary w-100 mt-auto" type="submit" <?= ($hasClass && !$isSchoolYearReadonly) ? '' : 'disabled' ?>><?= esc($submitLabel) ?></button>
<?php if (! $hasClass): ?> <?php if (! $hasClass): ?>
<div class="text-muted small mt-2"> <div class="text-muted small mt-2">
You are not assigned to a class. Contact the administrator to submit progress. You are not assigned to a class. Contact the administrator to submit progress.
@@ -288,7 +294,7 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button> <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="confirmOverwriteButton">Override</button> <button type="button" class="btn btn-primary" id="confirmOverwriteButton" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Override</button>
</div> </div>
</div> </div>
</div> </div>
+10 -4
View File
@@ -10,6 +10,7 @@ $examTypes = $examTypes ?? [];
$schoolYear = $schoolYear ?? ''; $schoolYear = $schoolYear ?? '';
$semester = $semester ?? ''; $semester = $semester ?? '';
$maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024); $maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024);
$isSchoolYearReadonly = (bool) ($isSchoolYearReadonly ?? false);
?> ?>
<style> <style>
.teacher-drafts-page, .teacher-drafts-page,
@@ -48,6 +49,11 @@ $maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024);
<?php if (session()->getFlashdata('error')): ?> <?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div> <div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($isSchoolYearReadonly): ?>
<div class="alert alert-warning">
This school year is read-only. Exam draft submissions are disabled.
</div>
<?php endif; ?>
<div class="card mb-4"> <div class="card mb-4">
<div class="card-body"> <div class="card-body">
@@ -55,7 +61,7 @@ $maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024);
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label" for="exam_type">Exam type <span class="text-danger">*</span></label> <label class="form-label" for="exam_type">Exam type <span class="text-danger">*</span></label>
<select name="exam_type" id="exam_type" class="form-select" required> <select name="exam_type" id="exam_type" class="form-select" required <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>
<option value="">— Select —</option> <option value="">— Select —</option>
<?php foreach ($examTypes as $t): ?> <?php foreach ($examTypes as $t): ?>
<option value="<?= esc($t) ?>" <?= old('exam_type') === $t ? 'selected' : '' ?>><?= esc($t) ?></option> <option value="<?= esc($t) ?>" <?= old('exam_type') === $t ? 'selected' : '' ?>><?= esc($t) ?></option>
@@ -64,15 +70,15 @@ $maxUploadBytes = $maxUploadBytes ?? (12 * 1024 * 1024);
</div> </div>
<div class="col-8"> <div class="col-8">
<label class="form-label" for="author_comment">Author comment</label> <label class="form-label" for="author_comment">Author comment</label>
<textarea name="author_comment" id="author_comment" class="form-control" rows="2" placeholder="Optional note for reviewers"><?= esc(old('author_comment') ?? '') ?></textarea> <textarea name="author_comment" id="author_comment" class="form-control" rows="2" placeholder="Optional note for reviewers" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>><?= esc(old('author_comment') ?? '') ?></textarea>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<label class="form-label" for="draft_file">File (Word) <span class="text-danger">*</span></label> <label class="form-label" for="draft_file">File (Word) <span class="text-danger">*</span></label>
<input type="file" name="draft_file" id="draft_file" class="form-control" accept=".doc,.docx" required> <input type="file" name="draft_file" id="draft_file" class="form-control" accept=".doc,.docx" required <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>
<div class="form-text">Max <?= esc(number_format($maxUploadBytes / 1048576, 1)) ?> MB.</div> <div class="form-text">Max <?= esc(number_format($maxUploadBytes / 1048576, 1)) ?> MB.</div>
</div> </div>
<div class="col-12 d-flex flex-wrap gap-2"> <div class="col-12 d-flex flex-wrap gap-2">
<button type="submit" class="btn btn-primary">Submit for review</button> <button type="submit" class="btn btn-primary" <?= $isSchoolYearReadonly ? 'disabled' : '' ?>>Submit for review</button>
</div> </div>
<?= form_close() ?> <?= form_close() ?>
<?php if ($schoolYear !== '' || $semester !== ''): ?> <?php if ($schoolYear !== '' || $semester !== ''): ?>
+30 -8
View File
@@ -32,7 +32,9 @@
<?php <?php
$classSectionId = (int)($class_section_id ?? 0); $classSectionId = (int)($class_section_id ?? 0);
$scoresLocked = !empty($scoresLocked); $scoresLocked = !empty($scoresLocked);
$lockAttr = $scoresLocked ? 'disabled' : ''; $isSchoolYearReadonly = !empty($isSchoolYearReadonly);
$editingDisabled = $scoresLocked || $isSchoolYearReadonly;
$lockAttr = $editingDisabled ? 'disabled' : '';
$displayScore = static function ($value) { $displayScore = static function ($value) {
return ($value === null || $value === '') ? '' : esc($value); return ($value === null || $value === '') ? '' : esc($value);
}; };
@@ -94,6 +96,11 @@
Scores are submitted and locked for this semester. Scores are submitted and locked for this semester.
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php if ($isSchoolYearReadonly): ?>
<div class="alert alert-info mt-3 mb-0">
This school year is read-only. You can view scores, but changes are disabled.
</div>
<?php endif; ?>
<div class="mt-3"> <div class="mt-3">
<a href="<?= base_url('/teacher/scores?choose_semester=1') ?>" class="btn btn-outline-secondary btn-sm">Choose another semester</a> <a href="<?= base_url('/teacher/scores?choose_semester=1') ?>" class="btn btn-outline-secondary btn-sm">Choose another semester</a>
</div> </div>
@@ -291,14 +298,29 @@
</div> </div>
<div class="scores-action-bar d-flex flex-wrap gap-2 mt-3"> <div class="scores-action-bar d-flex flex-wrap gap-2 mt-3">
<a href="<?= base_url('/teacher/addHomework?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Homework</a> <?php if ($isSchoolYearReadonly): ?>
<a href="<?= base_url('/teacher/addQuiz?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Quiz</a> <button type="button" class="btn btn-secondary" disabled>Add Homework</button>
<a href="<?= base_url('/teacher/addProject?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Project</a> <button type="button" class="btn btn-secondary" disabled>Add Quiz</button>
<a href="<?= base_url('/teacher/addParticipation?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Participation</a> <button type="button" class="btn btn-secondary" disabled>Add Project</button>
<button type="button" class="btn btn-secondary" disabled>Add Participation</button>
<?php else: ?>
<a href="<?= base_url('/teacher/addHomework?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Homework</a>
<a href="<?= base_url('/teacher/addQuiz?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Quiz</a>
<a href="<?= base_url('/teacher/addProject?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Project</a>
<a href="<?= base_url('/teacher/addParticipation?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Participation</a>
<?php endif; ?>
<?php if ($semester === 'fall'): ?> <?php if ($semester === 'fall'): ?>
<a href="<?= base_url('/teacher/addMidtermExam?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Midterm</a> <?php if ($isSchoolYearReadonly): ?>
<button type="button" class="btn btn-secondary" disabled>Add Midterm</button>
<?php else: ?>
<a href="<?= base_url('/teacher/addMidtermExam?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Midterm</a>
<?php endif; ?>
<?php elseif ($semester === 'spring'): ?> <?php elseif ($semester === 'spring'): ?>
<a href="<?= base_url('/teacher/addFinalExam?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Final</a> <?php if ($isSchoolYearReadonly): ?>
<button type="button" class="btn btn-secondary" disabled>Add Final</button>
<?php else: ?>
<a href="<?= base_url('/teacher/addFinalExam?class_section_id=' . $classSectionId) ?>" class="btn btn-secondary">Add Final</a>
<?php endif; ?>
<?php endif; ?> <?php endif; ?>
<button type="submit" class="btn btn-info" <?= $lockAttr ?>>Save Comments</button> <button type="submit" class="btn btn-info" <?= $lockAttr ?>>Save Comments</button>
@@ -309,7 +331,7 @@
formaction="<?= base_url('/teacher/submit-scores-lock') ?>" formaction="<?= base_url('/teacher/submit-scores-lock') ?>"
formmethod="post" formmethod="post"
data-confirm-message="Once you submit, you cannot add any scores or comments. Do you want to continue?" data-confirm-message="Once you submit, you cannot add any scores or comments. Do you want to continue?"
<?= $scoresLocked ? 'disabled' : '' ?>> <?= $editingDisabled ? 'disabled' : '' ?>>
<?= $scoresLocked ? 'Scores Locked' : 'Submit Semester Scores' ?> <?= $scoresLocked ? 'Scores Locked' : 'Submit Semester Scores' ?>
</button> </button>
</div> </div>