892 lines
34 KiB
PHP
892 lines
34 KiB
PHP
<?php
|
||
|
||
namespace App\Controllers\View;
|
||
|
||
use App\Controllers\BaseController;
|
||
use App\Models\UserRoleModel;
|
||
use App\Models\StudentClassModel;
|
||
use App\Models\InvoiceModel;
|
||
use App\Models\TeacherClassModel;
|
||
use App\Models\ClassSectionModel;
|
||
use App\Models\SemesterScoreModel;
|
||
use App\Models\ScoreCommentModel;
|
||
use App\Models\AttendanceRecordModel;
|
||
use App\Models\AttendanceDayModel;
|
||
use App\Models\CalendarModel;
|
||
use \Config\Database;
|
||
use DateTimeImmutable;
|
||
use DateTimeZone;
|
||
use App\Models\ConfigurationModel;
|
||
|
||
class LandingPageController extends BaseController
|
||
{
|
||
protected $classSectionId;
|
||
protected $db;
|
||
protected $schoolYear;
|
||
protected $semester;
|
||
protected $configModel;
|
||
protected $lastDayOfRegistration;
|
||
protected $refundDeadline;
|
||
protected $studentClassModel;
|
||
protected $invoiceModel;
|
||
protected $userRoleModel;
|
||
protected $teacherClassModel;
|
||
protected $classSectionModel;
|
||
protected $semesterScoreModel;
|
||
protected $scoreCommentModel;
|
||
protected $attendanceRecordModel;
|
||
protected $attendanceDayModel;
|
||
protected $calendarModel;
|
||
|
||
|
||
public function __construct()
|
||
{
|
||
$this->db = \Config\Database::connect();
|
||
$this->configModel = new ConfigurationModel();
|
||
$this->studentClassModel = new StudentClassModel();
|
||
$this->invoiceModel = new InvoiceModel();
|
||
$this->userRoleModel = new UserRoleModel();
|
||
$this->teacherClassModel = new TeacherClassModel();
|
||
$this->semesterScoreModel = new SemesterScoreModel();
|
||
$this->scoreCommentModel = new ScoreCommentModel();
|
||
$this->attendanceRecordModel = new AttendanceRecordModel();
|
||
$this->attendanceDayModel = new AttendanceDayModel();
|
||
$this->classSectionModel = new ClassSectionModel();
|
||
$this->calendarModel = new CalendarModel();
|
||
|
||
// Fetch Enrollment and Refund Deadlines from Configuration
|
||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||
$this->semester = $this->configModel->getConfig('semester');
|
||
$this->lastDayOfRegistration = $this->configModel->getConfig('enrollment_deadline') ?? 'Not set';
|
||
$this->refundDeadline = $this->configModel->getConfig('refund_deadline') ?? 'Not set';
|
||
|
||
// Get class_id and store it in session
|
||
$this->classSectionId = $this->classSectionId();
|
||
session()->set('class_section_id', $this->classSectionId);
|
||
}
|
||
|
||
public function index()
|
||
{
|
||
$userRole = $this->getUserRole(); // Assume this function gets the user role
|
||
|
||
switch (strtolower($userRole)) {
|
||
case 'administrator':
|
||
return $this->administrator();
|
||
case 'admin':
|
||
return $this->admin();
|
||
case 'teacher':
|
||
return $this->teacher();
|
||
case 'student':
|
||
return $this->student();
|
||
case 'parent':
|
||
case 'authorized_user':
|
||
return $this->parentDashboard();
|
||
default:
|
||
return $this->guest();
|
||
}
|
||
}
|
||
|
||
public function classSectionId()
|
||
{
|
||
// Get user_id from the session
|
||
$user_id = session()->get('user_id');
|
||
|
||
if (!$user_id) {
|
||
return null;
|
||
}
|
||
|
||
// Teacher class sections only apply to the teacher role; other roles have no teacher_class rows.
|
||
if (strtolower(trim((string) $this->getUserRole())) !== 'teacher') {
|
||
return null;
|
||
}
|
||
|
||
// Get all class assignments for this teacher in the current term
|
||
$assignments = $this->teacherClassModel->getClassAssignmentsByUserId(
|
||
(int)$user_id,
|
||
(string)$this->schoolYear,
|
||
(string)$this->semester
|
||
);
|
||
|
||
$ids = array_map(static fn($a) => (int)($a['class_section_id'] ?? 0), $assignments);
|
||
$ids = array_values(array_filter(array_unique($ids)));
|
||
|
||
if (empty($ids)) {
|
||
log_message('warning', "No class section found for user ID: $user_id");
|
||
return null;
|
||
}
|
||
|
||
$current = (int)(session()->get('class_section_id') ?? 0);
|
||
$chosen = in_array($current, $ids, true) ? $current : $ids[0];
|
||
|
||
session()->set([
|
||
'class_section_id' => $chosen,
|
||
'class_section_ids' => $ids,
|
||
]);
|
||
|
||
return $chosen;
|
||
}
|
||
|
||
|
||
public function administrator()
|
||
{
|
||
return view('administrator/administratordashboard');
|
||
}
|
||
|
||
public function admin()
|
||
{
|
||
return view('/landing_page/admin_dashboard');
|
||
}
|
||
|
||
public function teacher()
|
||
{
|
||
$userId = (int)(session()->get('user_id') ?? 0);
|
||
$assignments = $this->teacherClassModel->getClassAssignmentsByUserId(
|
||
$userId,
|
||
(string)$this->schoolYear,
|
||
(string)$this->semester
|
||
);
|
||
|
||
$availableIds = array_map(static fn($a) => (int)($a['class_section_id'] ?? 0), $assignments);
|
||
$availableIds = array_values(array_filter(array_unique($availableIds)));
|
||
|
||
$requestedId = (int)($this->request->getGet('class_section_id') ?? 0);
|
||
$activeId = null;
|
||
|
||
if ($requestedId && in_array($requestedId, $availableIds, true)) {
|
||
$activeId = $requestedId;
|
||
} elseif (!empty($availableIds)) {
|
||
$activeId = in_array((int)$this->classSectionId, $availableIds, true)
|
||
? (int)$this->classSectionId
|
||
: $availableIds[0];
|
||
}
|
||
|
||
if ($activeId) {
|
||
session()->set('class_section_id', $activeId);
|
||
}
|
||
|
||
$activeName = null;
|
||
foreach ($assignments as $a) {
|
||
if ((int)$a['class_section_id'] === (int)$activeId) {
|
||
$activeName = $a['class_section_name'];
|
||
break;
|
||
}
|
||
}
|
||
|
||
$normalizedSemester = ucfirst(strtolower(trim((string)($this->semester ?? ''))));
|
||
$classSectionIds = array_values(array_filter(array_unique(array_map(static fn($a) => (int)($a['class_section_id'] ?? 0), $assignments)), static fn($id) => $id > 0));
|
||
$dashboardSummary = $this->buildTeacherDashboardSummary($classSectionIds, $normalizedSemester);
|
||
|
||
return view('/landing_page/teacher_dashboard', [
|
||
'class_section_id' => $activeId,
|
||
'active_class_name' => $activeName,
|
||
'classes' => $assignments,
|
||
'jobs' => $assignments,
|
||
'jobCount' => count($assignments),
|
||
'scoreSummary' => $dashboardSummary['scoreSummary'],
|
||
'commentSummary' => $dashboardSummary['commentSummary'],
|
||
'attendanceSummary' => $dashboardSummary['attendanceSummary'],
|
||
'deadlineEvents' => $dashboardSummary['deadlineEvents'],
|
||
'participationSummary' => $dashboardSummary['participationSummary'],
|
||
'attendanceStatus' => $dashboardSummary['attendanceStatus'],
|
||
'dashboardNotifications' => $dashboardSummary['notifications'],
|
||
'school_year' => (string)$this->schoolYear,
|
||
'semester' => (string)$this->semester,
|
||
]);
|
||
}
|
||
|
||
private function buildTeacherDashboardSummary(array $classSectionIds, string $semester): array
|
||
{
|
||
$normalizedSemester = ucfirst(strtolower(trim((string)$semester)));
|
||
if ($normalizedSemester === '' || !in_array($normalizedSemester, ['Fall', 'Spring'], true)) {
|
||
$normalizedSemester = 'Fall';
|
||
}
|
||
|
||
$scoreField = ($normalizedSemester === 'Spring') ? 'final_exam_score' : 'midterm_exam_score';
|
||
$scoreLabel = ($normalizedSemester === 'Spring') ? 'Final Exam' : 'Midterm';
|
||
|
||
$summary = [
|
||
'scoreSummary' => [
|
||
'completionPct' => 0,
|
||
'missing' => 0,
|
||
'expected' => 0,
|
||
'filled' => 0,
|
||
'fieldLabel' => $scoreLabel,
|
||
],
|
||
'commentSummary' => [
|
||
'total' => 0,
|
||
'pendingReview' => 0,
|
||
'reviewed' => 0,
|
||
'byType' => [],
|
||
],
|
||
'attendanceSummary' => [
|
||
'recorded' => 0,
|
||
'students' => 0,
|
||
'completionPct' => 0,
|
||
'avgAbsences' => 0,
|
||
],
|
||
'participationSummary' => [
|
||
'filled' => 0,
|
||
'missing' => 0,
|
||
'completionPct' => 0,
|
||
'expected' => 0,
|
||
],
|
||
'deadlineEvents' => [],
|
||
];
|
||
|
||
if (!empty($classSectionIds)) {
|
||
$studentRows = $this->db->table('student_class sc')
|
||
->select('sc.student_id, sc.class_section_id')
|
||
->join('students s', 's.id = sc.student_id', 'inner')
|
||
->whereIn('sc.class_section_id', $classSectionIds)
|
||
->where('s.is_active', 1)
|
||
->where('sc.school_year', (string)$this->schoolYear)
|
||
->get()
|
||
->getResultArray();
|
||
|
||
$studentIds = [];
|
||
foreach ($studentRows as $row) {
|
||
$studentId = (int)($row['student_id'] ?? 0);
|
||
$sectionId = (int)($row['class_section_id'] ?? 0);
|
||
if ($studentId <= 0 || $sectionId <= 0) {
|
||
continue;
|
||
}
|
||
$studentIds[] = $studentId;
|
||
}
|
||
$uniqueStudentIds = array_values(array_unique($studentIds));
|
||
} else {
|
||
$uniqueStudentIds = [];
|
||
}
|
||
|
||
$totalStudents = count($uniqueStudentIds);
|
||
$summary['attendanceSummary']['students'] = $totalStudents;
|
||
|
||
$scoreRows = [];
|
||
if (!empty($classSectionIds)) {
|
||
$scoreRows = $this->semesterScoreModel
|
||
->select('student_id, class_section_id, midterm_exam_score, final_exam_score, participation_score')
|
||
->whereIn('class_section_id', $classSectionIds)
|
||
->where('semester', $normalizedSemester)
|
||
->where('school_year', (string)$this->schoolYear)
|
||
->findAll();
|
||
}
|
||
|
||
$scoreTypesConfig = [
|
||
'participation' => ['label' => 'Participation', 'field' => 'participation_score'],
|
||
];
|
||
if ($normalizedSemester === 'Fall') {
|
||
$scoreTypesConfig = array_merge([
|
||
'midterm' => ['label' => 'Midterm', 'field' => 'midterm_exam_score'],
|
||
], $scoreTypesConfig);
|
||
}
|
||
if ($normalizedSemester === 'Spring') {
|
||
$scoreTypesConfig = array_merge([
|
||
'final' => ['label' => 'Final exam', 'field' => 'final_exam_score'],
|
||
], $scoreTypesConfig);
|
||
}
|
||
|
||
$filledCounts = array_fill_keys(array_keys($scoreTypesConfig), 0);
|
||
foreach ($scoreRows as $row) {
|
||
foreach ($scoreTypesConfig as $key => $cfg) {
|
||
$value = trim((string)($row[$cfg['field']] ?? ''));
|
||
if ($value !== '') {
|
||
$filledCounts[$key]++;
|
||
}
|
||
}
|
||
}
|
||
|
||
$expectedScoreCount = $totalStudents > 0 ? $totalStudents : count($scoreRows);
|
||
$scoreDetails = [];
|
||
foreach ($scoreTypesConfig as $key => $cfg) {
|
||
$filled = $filledCounts[$key] ?? 0;
|
||
$missing = max(0, $expectedScoreCount - $filled);
|
||
$completionPct = $expectedScoreCount > 0 ? (int)round(min(100, ($filled / $expectedScoreCount) * 100)) : 0;
|
||
$scoreDetails[$key] = [
|
||
'label' => $cfg['label'],
|
||
'field' => $cfg['field'],
|
||
'filled' => $filled,
|
||
'missing' => $missing,
|
||
'expected' => $expectedScoreCount,
|
||
'completionPct' => $completionPct,
|
||
];
|
||
}
|
||
|
||
$scoreTypeByField = [];
|
||
if ($normalizedSemester === 'Fall') {
|
||
$scoreTypeByField['midterm_exam_score'] = 'midterm';
|
||
}
|
||
if ($normalizedSemester === 'Spring') {
|
||
$scoreTypeByField['final_exam_score'] = 'final';
|
||
}
|
||
$activeScoreType = $scoreTypeByField[$scoreField] ?? 'midterm';
|
||
$activeScoreDetails = $scoreDetails[$activeScoreType] ?? $scoreDetails['midterm'];
|
||
|
||
$summary['scoreSummary'] = [
|
||
'completionPct' => $activeScoreDetails['completionPct'],
|
||
'missing' => $activeScoreDetails['missing'],
|
||
'expected' => $activeScoreDetails['expected'],
|
||
'filled' => $activeScoreDetails['filled'],
|
||
'fieldLabel' => $scoreLabel,
|
||
'types' => $scoreDetails,
|
||
];
|
||
|
||
$participationMetrics = $scoreDetails['participation'];
|
||
$summary['participationSummary'] = [
|
||
'filled' => $participationMetrics['filled'],
|
||
'missing' => $participationMetrics['missing'],
|
||
'completionPct' => $participationMetrics['completionPct'],
|
||
'expected' => $participationMetrics['expected'],
|
||
];
|
||
|
||
if (!empty($uniqueStudentIds)) {
|
||
$commentRows = $this->scoreCommentModel
|
||
->select('student_id, score_type, comment, comment_review')
|
||
->whereIn('student_id', $uniqueStudentIds)
|
||
->where('semester', $normalizedSemester)
|
||
->where('school_year', (string)$this->schoolYear)
|
||
->findAll();
|
||
} else {
|
||
$commentRows = [];
|
||
}
|
||
|
||
$expectedCommentTypes = array_values(array_filter([
|
||
'ptap',
|
||
$normalizedSemester === 'Fall' ? 'midterm' : null,
|
||
$normalizedSemester === 'Spring' ? 'final' : null,
|
||
]));
|
||
$commentStats = [];
|
||
$commentsByStudent = [];
|
||
|
||
foreach ($commentRows as $row) {
|
||
$sid = (int)($row['student_id'] ?? 0);
|
||
$type = strtolower(trim((string)($row['score_type'] ?? '')));
|
||
if ($type === '') {
|
||
$type = 'general';
|
||
}
|
||
if (!isset($commentStats[$type])) {
|
||
$commentStats[$type] = ['pending' => 0, 'reviewed' => 0];
|
||
}
|
||
|
||
$commentText = trim((string)($row['comment'] ?? ''));
|
||
$reviewValue = trim((string)($row['comment_review'] ?? ''));
|
||
|
||
if ($sid > 0 && $commentText !== '') {
|
||
$commentsByStudent[$sid][$type] = $commentText;
|
||
}
|
||
|
||
if ($commentText === '') {
|
||
continue;
|
||
}
|
||
|
||
if ($reviewValue === '') {
|
||
$commentStats[$type]['pending']++;
|
||
} else {
|
||
$commentStats[$type]['reviewed']++;
|
||
}
|
||
}
|
||
|
||
foreach ($expectedCommentTypes as $type) {
|
||
if (!isset($commentStats[$type])) {
|
||
$commentStats[$type] = ['pending' => 0, 'reviewed' => 0];
|
||
}
|
||
}
|
||
|
||
$missingComments = 0;
|
||
$missingDetail = [];
|
||
foreach ($uniqueStudentIds as $sid) {
|
||
foreach ($expectedCommentTypes as $type) {
|
||
$text = $commentsByStudent[$sid][$type] ?? '';
|
||
if ($text === '') {
|
||
$missingDetail[$type] = ($missingDetail[$type] ?? 0) + 1;
|
||
$missingComments++;
|
||
}
|
||
}
|
||
}
|
||
|
||
$commentTypes = [];
|
||
foreach ($expectedCommentTypes as $type) {
|
||
$stats = $commentStats[$type];
|
||
$filled = $stats['pending'] + $stats['reviewed'];
|
||
$completionPct = $totalStudents > 0 ? (int)round(min(100, ($filled / $totalStudents) * 100)) : 0;
|
||
|
||
$commentTypes[$type] = [
|
||
'label' => ucfirst($type) . ' comments',
|
||
'pending' => $stats['pending'],
|
||
'reviewed' => $stats['reviewed'],
|
||
'filled' => $filled,
|
||
'missing' => $missingDetail[$type] ?? 0,
|
||
'expected' => $totalStudents,
|
||
'completionPct' => $completionPct,
|
||
];
|
||
}
|
||
|
||
$totalPending = array_sum(array_column($commentTypes, 'pending'));
|
||
$totalReviewed = array_sum(array_column($commentTypes, 'reviewed'));
|
||
$totalFilled = array_sum(array_column($commentTypes, 'filled'));
|
||
$totalExpected = array_sum(array_column($commentTypes, 'expected'));
|
||
$commentCompletionPct = $totalExpected > 0 ? (int)round(min(100, ($totalFilled / $totalExpected) * 100)) : 0;
|
||
|
||
$summary['commentSummary'] = [
|
||
'total' => $totalFilled,
|
||
'pendingReview' => $totalPending,
|
||
'reviewed' => $totalReviewed,
|
||
'missing' => $missingComments,
|
||
'missingDetail' => $missingDetail,
|
||
'completionPct' => $commentCompletionPct,
|
||
'types' => $commentTypes,
|
||
];
|
||
|
||
$attendanceRows = [];
|
||
if (!empty($classSectionIds)) {
|
||
$attendanceRows = $this->attendanceRecordModel
|
||
->select('student_id, class_section_id, total_absence')
|
||
->whereIn('class_section_id', $classSectionIds)
|
||
->where('semester', $normalizedSemester)
|
||
->where('school_year', (string)$this->schoolYear)
|
||
->findAll();
|
||
}
|
||
|
||
$attendanceSet = [];
|
||
$totalAbsences = 0;
|
||
foreach ($attendanceRows as $record) {
|
||
$studentId = (int)($record['student_id'] ?? 0);
|
||
$sectionId = (int)($record['class_section_id'] ?? 0);
|
||
if ($studentId <= 0 || $sectionId <= 0) {
|
||
continue;
|
||
}
|
||
$key = "{$sectionId}:{$studentId}";
|
||
$attendanceSet[$key] = true;
|
||
$totalAbsences += (int)($record['total_absence'] ?? 0);
|
||
}
|
||
|
||
$attendanceRecorded = count($attendanceSet);
|
||
$attendancePct = $totalStudents > 0 ? (int)round(min(100, ($attendanceRecorded / $totalStudents) * 100)) : 0;
|
||
$avgAbsence = $attendanceRecorded > 0 ? round($totalAbsences / $attendanceRecorded, 1) : 0;
|
||
|
||
$summary['attendanceSummary'] = [
|
||
'recorded' => $attendanceRecorded,
|
||
'students' => $totalStudents,
|
||
'completionPct' => $attendancePct,
|
||
'avgAbsences' => $avgAbsence,
|
||
];
|
||
$timezone = new DateTimeZone(date_default_timezone_get() ?: 'UTC');
|
||
$today = (new DateTimeImmutable('now', $timezone))->format('Y-m-d');
|
||
|
||
$attendanceStatus = $this->buildAttendanceSubmissionStatus($classSectionIds, $normalizedSemester, $today);
|
||
|
||
$summary['deadlineEvents'] = $this->calendarModel
|
||
->where('notify_teacher', 1)
|
||
->where('school_year', (string)$this->schoolYear)
|
||
->where('semester', $normalizedSemester)
|
||
->where('date >=', $today)
|
||
->orderBy('date', 'ASC')
|
||
->limit(5)
|
||
->findAll();
|
||
|
||
$summary['attendanceStatus'] = $attendanceStatus;
|
||
if ($attendanceStatus['submitted'] === false && !empty($classSectionIds)) {
|
||
$summary['attendanceSummary']['completionPct'] = 0;
|
||
$summary['attendanceSummary']['recorded'] = 0;
|
||
}
|
||
|
||
$summary['notifications'] = $this->buildDashboardNotifications($summary);
|
||
|
||
return $summary;
|
||
}
|
||
|
||
private function buildDashboardNotifications(array $summary): array
|
||
{
|
||
$notifications = [];
|
||
$baseScoreLink = base_url('/teacher/scores');
|
||
$buildScoreLink = static function (?string $focus) use ($baseScoreLink) {
|
||
if (empty($focus)) {
|
||
return $baseScoreLink;
|
||
}
|
||
return $baseScoreLink . '?focus=' . urlencode($focus);
|
||
};
|
||
$determineFocus = static function (array $event): string {
|
||
$eventTypeText = strtolower(trim((string)($event['event_type'] ?? '')));
|
||
if ($eventTypeText !== '' && str_contains($eventTypeText, 'draft')) {
|
||
return 'comments';
|
||
}
|
||
$text = strtolower(trim(($event['title'] ?? '') . ' ' . ($event['description'] ?? '')));
|
||
if ($text === '') {
|
||
return 'scores';
|
||
}
|
||
if (str_contains($text, 'comment') || str_contains($text, 'ptap')) {
|
||
return 'comments';
|
||
}
|
||
return 'scores';
|
||
};
|
||
$calendarLink = base_url('/teacher/calendar');
|
||
$attendanceLink = base_url('/teacher/showupdate_attendance');
|
||
$timezone = new DateTimeZone(date_default_timezone_get() ?: 'UTC');
|
||
$todayDate = (new DateTimeImmutable('now', $timezone))->setTime(0, 0);
|
||
$today = $todayDate->format('Y-m-d');
|
||
|
||
$calendarNotificationConfig = [
|
||
'1st Semester Scores' => 14,
|
||
'2nd Semester Scores' => 14,
|
||
'Final' => 14,
|
||
'Final Exam Draft' => 42,
|
||
'Midterm' => 14,
|
||
'Midterm Exam Draft' => 42,
|
||
];
|
||
|
||
$activeDeadline = false;
|
||
foreach ($summary['deadlineEvents'] ?? [] as $event) {
|
||
$eventTypeRaw = trim((string)($event['event_type'] ?? ''));
|
||
if ($eventTypeRaw === '') {
|
||
continue;
|
||
}
|
||
$matchedType = null;
|
||
$thresholdDays = null;
|
||
foreach ($calendarNotificationConfig as $type => $days) {
|
||
if (strcasecmp($eventTypeRaw, $type) === 0) {
|
||
$matchedType = $type;
|
||
$thresholdDays = $days;
|
||
break;
|
||
}
|
||
}
|
||
if ($matchedType === null || $thresholdDays === null) {
|
||
continue;
|
||
}
|
||
$rawDate = $event['date'] ?? '';
|
||
if ($rawDate === '') {
|
||
continue;
|
||
}
|
||
try {
|
||
$eventDateTime = new DateTimeImmutable($rawDate, $timezone);
|
||
} catch (\Exception $e) {
|
||
continue;
|
||
}
|
||
$eventDateTime = $eventDateTime->setTime(0, 0);
|
||
$daysUntil = (int)$todayDate->diff($eventDateTime)->format('%r%a');
|
||
|
||
if ($daysUntil <= 0) {
|
||
$activeDeadline = true;
|
||
$eventFocus = $determineFocus($event);
|
||
$notifications[] = [
|
||
'type' => 'danger',
|
||
'message' => sprintf(
|
||
"Deadline today: %s is due. Submit the remaining entries before the day ends.",
|
||
$matchedType
|
||
),
|
||
'link' => $buildScoreLink($eventFocus),
|
||
'linkText' => 'Submit now',
|
||
];
|
||
continue;
|
||
}
|
||
|
||
if ($daysUntil > $thresholdDays) {
|
||
continue;
|
||
}
|
||
|
||
$relativeText = $daysUntil === 1 ? ' (tomorrow)' : " (in {$daysUntil} days)";
|
||
$notifications[] = [
|
||
'type' => 'warning',
|
||
'message' => sprintf(
|
||
"%s is due on %s%s. Review it on the calendar so you don’t miss it.",
|
||
$matchedType,
|
||
$eventDateTime->format('M j, Y'),
|
||
$relativeText
|
||
),
|
||
'link' => $calendarLink,
|
||
'linkText' => 'View deadline',
|
||
];
|
||
}
|
||
|
||
if (!$activeDeadline) {
|
||
$scoreLabel = $summary['scoreSummary']['fieldLabel'] ?? 'Score';
|
||
if (!empty($summary['scoreSummary']['missing'] ?? 0)) {
|
||
$notifications[] = [
|
||
'type' => 'warning',
|
||
'message' => sprintf(
|
||
"You have %s %s entries missing on the scores page. Please finish submitting them.",
|
||
(int)$summary['scoreSummary']['missing'],
|
||
$scoreLabel
|
||
),
|
||
'link' => $buildScoreLink('scores'),
|
||
'linkText' => 'Score sheet',
|
||
];
|
||
}
|
||
|
||
foreach (['midterm', 'final', 'ptap'] as $type) {
|
||
$count = (int)($summary['commentSummary']['missingDetail'][$type] ?? 0);
|
||
if ($count <= 0) {
|
||
continue;
|
||
}
|
||
$notifications[] = [
|
||
'type' => 'warning',
|
||
'message' => sprintf(
|
||
"%s comment missing for %s student(s). Leave comments on the scores page.",
|
||
ucfirst($type),
|
||
$count
|
||
),
|
||
'link' => $buildScoreLink('comments'),
|
||
'linkText' => ucfirst($type) . ' comments',
|
||
];
|
||
}
|
||
|
||
if (!empty($summary['participationSummary']['missing'] ?? 0)) {
|
||
$notifications[] = [
|
||
'type' => 'warning',
|
||
'message' => sprintf(
|
||
"Participation entries are still missing for %s student(s). Please add them from the scores page.",
|
||
(int)$summary['participationSummary']['missing']
|
||
),
|
||
'link' => $buildScoreLink('scores'),
|
||
'linkText' => 'Participation',
|
||
];
|
||
}
|
||
}
|
||
|
||
if (!empty($summary['attendanceStatus']['missingSections'] ?? [])) {
|
||
$missingNames = $summary['attendanceStatus']['missingNames'] ?? [];
|
||
$label = '';
|
||
if (!empty($missingNames)) {
|
||
$label = ' (' . implode(', ', array_slice($missingNames, 0, 3)) . (count($missingNames) > 3 ? '…' : '') . ')';
|
||
}
|
||
$notifications[] = [
|
||
'type' => 'danger',
|
||
'message' => "Today's attendance is still unsubmitted for {$summary['attendanceStatus']['date']}{$label}. Please submit it now.",
|
||
'link' => $attendanceLink,
|
||
'linkText' => 'Record attendance',
|
||
];
|
||
}
|
||
|
||
return $notifications;
|
||
}
|
||
|
||
private function buildAttendanceSubmissionStatus(array $classSectionIds, string $semester, string $date): array
|
||
{
|
||
if (empty($classSectionIds)) {
|
||
return [
|
||
'date' => $date,
|
||
'missingSections' => [],
|
||
'missingNames' => [],
|
||
'submitted' => true,
|
||
];
|
||
}
|
||
|
||
$rows = $this->attendanceDayModel
|
||
->select('class_section_id, status')
|
||
->whereIn('class_section_id', $classSectionIds)
|
||
->where('date', $date)
|
||
->where('semester', $semester)
|
||
->where('school_year', (string)$this->schoolYear)
|
||
->findAll();
|
||
|
||
$statusMap = [];
|
||
foreach ($rows as $row) {
|
||
$csId = (int)($row['class_section_id'] ?? 0);
|
||
if ($csId <= 0) {
|
||
continue;
|
||
}
|
||
$statusMap[$csId] = strtolower(trim((string)($row['status'] ?? '')));
|
||
}
|
||
|
||
$missingSections = [];
|
||
foreach ($classSectionIds as $classSectionId) {
|
||
$status = $statusMap[$classSectionId] ?? '';
|
||
if (!in_array($status, ['submitted', 'published', 'finalized'], true)) {
|
||
$missingSections[] = $classSectionId;
|
||
}
|
||
}
|
||
|
||
$missingNames = [];
|
||
if (!empty($missingSections)) {
|
||
$sectionRows = $this->classSectionModel
|
||
->select('class_section_id, class_section_name')
|
||
->whereIn('class_section_id', $missingSections)
|
||
->findAll();
|
||
foreach ($sectionRows as $row) {
|
||
$name = trim((string)($row['class_section_name'] ?? ''));
|
||
if ($name === '') {
|
||
$name = (string)($row['class_section_id'] ?? '');
|
||
}
|
||
$missingNames[] = $name;
|
||
}
|
||
}
|
||
|
||
return [
|
||
'date' => $date,
|
||
'missingSections' => $missingSections,
|
||
'missingNames' => $missingNames,
|
||
'submitted' => empty($missingSections),
|
||
];
|
||
}
|
||
|
||
|
||
public function student()
|
||
{
|
||
return view('/landing_page/student_dashboard');
|
||
}
|
||
|
||
public function parentDashboard()
|
||
{
|
||
$parentId = session()->get('user_id');
|
||
$userType = $_SESSION['user_type'];
|
||
|
||
// Map user type to roles
|
||
if ($userType === 'primary') {
|
||
// If user type is 'Primary', they are the firstparent
|
||
$parentId = $parentId;
|
||
} elseif ($userType === 'secondary') {
|
||
// If user type is 'Secondary', find the parent_id from the parents table
|
||
$parentData = $this->db->table('parents')
|
||
->select('parent_id')
|
||
->where('secondparent_user_id', $parentId)
|
||
->get()
|
||
->getRowArray();
|
||
|
||
if ($parentData) {
|
||
$parentId = $parentData['parent_id'];
|
||
}
|
||
} elseif ($userType === 'tertiary') {
|
||
// If user type is 'Tertiary', find the parent_id from the authorized_users table
|
||
$authUserData = $this->db->table('authorized_users')
|
||
->select('user_id as parent_id')
|
||
->where('authorized_user_id', $parentId)
|
||
->get()
|
||
->getRowArray();
|
||
|
||
if ($authUserData) {
|
||
$parentId = $authUserData['parent_id'];
|
||
}
|
||
}
|
||
|
||
// If no firstparent ID is found, show an error or redirect
|
||
if (!$parentId) {
|
||
return redirect()->back()->with(
|
||
'error',
|
||
'Unable to retrieve student data. Please contact support.'
|
||
);
|
||
}
|
||
|
||
|
||
// Fetch Notifications (only active, non-expired, non-deleted)
|
||
$notifications = $this->db->table('notifications')
|
||
->select([
|
||
'notifications.id',
|
||
'notifications.title',
|
||
'notifications.message',
|
||
'notifications.target_group',
|
||
'notifications.created_at',
|
||
'notifications.expires_at',
|
||
'user_notifications.user_id',
|
||
"CASE
|
||
WHEN user_notifications.user_id IS NOT NULL THEN 'personal'
|
||
ELSE 'broadcast'
|
||
END as notification_type"
|
||
])
|
||
->join(
|
||
'user_notifications',
|
||
'user_notifications.notification_id = notifications.id AND user_notifications.user_id = ' . (int) $parentId,
|
||
'left'
|
||
)
|
||
->groupStart()
|
||
->where('notifications.target_group', 'parent')
|
||
->orWhere('user_notifications.user_id', $parentId)
|
||
->groupEnd()
|
||
->where('notifications.deleted_at IS NULL') // Exclude soft-deleted notifications
|
||
->groupStart()
|
||
->where('notifications.expires_at IS NULL')
|
||
->orWhere('notifications.expires_at > NOW()') // Exclude expired
|
||
->groupEnd()
|
||
->orderBy('notifications.created_at', 'DESC')
|
||
->get()
|
||
->getResultArray();
|
||
|
||
// Fetch Student Information (no filtering needed by school year or semester)
|
||
|
||
$students = $this->db->table('students')
|
||
->where('parent_id', $parentId)
|
||
->get()
|
||
->getResultArray();
|
||
|
||
foreach ($students as &$student) {
|
||
// Get class_section_name and treat it as grade
|
||
$classSection = $this->studentClassModel->getClassSectionsByStudentId($student['id'], $this->schoolYear);
|
||
$student['class_section'] = $classSection;
|
||
$student['grade'] = $classSection; // grade same as section
|
||
}
|
||
unset($student);
|
||
|
||
|
||
// Fetch Attendance Records (filtered by most recent school year and semester)
|
||
$attendanceData = $this->db->table('attendance_data')
|
||
->select('attendance_data.*, students.firstname, students.lastname')
|
||
->join('students', 'students.id = attendance_data.student_id')
|
||
->where('students.parent_id', $parentId)
|
||
// ->orWhere('students.secondparent_user_id', $parentId)
|
||
->where('attendance_data.school_year', $this->schoolYear)
|
||
->where('attendance_data.semester', $this->semester)
|
||
->orderBy('attendance_data.date', 'DESC')
|
||
->get()
|
||
->getResultArray();
|
||
|
||
// Fetch Grades (filtered by most recent school year and semester)
|
||
$grades = $this->db->table('final_score') // Correct table name
|
||
->select('final_score.*, students.firstname, students.lastname') // Ensure table name is correct
|
||
->join('students', 'students.id = final_score.student_id')
|
||
->where('students.parent_id', $parentId)
|
||
// ->orWhere('students.secondparent_user_id', $parentId)
|
||
->where('final_score.school_year', $this->schoolYear) // Ensure correct table column names
|
||
->where('final_score.semester', $this->semester)
|
||
->orderBy('final_score.created_at', 'DESC')
|
||
->get()
|
||
->getResultArray();
|
||
|
||
// Fetch Enrollments (filtered by most recent school year and semester)
|
||
$enrollments = $this->db->table('enrollments')
|
||
->select('enrollments.*, classes.class_name')
|
||
->join('students', 'students.id = enrollments.student_id')
|
||
->join('classes', 'classes.id = enrollments.class_section_id') // Ensure this join is correct
|
||
->where('students.parent_id', $parentId)
|
||
// ->orWhere('students.secondparent_user_id', $parentId)
|
||
->where('enrollments.school_year', $this->schoolYear)
|
||
->where('enrollments.semester', $this->semester)
|
||
->orderBy('enrollments.enrollment_date', 'DESC')
|
||
->get()
|
||
->getResultArray();
|
||
|
||
// Fetch latest invoice balance
|
||
$paymentBalance = $this->invoiceModel->getLatestInvoiceTotalAmount($parentId);
|
||
|
||
// Pass data to the view, including the deadlines
|
||
return view('/landing_page/parent_dashboard', [
|
||
'notifications' => $notifications,
|
||
'students' => $students,
|
||
'attendance' => $attendanceData,
|
||
'grades' => $grades,
|
||
'enrollments' => $enrollments,
|
||
'lastDayOfRegistration' => $this->lastDayOfRegistration, // Add the enrollment deadline to the view
|
||
'withdrawalDeadline' => $this->refundDeadline, // Add the refund deadline to the view
|
||
'paymentBalance' => $paymentBalance, // 🔹 New
|
||
]);
|
||
}
|
||
|
||
public function guest()
|
||
{
|
||
return view('/landing_page/guest_dashboard');
|
||
}
|
||
|
||
protected function getUserRole()
|
||
{
|
||
$active = session()->get('active_role');
|
||
if ($active !== null && $active !== '') {
|
||
return (string) $active;
|
||
}
|
||
|
||
return (string) (session()->get('role') ?? 'guest');
|
||
}
|
||
|
||
protected function getUserRoleFromDatabase($user_id)
|
||
{
|
||
// Fetching user role from the database
|
||
$role = $this->userRoleModel->getRoleByUserId($user_id);
|
||
|
||
return $role ?? 'guest';
|
||
}
|
||
}
|