extend('layout/management_layout') ?> section('content') ?>

Attendance Management

include('partials/academic_filter') ?> section('styles') ?> endSection() ?>
Search Student
Searches all grades.
$sections) { $kept = []; foreach ($sections as $section) { $secKey = (string)($section['class_section_id'] ?? ($section['id'] ?? '')); if ($secKey !== '' && !empty($studentsBySection[$secKey])) { $kept[] = $section; } } if (!empty($kept)) { $filteredGrades[$cid] = $kept; } } $grades = $filteredGrades; ?> $sections) { foreach ($sections as $section) { $secId = $section['class_section_id'] ?? null; if (!$secId || empty($studentsBySection[$secId])) continue; foreach ($studentsBySection[$secId] as $stu) { $sid = (int)($stu['id'] ?? 0); $scid = trim((string)($stu['school_id'] ?? '')); $fname = trim((string)($stu['firstname'] ?? '')); $lname = trim((string)($stu['lastname'] ?? '')); if (!$sid || $scid === '' || $fname === '' || $lname === '') continue; // De-dup by either student DB id or school_id if (isset($seen[$sid]) || isset($seen[$scid])) continue; $seen[$sid] = true; $seen[$scid] = true; $className = (string)($section['class_section_name'] ?? ''); $suffix = $className !== '' ? (' (' . $className . ')') : ''; $label = $scid . ' - ' . $fname . ' ' . $lname . $suffix; // ASCII hyphen, then class section in parentheses echo ''; } } } ?> Grade 1..12 -> Youth ---- $labelsByClassId = []; $gradeNumberById = []; $kgIds = []; $arabicIds = []; $youthIds = []; foreach ($grades as $classId => $sections) { $cid = (int)$classId; $sampleName = ''; $isArabicProgram = false; foreach ($sections as $s) { $sampleName = (string)($s['class_section_name'] ?? ''); $lowName = strtolower($sampleName); $lowCode = strtolower((string)($s['class_section_id'] ?? '')); if (strpos($lowName, 'arabic') !== false || strpos($lowCode, 'arabic') !== false) { $isArabicProgram = true; } if ($sampleName !== '') break; } if ($isArabicProgram || $cid === 14) { // Arabic program: tab label is "Arabic" (sections show their own names e.g., Arabic-1) $labelsByClassId[$cid] = 'Arabic'; $arabicIds[] = $cid; } elseif (preg_match('/\bKG\b/i', $sampleName)) { $labelsByClassId[$cid] = 'KG'; $kgIds[] = $cid; } elseif (preg_match('/\bYouth\b/i', $sampleName)) { $labelsByClassId[$cid] = 'Youth'; $youthIds[] = $cid; } elseif (preg_match('/Grade\s*(\d+)/i', $sampleName, $m)) { $labelsByClassId[$cid] = 'Grade ' . (int)$m[1]; $gradeNumberById[$cid] = (int)$m[1]; } else { if ($cid === 0) { $labelsByClassId[$cid] = 'KG'; $kgIds[] = $cid; } elseif ($cid === 13) { $labelsByClassId[$cid] = 'Youth'; $youthIds[] = $cid; } else { $labelsByClassId[$cid] = 'Grade ' . $cid; $gradeNumberById[$cid] = $cid; } } } $sortedClassIds = []; if ($kgIds) { sort($kgIds, SORT_NUMERIC); $sortedClassIds = array_merge($sortedClassIds, $kgIds); } if ($gradeNumberById) { asort($gradeNumberById, SORT_NUMERIC); $sortedClassIds = array_merge($sortedClassIds, array_keys($gradeNumberById)); } if ($youthIds) { sort($youthIds, SORT_NUMERIC); $sortedClassIds = array_merge($sortedClassIds, $youthIds); } // Keep Arabic after Youth (if present) and before any unknown leftovers if ($arabicIds) { sort($arabicIds, SORT_NUMERIC); $sortedClassIds = array_merge($sortedClassIds, $arabicIds); } // Append any remaining class IDs (e.g., Arabic class) that didn’t fit standard buckets $presentIds = array_map('intval', array_keys($grades)); $missingIds = array_values(array_diff($presentIds, $sortedClassIds)); if (!empty($missingIds)) { $sortedClassIds = array_merge($sortedClassIds, $missingIds); } $labelFor = function (int $id) use ($labelsByClassId): string { return $labelsByClassId[$id] ?? ('Class ' . $id); }; $slugFor = function (int $id) use ($labelsByClassId, $gradeNumberById, $arabicIds): string { $lbl = $labelsByClassId[$id] ?? ''; if ($lbl === 'KG') return 'kg'; if ($lbl === 'Youth') return 'youth'; if (in_array($id, $arabicIds, true) || strcasecmp($lbl, 'Arabic') === 0) return 'arabic'; $n = $gradeNumberById[$id] ?? null; return $n !== null ? 'g' . $n : (string)$id; }; // Distinct student counts per classId for tab badges $studentsCountByClassId = []; foreach ($grades as $classId => $sections) { $cid = (int)$classId; $uniq = []; foreach ($sections as $section) { $secId = $section['class_section_id'] ?? null; if ($secId && !empty($studentsBySection[$secId])) { foreach ($studentsBySection[$secId] as $stu) { if (!empty($stu['id'])) $uniq[(int)$stu['id']] = true; } } } $studentsCountByClassId[$cid] = count($uniq); } // Default active tab = first in sorted order (can still be overridden by ?class_id) $defaultId = $sortedClassIds[0] ?? null; // ===================================================================== // NEW UTILITIES (dates pivot logic) — includes "today if Sunday, else next Sunday" column // ===================================================================== $__datesBySection = (isset($datesBySection) && is_array($datesBySection)) ? $datesBySection : []; $__dateList = (isset($dateList) && is_array($dateList)) ? array_values(array_filter(array_map('strval', $dateList))) : []; $__attendanceData = (isset($attendanceData) && is_array($attendanceData)) ? $attendanceData : []; $__noSchoolDays = (isset($noSchoolDays) && is_array($noSchoolDays)) ? $noSchoolDays : []; try { $tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $tzObj = new DateTimeZone($tzName ?: 'UTC'); } catch (\Throwable $e) { try { $tzObj = new DateTimeZone(user_timezone() ?: 'UTC'); } catch (\Throwable $e2) { $tzObj = new DateTimeZone('UTC'); } } try { $__nowDate = new DateTime('now', $tzObj); } catch (\Throwable $e) { $__nowDate = new DateTime('now'); } $weekday = (int) $__nowDate->format('w'); if ($weekday === 0) { $__anchorSunday = $__nowDate->format('Y-m-d'); } else { $__anchorSunday = (clone $__nowDate)->modify('next sunday')->format('Y-m-d'); } $buildDateCols = function ($sectionId) use ($__datesBySection, $__dateList, $__attendanceData, $__anchorSunday, $__nowDate, $__noSchoolDays) { $filterDates = function (array $items) use ($__noSchoolDays) { $map = []; foreach ($items as $item) { $d = (string)$item; if ($d === '' || !empty($__noSchoolDays[$d])) { continue; } $map[$d] = $d; } return array_values($map); }; $candidates = []; if (!empty($__datesBySection[$sectionId]) && is_array($__datesBySection[$sectionId])) { $candidates = array_merge($candidates, $__datesBySection[$sectionId]); } if (!empty($__dateList) && is_array($__dateList)) { $candidates = array_merge($candidates, $__dateList); } $cols = $filterDates($candidates); if (!$cols && !empty($__attendanceData[$sectionId]) && is_array($__attendanceData[$sectionId])) { $set = []; foreach ($__attendanceData[$sectionId] as $sid => $entries) { if (!is_array($entries)) continue; foreach ($entries as $e) { $d = (string)($e['date'] ?? ''); if ($d !== '') $set[$d] = $d; } } $cols = $filterDates(array_keys($set)); } if (!$cols) { $nowRef = clone $__nowDate; $fallback = [ (clone $nowRef)->modify('-7 days')->format('Y-m-d'), $nowRef->format('Y-m-d'), ]; $cols = $filterDates($fallback); } if ($cols) { sort($cols, SORT_STRING); } if (!empty($__anchorSunday) && empty($__noSchoolDays[$__anchorSunday]) && !in_array($__anchorSunday, $cols, true)) { $cols[] = $__anchorSunday; sort($cols, SORT_STRING); } return $cols; }; $formatDateLabel = function ($ymd) { $ymd = (string)$ymd; if ($ymd === '') return ''; if (preg_match('/^(\\d{4})-(\\d{2})-(\\d{2})$/', $ymd, $m)) { return $m[2] . '-' . $m[3] . '-' . $m[1]; } try { return local_date($ymd, 'm-d-Y'); } catch (\Throwable $e) { return $ymd; } }; $todayDate = $__nowDate->format('Y-m-d'); $todayLabel = $todayDate ? $formatDateLabel($todayDate) : ''; // Build {date => {status, is_reported}} $recAt = function (array $entries) { $map = []; foreach ($entries as $e) { $d = (string)($e['date'] ?? ''); if ($d !== '') { $map[$d] = [ 'status' => (string)($e['status'] ?? ''), 'is_reported' => (string)($e['is_reported'] ?? 'no'), ]; } } return $map; }; // Dropdown options + badge meta (first is placeholder) $optionDefs = [ 'none' => ['label' => 'Select attendance', 'status' => '', 'is_reported' => '', 'badge' => 'secondary', 'short' => '—'], 'present' => ['label' => 'Present', 'status' => 'present', 'is_reported' => 'no', 'badge' => 'success', 'short' => 'P'], 'late_yes' => ['label' => 'Late — Reported', 'status' => 'late', 'is_reported' => 'yes', 'badge' => 'warning text-dark', 'short' => 'LR'], 'late_no' => ['label' => 'Late — Not-reported', 'status' => 'late', 'is_reported' => 'no', 'badge' => 'warning text-dark', 'short' => 'LnR'], 'absent_yes' => ['label' => 'Absent — Reported', 'status' => 'absent', 'is_reported' => 'yes', 'badge' => 'danger', 'short' => 'AR'], 'absent_no' => ['label' => 'Absent — Not-reported', 'status' => 'absent', 'is_reported' => 'no', 'badge' => 'danger', 'short' => 'AnR'], ]; ?>
$maxNameLen) $maxNameLen = $len_; } $minNameCh = max(12, $maxNameLen + 2); // Compute longest school ID length (characters) for this section (ensure header fits: 9ch) $maxIdLen = 0; foreach ($studentsBySection[$sectionKey] as $s_) { $id_ = trim((string)($s_['school_id'] ?? '')); $len_ = function_exists('mb_strlen') ? mb_strlen($id_, 'UTF-8') : strlen($id_); if ($len_ > $maxIdLen) $maxIdLen = $len_; } $minIdCh = max(9, $maxIdLen); // Derive a human-friendly section label above the table $rawSecName = trim((string)($section['class_section_name'] ?? '')); // Base label from section name (fallback to tab label) $sectionLabel = ($rawSecName !== '') ? $rawSecName : $label; // Add "Grade " prefix unless already present, but NEVER for KG, Youth, or Arabic program if (preg_match('/^(KG(\b|-)|Youth(\b|-))/i', $sectionLabel)) { $headerText = $sectionLabel; } elseif (strcasecmp($label, 'Arabic') === 0 || stripos($sectionLabel, 'Arabic') === 0) { $headerText = $sectionLabel; } elseif (preg_match('/^\s*Grade\b/i', $sectionLabel)) { $headerText = $sectionLabel; } else { $headerText = 'Grade ' . $sectionLabel; } ?>

 
"bg-warning text-dark" or "bg-secondary" $dLabel = $formatDateLabel($dcol); ?>
School ID Student Name P L A T
Parent report
endSection() ?> section('scripts') ?> endSection() ?>