add projet
This commit is contained in:
+748
@@ -0,0 +1,748 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Configuration;
|
||||
use App\Models\TeacherClass;
|
||||
use App\Models\User;
|
||||
use App\Models\Teacher;
|
||||
use App\Models\Student;
|
||||
use App\Models\ClassSection;
|
||||
use App\Models\StudentClass;
|
||||
use App\Models\StudentMedicalCondition;
|
||||
use App\Models\StudentAllergy;
|
||||
use App\Models\StaffAttendance;
|
||||
use App\Services\EmailService;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class TeacherController extends BaseApiController
|
||||
{
|
||||
protected User $user;
|
||||
protected TeacherClass $teacherClass;
|
||||
protected Configuration $config;
|
||||
protected Teacher $teacher;
|
||||
protected Student $student;
|
||||
protected ClassSection $classSection;
|
||||
protected StudentClass $studentClass;
|
||||
protected StudentMedicalCondition $medical;
|
||||
protected StudentAllergy $allergy;
|
||||
protected StaffAttendance $staffAttendance;
|
||||
protected EmailService $emailService;
|
||||
protected string $semester;
|
||||
protected string $schoolYear;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->user = model(User::class);
|
||||
$this->teacherClass = model(TeacherClass::class);
|
||||
$this->config = model(Configuration::class);
|
||||
$this->teacher = model(Teacher::class);
|
||||
$this->student = model(Student::class);
|
||||
$this->classSection = model(ClassSection::class);
|
||||
$this->studentClass = model(StudentClass::class);
|
||||
$this->medical = model(StudentMedicalCondition::class);
|
||||
$this->allergy = model(StudentAllergy::class);
|
||||
$this->staffAttendance = model(StaffAttendance::class);
|
||||
$this->emailService = app(EmailService::class);
|
||||
$this->semester = (string) ($this->config->getConfig('semester') ?? '');
|
||||
$this->schoolYear = (string) ($this->config->getConfig('school_year') ?? '');
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$page = max(1, (int) ($this->request->getGet('page') ?? 1));
|
||||
$perPage = min(100, max(1, (int) ($this->request->getGet('per_page') ?? 20)));
|
||||
|
||||
$query = $this->user->newQuery()
|
||||
->select('users.*')
|
||||
->join('user_roles', 'user_roles.user_id', '=', 'users.id')
|
||||
->join('roles', 'roles.id', '=', 'user_roles.role_id')
|
||||
->whereIn('LOWER(roles.name)', ['teacher', 'teacher_assistant'])
|
||||
->groupBy('users.id')
|
||||
->orderBy('users.lastname', 'ASC');
|
||||
|
||||
$result = $this->paginate($query, $page, $perPage);
|
||||
|
||||
foreach ($result['data'] as &$teacher) {
|
||||
unset($teacher['password'], $teacher['token']);
|
||||
|
||||
$assignments = $this->teacherClass->newQuery()
|
||||
->where('teacher_id', $teacher['id'])
|
||||
->where('school_year', $this->schoolYear)
|
||||
->where('semester', $this->semester)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$teacher['class_assignments'] = $assignments;
|
||||
}
|
||||
|
||||
return $this->success($result, 'Teachers retrieved successfully');
|
||||
}
|
||||
|
||||
public function show($id = null)
|
||||
{
|
||||
$teacher = $this->user->newQuery()
|
||||
->select('users.*')
|
||||
->join('user_roles', 'user_roles.user_id', '=', 'users.id')
|
||||
->join('roles', 'roles.id', '=', 'user_roles.role_id')
|
||||
->whereIn('LOWER(roles.name)', ['teacher', 'teacher_assistant'])
|
||||
->where('users.id', $id)
|
||||
->first();
|
||||
|
||||
if (!$teacher) {
|
||||
return $this->respondError('Teacher not found', Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$teacherArray = $teacher->toArray();
|
||||
unset($teacherArray['password'], $teacherArray['token']);
|
||||
|
||||
$assignments = $this->teacherClass->newQuery()
|
||||
->where('teacher_id', $id)
|
||||
->where('school_year', $this->schoolYear)
|
||||
->where('semester', $this->semester)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$teacherArray['class_assignments'] = $assignments;
|
||||
|
||||
return $this->success($teacherArray, 'Teacher retrieved successfully');
|
||||
}
|
||||
|
||||
public function getClasses($id = null)
|
||||
{
|
||||
$assignments = $this->teacherClass->newQuery()
|
||||
->select([
|
||||
'tc.*',
|
||||
'cs.class_section_name',
|
||||
'c.name as class_name',
|
||||
])
|
||||
->from('teacher_class as tc')
|
||||
->leftJoin('class_sections as cs', 'cs.class_section_id', '=', 'tc.class_section_id')
|
||||
->leftJoin('classes as c', 'c.id', '=', 'cs.class_id')
|
||||
->where('tc.teacher_id', $id)
|
||||
->where('tc.school_year', $this->schoolYear)
|
||||
->where('tc.semester', $this->semester)
|
||||
->orderBy('cs.class_section_name', 'ASC')
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
return $this->success($assignments, 'Teacher classes retrieved successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/teachers/class-view
|
||||
* Get class view data for the logged-in teacher
|
||||
*/
|
||||
public function getClassView()
|
||||
{
|
||||
try {
|
||||
$userId = $this->getCurrentUserId();
|
||||
if (!$userId) {
|
||||
return $this->respondError('User not logged in', Response::HTTP_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
// Fetch all assignments (as main or TA)
|
||||
$classes = $this->teacherClass->getClassAssignmentsByUserId($userId, $this->schoolYear);
|
||||
|
||||
if (empty($classes)) {
|
||||
return $this->success([
|
||||
'has_classes' => false,
|
||||
'message' => 'You do not have an assigned class yet. Please contact the administration.',
|
||||
], 'No classes found');
|
||||
}
|
||||
|
||||
// Identify user and their role
|
||||
$user = $this->user->find($userId);
|
||||
if (!$user) {
|
||||
return $this->respondError('User not found', Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$roles = DB::table('user_roles ur')
|
||||
->select('r.name')
|
||||
->join('roles r', 'r.id', '=', 'ur.role_id')
|
||||
->where('ur.user_id', $userId)
|
||||
->get()
|
||||
->map(fn($r) => strtolower($r->name))
|
||||
->toArray();
|
||||
|
||||
$roleName = 'teacher';
|
||||
if (in_array('teacher', $roles)) {
|
||||
$roleName = 'teacher';
|
||||
} elseif (in_array('teacher_assistant', $roles)) {
|
||||
$roleName = 'teacher_assistant';
|
||||
} else {
|
||||
return $this->respondError('Access denied', Response::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
// Collect class section IDs
|
||||
$classSectionIds = array_column($classes, 'class_section_id');
|
||||
$studentsData = [];
|
||||
|
||||
if (!empty($classSectionIds)) {
|
||||
// Fetch all students for the selected class sections
|
||||
$students = $this->studentClass->getStudentsByClassSectionIds($classSectionIds);
|
||||
|
||||
if (!empty($students)) {
|
||||
// Collect unique student IDs
|
||||
$studentIds = array_values(array_unique(array_map(
|
||||
static fn(array $row) => (int) ($row['student_id'] ?? 0),
|
||||
$students
|
||||
)));
|
||||
$studentIds = array_values(array_filter($studentIds));
|
||||
|
||||
// Bulk fetch medical conditions & allergies grouped by student_id
|
||||
$conditionsByStudent = !empty($studentIds)
|
||||
? $this->medical->getMedicalByStudentIds($studentIds)
|
||||
: [];
|
||||
|
||||
$allergiesByStudent = !empty($studentIds)
|
||||
? $this->allergy->getAllergiesByStudentIds($studentIds)
|
||||
: [];
|
||||
|
||||
// Attach medical/allergy data to each student
|
||||
foreach ($students as $student) {
|
||||
$sid = (int) ($student['student_id'] ?? 0);
|
||||
$cid = (int) ($student['class_section_id'] ?? 0);
|
||||
|
||||
$medList = array_column($conditionsByStudent[$sid] ?? [], 'condition_name');
|
||||
$algList = array_column($allergiesByStudent[$sid] ?? [], 'allergy');
|
||||
|
||||
$student['medical_conditions'] = $medList;
|
||||
$student['allergies'] = $algList;
|
||||
$student['medical_conditions_text'] = implode(', ', $medList);
|
||||
$student['allergies_text'] = implode(', ', $algList);
|
||||
|
||||
$studentsData[$cid][] = $student;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build assigned staff lists (all Main teachers and all TAs) per class section
|
||||
$assignedNames = [];
|
||||
if (!empty($classSectionIds)) {
|
||||
$rows = DB::table('teacher_class tc')
|
||||
->select('tc.class_section_id', 'tc.position', 'u.firstname', 'u.lastname')
|
||||
->join('users u', 'u.id', '=', 'tc.teacher_id')
|
||||
->whereIn('tc.class_section_id', $classSectionIds)
|
||||
->where('tc.school_year', $this->schoolYear)
|
||||
->where('tc.semester', $this->semester)
|
||||
->orderBy('tc.class_section_id', 'ASC')
|
||||
->orderByRaw("FIELD(tc.position,'main','ta')")
|
||||
->orderBy('u.firstname', 'ASC')
|
||||
->get();
|
||||
|
||||
foreach ($rows as $r) {
|
||||
$sid = (int) ($r->class_section_id ?? 0);
|
||||
$pos = strtolower((string) ($r->position ?? ''));
|
||||
$name = trim(($r->firstname ?? '') . ' ' . ($r->lastname ?? ''));
|
||||
|
||||
if ($sid === 0 || $name === '') continue;
|
||||
|
||||
if (in_array($pos, ['main', 'teacher'], true)) {
|
||||
$assignedNames[$sid]['mains'][] = $name;
|
||||
} elseif (in_array($pos, ['ta', 'assistant', 'teacher_assistant'], true)) {
|
||||
$assignedNames[$sid]['tas'][] = $name;
|
||||
} else {
|
||||
$assignedNames[$sid]['others'][] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$classSectionId = $classSectionIds[0] ?? null;
|
||||
|
||||
return $this->success([
|
||||
'teacher_name' => trim(($user['firstname'] ?? '') . ' ' . ($user['lastname'] ?? '')),
|
||||
'role_name' => $roleName,
|
||||
'classes' => $classes,
|
||||
'students' => $studentsData[$classSectionId] ?? [],
|
||||
'studentsData' => $studentsData,
|
||||
'class_section_id' => $classSectionId,
|
||||
'assignedNames' => $assignedNames,
|
||||
], 'Class view data retrieved successfully');
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error in TeacherController::getClassView(): ' . $e->getMessage());
|
||||
return $this->respondError('An error occurred. Please try again later.', Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/teachers/class-assignments
|
||||
* Get teacher class assignment data for administration
|
||||
*/
|
||||
public function getTeacherClassAssignments()
|
||||
{
|
||||
$forYear = trim((string) ($this->request->getGet('schoolYear') ?? $this->request->getGet('school_year') ?? ''));
|
||||
$payload = $this->buildTeacherClassAssignmentPayload($forYear !== '' ? $forYear : null);
|
||||
$payload['ok'] = true;
|
||||
return $this->success($payload, 'Teacher class assignments retrieved successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/v1/teachers/class-assignments/assign
|
||||
* Assign a teacher to a class
|
||||
*/
|
||||
public function assignClassTeacher()
|
||||
{
|
||||
$data = $this->payloadData();
|
||||
$result = $this->handleAssignClassTeacher($data);
|
||||
$statusCode = $result['ok'] ? Response::HTTP_OK : Response::HTTP_UNPROCESSABLE_ENTITY;
|
||||
return response()->json($result, $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/v1/teachers/class-assignments
|
||||
* Delete a teacher class assignment
|
||||
*/
|
||||
public function deleteAssignment()
|
||||
{
|
||||
$data = $this->payloadData();
|
||||
$result = $this->handleDeleteAssignment($data);
|
||||
$statusCode = $result['ok'] ? Response::HTTP_OK : Response::HTTP_UNPROCESSABLE_ENTITY;
|
||||
return response()->json($result, $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/v1/teachers/absence-form
|
||||
* Get absence form data for the logged-in teacher
|
||||
*/
|
||||
public function getAbsenceForm()
|
||||
{
|
||||
$userId = $this->getCurrentUserId();
|
||||
if (!$userId) {
|
||||
return $this->respondError('Please log in first.', Response::HTTP_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$teacher = $this->user->find($userId);
|
||||
$displayName = $teacher ? trim(($teacher['firstname'] ?? '') . ' ' . ($teacher['lastname'] ?? '')) : 'Unknown';
|
||||
|
||||
// Fetch existing self-reported staff attendance rows for this user (current term)
|
||||
$existing = $this->staffAttendance
|
||||
->where('user_id', $userId)
|
||||
->where('semester', $this->semester)
|
||||
->where('school_year', $this->schoolYear)
|
||||
->orderBy('date', 'DESC')
|
||||
->findAll();
|
||||
|
||||
return $this->success([
|
||||
'teacher_name' => $displayName,
|
||||
'semester' => $this->semester,
|
||||
'schoolYear' => $this->schoolYear,
|
||||
'existing' => $existing,
|
||||
'availableDates' => $this->allowedAbsenceDates(),
|
||||
], 'Absence form data retrieved successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/v1/teachers/absence
|
||||
* Submit absence/vacation request
|
||||
*/
|
||||
public function submitAbsence()
|
||||
{
|
||||
$userId = $this->getCurrentUserId();
|
||||
if (!$userId) {
|
||||
return $this->respondError('Please log in first.', Response::HTTP_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($this->semester === '' || $this->schoolYear === '') {
|
||||
return $this->respondError('Semester or school year not configured.', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$data = $this->payloadData();
|
||||
$dates = (array) ($data['dates'] ?? []);
|
||||
$reasonType = trim((string) ($data['reason_type'] ?? ''));
|
||||
$reasonText = trim((string) ($data['reason'] ?? ''));
|
||||
|
||||
if ($reasonText === '') {
|
||||
return $this->respondValidationError(['reason' => ['Reason is required.']]);
|
||||
}
|
||||
|
||||
// Build reason string
|
||||
$reasonBase = ($reasonType !== '') ? strtolower($reasonType) : '';
|
||||
$reason = $reasonBase !== '' ? ($reasonBase . ': ' . $reasonText) : $reasonText;
|
||||
|
||||
// Enforce allowed Sundays list
|
||||
$allowedDates = $this->allowedAbsenceDates();
|
||||
$allowedSet = array_fill_keys($allowedDates, true);
|
||||
|
||||
$roleName = $this->user->getUserRole($userId) ?: null;
|
||||
|
||||
// Validate dates and save
|
||||
$saved = 0;
|
||||
$invalid = [];
|
||||
$savedDates = [];
|
||||
|
||||
$dates = array_values(array_unique(array_map('strval', $dates)));
|
||||
|
||||
foreach ($dates as $d) {
|
||||
$d = trim((string) $d);
|
||||
if ($d === '') continue;
|
||||
|
||||
$dt = \DateTimeImmutable::createFromFormat('Y-m-d', $d);
|
||||
if (!$dt || $dt->format('Y-m-d') !== $d || empty($allowedSet[$d])) {
|
||||
$invalid[] = $d;
|
||||
continue;
|
||||
}
|
||||
|
||||
$ok = $this->staffAttendance->upsertOne(
|
||||
userId: $userId,
|
||||
roleName: $roleName,
|
||||
date: $d,
|
||||
semester: $this->semester,
|
||||
schoolYear: $this->schoolYear,
|
||||
status: StaffAttendance::STATUS_ABSENT,
|
||||
reason: $reason,
|
||||
editorId: $userId
|
||||
);
|
||||
|
||||
if ($ok) {
|
||||
$saved++;
|
||||
$savedDates[] = $d;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($invalid)) {
|
||||
return $this->respondError('Invalid dates: ' . implode(', ', $invalid), Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
// Send notification email to principal
|
||||
try {
|
||||
$user = $this->user->find($userId) ?: [];
|
||||
$fullName = trim(($user['firstname'] ?? '') . ' ' . ($user['lastname'] ?? '')) ?: 'Unknown';
|
||||
$userEmail = $user['email'] ?? '';
|
||||
$role = $roleName ?: 'staff';
|
||||
$dateList = !empty($savedDates) ? implode(', ', $savedDates) : implode(', ', $dates);
|
||||
|
||||
// If teacher/TA, include assigned class(es) for current term
|
||||
$assignedText = '-';
|
||||
if (in_array(strtolower((string) $roleName), ['teacher', 'teacher_assistant'], true)) {
|
||||
try {
|
||||
$assignments = $this->teacherClass->getClassAssignmentsByUserId($userId, $this->schoolYear, $this->semester);
|
||||
if (!empty($assignments)) {
|
||||
$parts = [];
|
||||
foreach ($assignments as $as) {
|
||||
$name = (string) ($as['class_section_name'] ?? '');
|
||||
$r = (string) ($as['teacher_role'] ?? '');
|
||||
if ($name !== '') {
|
||||
$parts[] = $r ? ($name . ' (' . $r . ')') : $name;
|
||||
}
|
||||
}
|
||||
if (!empty($parts)) {
|
||||
$assignedText = implode(', ', $parts);
|
||||
} else {
|
||||
$assignedText = 'No assigned class';
|
||||
}
|
||||
} else {
|
||||
$assignedText = 'No assigned class';
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$assignedText = 'N/A';
|
||||
}
|
||||
}
|
||||
|
||||
$subject = sprintf('TimeOff Request: %s (%s) — %s', $fullName, ucfirst((string) $role), $dateList ?: 'No dates');
|
||||
$submittedAt = utc_now();
|
||||
|
||||
$body = '<div style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:1.5;">'
|
||||
. '<h3 style="margin:0 0 8px;">New TimeOff Request</h3>'
|
||||
. '<p style="margin:0 0 12px;">A staff time-off request was submitted from the teacher portal.</p>'
|
||||
. '<table cellpadding="6" cellspacing="0" style="border-collapse:collapse;">'
|
||||
. '<tr><td><strong>Name</strong></td><td>' . esc($fullName) . '</td></tr>'
|
||||
. '<tr><td><strong>Email</strong></td><td>' . esc($userEmail) . '</td></tr>'
|
||||
. '<tr><td><strong>Role</strong></td><td>' . esc((string) $role) . '</td></tr>'
|
||||
. '<tr><td><strong>Semester</strong></td><td>' . esc($this->semester) . '</td></tr>'
|
||||
. '<tr><td><strong>School Year</strong></td><td>' . esc($this->schoolYear) . '</td></tr>'
|
||||
. '<tr><td><strong>Reason Type</strong></td><td>' . esc($reasonType ?: '-') . '</td></tr>'
|
||||
. '<tr><td><strong>Reason</strong></td><td>' . esc($reasonText) . '</td></tr>'
|
||||
. '<tr><td><strong>Dates</strong></td><td>' . esc($dateList ?: '-') . '</td></tr>'
|
||||
. (in_array(strtolower((string) $roleName), ['teacher', 'teacher_assistant'], true)
|
||||
? '<tr><td><strong>Assigned Class(es)</strong></td><td>' . esc($assignedText) . '</td></tr>'
|
||||
: '')
|
||||
. '<tr><td><strong>Submitted At</strong></td><td>' . esc($submittedAt) . '</td></tr>'
|
||||
. '</table>'
|
||||
. '</div>';
|
||||
|
||||
// Note: StaffTimeOffLinkService not found, skipping link generation
|
||||
$principalEmail = env('PRINCIPAL_EMAIL') ?: 'principal@alrahmaisgl.org';
|
||||
$this->emailService->send($principalEmail, $subject, $body);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('Failed to send TimeOff email: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'saved' => $saved,
|
||||
'savedDates' => $savedDates,
|
||||
], $saved . ' day(s) saved as absent.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build teacher class assignment payload
|
||||
*/
|
||||
private function buildTeacherClassAssignmentPayload(?string $forYear = null): array
|
||||
{
|
||||
$schoolYear = $forYear ?: $this->schoolYear;
|
||||
|
||||
$teachers = $this->teacher->getTeachersAndTAs();
|
||||
|
||||
// Prefer class sections for the selected year, fallback to all if none
|
||||
$classSections = $this->classSection
|
||||
->where('school_year', $schoolYear)
|
||||
->orderBy('class_section_name', 'ASC')
|
||||
->findAll();
|
||||
|
||||
if (empty($classSections)) {
|
||||
$classSections = $this->classSection->orderBy('class_section_name', 'ASC')->findAll();
|
||||
}
|
||||
|
||||
$classNames = [];
|
||||
foreach ($classSections as $section) {
|
||||
$sectionId = (int) ($section['class_section_id'] ?? $section['id'] ?? 0);
|
||||
if ($sectionId <= 0) continue;
|
||||
$classNames[$sectionId] = $section['class_section_name'] ?? ($section['name'] ?? 'N/A');
|
||||
}
|
||||
|
||||
$assignments = $this->teacherClass
|
||||
->where('school_year', $schoolYear)
|
||||
->findAll();
|
||||
|
||||
$assignmentMap = [];
|
||||
foreach ($assignments as $assign) {
|
||||
$teacherId = (int) ($assign['teacher_id'] ?? 0);
|
||||
$sectionId = (int) ($assign['class_section_id'] ?? 0);
|
||||
if ($teacherId <= 0 || $sectionId <= 0) continue;
|
||||
|
||||
$role = strtolower((string) ($assign['position'] ?? ''));
|
||||
if (!in_array($role, ['main', 'ta'], true)) continue;
|
||||
|
||||
if (!isset($assignmentMap[$teacherId])) {
|
||||
$assignmentMap[$teacherId] = ['main' => [], 'ta' => []];
|
||||
}
|
||||
|
||||
$assignmentMap[$teacherId][$role][] = [
|
||||
'section_id' => $sectionId,
|
||||
'class_section_name' => $classNames[$sectionId] ?? 'N/A',
|
||||
'role' => $role,
|
||||
'semester' => (string) ($assign['semester'] ?? $this->semester ?? ''),
|
||||
'school_year' => (string) ($assign['school_year'] ?? $schoolYear),
|
||||
];
|
||||
}
|
||||
|
||||
$teacherData = [];
|
||||
foreach ($teachers as $teacher) {
|
||||
$tid = (int) ($teacher['id'] ?? 0);
|
||||
if ($tid <= 0) continue;
|
||||
|
||||
$assigned = $assignmentMap[$tid] ?? ['main' => [], 'ta' => []];
|
||||
$firstName = $teacher['firstname'] ?? '';
|
||||
$lastName = $teacher['lastname'] ?? '';
|
||||
$name = trim($firstName . ' ' . $lastName);
|
||||
if ($name === '') {
|
||||
$name = 'User#' . $tid;
|
||||
}
|
||||
|
||||
$teacherData[] = [
|
||||
'teacher_id' => $tid,
|
||||
'firstname' => $firstName,
|
||||
'lastname' => $lastName,
|
||||
'name' => $name,
|
||||
'email' => $teacher['email'] ?? '',
|
||||
'cellphone' => $teacher['cellphone'] ?? '',
|
||||
'role' => $teacher['role'] ?? '',
|
||||
'school_year' => $this->schoolYear,
|
||||
'main_assignments' => $assigned['main'],
|
||||
'ta_assignments' => $assigned['ta'],
|
||||
];
|
||||
}
|
||||
|
||||
$classesPayload = [];
|
||||
foreach ($classSections as $section) {
|
||||
$sectionId = (int) ($section['class_section_id'] ?? $section['id'] ?? 0);
|
||||
if ($sectionId <= 0) continue;
|
||||
|
||||
$classesPayload[] = [
|
||||
'class_section_id' => $sectionId,
|
||||
'class_section_name' => $section['class_section_name'] ?? ($section['name'] ?? 'N/A'),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'school_year' => $schoolYear,
|
||||
'teachers' => $teacherData,
|
||||
'classes' => $classesPayload,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle assign class teacher logic
|
||||
*/
|
||||
private function handleAssignClassTeacher(array $input): array
|
||||
{
|
||||
$teacherId = (int) ($input['teacher_id'] ?? 0);
|
||||
$classSectionId = (int) ($input['class_section_id'] ?? 0);
|
||||
$teacherRole = $input['teacher_role'] ?? null;
|
||||
$loggedInUserId = $this->getCurrentUserId() ?? 0;
|
||||
$currentDateTime = utc_now();
|
||||
|
||||
if ($teacherId <= 0 || $classSectionId <= 0) {
|
||||
return ['ok' => false, 'status' => 'error', 'message' => 'Missing required parameters for assignment.'];
|
||||
}
|
||||
|
||||
if (empty($this->semester) || empty($this->schoolYear)) {
|
||||
return ['ok' => false, 'status' => 'error', 'message' => 'Semester or school year not configured.'];
|
||||
}
|
||||
|
||||
if ($teacherRole === null || $teacherRole === '') {
|
||||
$teacherRole = $this->user->getUserRole($teacherId);
|
||||
}
|
||||
|
||||
$isAssistant = strtolower((string) $teacherRole) === 'teacher_assistant';
|
||||
$position = $isAssistant ? 'ta' : 'main';
|
||||
|
||||
$alreadyAssigned = $this->teacherClass->where([
|
||||
'teacher_id' => $teacherId,
|
||||
'class_section_id' => $classSectionId,
|
||||
'position' => $position,
|
||||
'semester' => $this->semester,
|
||||
'school_year' => $this->schoolYear,
|
||||
])->first();
|
||||
|
||||
if ($alreadyAssigned) {
|
||||
return ['ok' => false, 'status' => 'info', 'message' => 'This teacher is already assigned to this class with the same role.'];
|
||||
}
|
||||
|
||||
$insertData = [
|
||||
'teacher_id' => $teacherId,
|
||||
'class_section_id' => $classSectionId,
|
||||
'position' => $position,
|
||||
'semester' => $this->semester,
|
||||
'school_year' => $this->schoolYear,
|
||||
'created_at' => $currentDateTime,
|
||||
'updated_at' => $currentDateTime,
|
||||
'updated_by' => $loggedInUserId,
|
||||
];
|
||||
|
||||
try {
|
||||
$this->teacherClass->insert($insertData);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('assignClassTeacher failed: ' . $e->getMessage());
|
||||
return ['ok' => false, 'status' => 'error', 'message' => 'Unable to assign class. Please try again.'];
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'status' => 'success',
|
||||
'message' => 'Class assigned to ' . ($isAssistant ? 'Teacher Assistant' : 'Main Teacher') . ' successfully.',
|
||||
'position' => $position,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle delete assignment logic
|
||||
*/
|
||||
private function handleDeleteAssignment(array $input): array
|
||||
{
|
||||
$teacherId = (int) ($input['teacher_id'] ?? 0);
|
||||
$classSectionId = (int) ($input['class_section_id'] ?? 0);
|
||||
$position = strtolower((string) ($input['position'] ?? ''));
|
||||
|
||||
if ($teacherId <= 0 || $classSectionId <= 0 || !in_array($position, ['main', 'ta'], true)) {
|
||||
return ['ok' => false, 'status' => 'error', 'message' => 'Missing or invalid data for deletion.'];
|
||||
}
|
||||
|
||||
if (empty($this->semester) || empty($this->schoolYear)) {
|
||||
return ['ok' => false, 'status' => 'error', 'message' => 'Semester or school year not configured.'];
|
||||
}
|
||||
|
||||
$requestedSemester = trim((string) ($input['semester'] ?? ''));
|
||||
$requestedSchoolYear = trim((string) ($input['school_year'] ?? ''));
|
||||
|
||||
$semester = $requestedSemester !== '' ? $requestedSemester : $this->semester;
|
||||
$schoolYear = $requestedSchoolYear !== '' ? $requestedSchoolYear : $this->schoolYear;
|
||||
|
||||
if ($semester === '' || $schoolYear === '') {
|
||||
return ['ok' => false, 'status' => 'error', 'message' => 'Semester or school year not configured.'];
|
||||
}
|
||||
|
||||
$assignment = $this->teacherClass
|
||||
->where('teacher_id', $teacherId)
|
||||
->where('class_section_id', $classSectionId)
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->where('position', $position)
|
||||
->first();
|
||||
|
||||
if (!$assignment) {
|
||||
return ['ok' => false, 'status' => 'error', 'message' => 'Assignment not found.'];
|
||||
}
|
||||
|
||||
try {
|
||||
$this->teacherClass->delete($assignment['id']);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('deleteAssignment failed: ' . $e->getMessage());
|
||||
return ['ok' => false, 'status' => 'error', 'message' => 'Unable to remove assignment. Please try again.'];
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'status' => 'success',
|
||||
'message' => ucfirst($position) . ' assignment removed successfully.',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute all allowed absence dates (Sundays) for the configured school year
|
||||
*/
|
||||
private function allowedAbsenceDates(): array
|
||||
{
|
||||
$todayStr = local_date(utc_now(), 'Y-m-d');
|
||||
try {
|
||||
$today = new \DateTimeImmutable($todayStr);
|
||||
} catch (\Throwable $e) {
|
||||
$today = new \DateTimeImmutable();
|
||||
}
|
||||
|
||||
$sy = $this->schoolYear;
|
||||
$startYear = null;
|
||||
$endYear = null;
|
||||
|
||||
if (preg_match('/^(\d{4})\D+(\d{4})$/', $sy, $m)) {
|
||||
$startYear = (int) $m[1];
|
||||
$endYear = (int) $m[2];
|
||||
} else {
|
||||
// Fallback based on current date
|
||||
$cy = (int) date('Y');
|
||||
$cm = (int) date('n');
|
||||
if ($cm >= 9) { // Sep-Dec
|
||||
$startYear = $cy;
|
||||
$endYear = $cy + 1;
|
||||
} else { // Jan-Aug
|
||||
$startYear = $cy - 1;
|
||||
$endYear = $cy;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$start = new \DateTimeImmutable(sprintf('%04d-09-01', $startYear));
|
||||
$end = new \DateTimeImmutable(sprintf('%04d-05-31', $endYear));
|
||||
} catch (\Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Clamp start to today if in range
|
||||
if ($start < $today) {
|
||||
$start = $today;
|
||||
}
|
||||
|
||||
// Iterate and collect Sundays in range [start..end]
|
||||
$dates = [];
|
||||
for ($cursor = $start; $cursor <= $end; $cursor = $cursor->modify('+1 day')) {
|
||||
if ((int) $cursor->format('w') === 0) { // Sunday
|
||||
$dates[] = $cursor->format('Y-m-d');
|
||||
}
|
||||
}
|
||||
|
||||
return $dates;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user