staff = model(Staff::class); $this->user = model(User::class); $this->teacherClass = model(TeacherClass::class); $this->config = model(Configuration::class); $this->schoolYear = (string) ($this->config->getConfig('school_year') ?? ''); $this->semester = (string) ($this->config->getConfig('semester') ?? ''); } public function index() { $page = max(1, (int) ($this->request->getGet('page') ?? 1)); $perPage = min(100, max(1, (int) ($this->request->getGet('per_page') ?? 20))); $role = $this->request->getGet('role'); // Roles we never show $excludedRoles = ['student', 'parent', 'guest', 'inactive']; $normalizedExcluded = array_map('strtolower', $excludedRoles); $placeholders = implode(',', array_fill(0, count($normalizedExcluded), '?')); $query = $this->staff->newQuery() ->whereRaw('LOWER(active_role) NOT IN (' . $placeholders . ')', $normalizedExcluded); if (!empty($role)) { $query->whereRaw('LOWER(active_role) = ?', [strtolower((string) $role)]); } $query->orderByDesc('created_at'); // Preload assignments for the selected school year (across all semesters) $assignRows = DB::table('teacher_class as tc') ->select('tc.teacher_id', 'tc.position', 'cs.class_section_name') ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'tc.class_section_id') ->where('tc.school_year', (string) $this->schoolYear) ->get() ->toArray(); $assignByTeacher = []; foreach ($assignRows as $row) { $teacherId = (int) ($row->teacher_id ?? 0); if ($teacherId <= 0) { continue; } $section = trim((string) ($row->class_section_name ?? '')); if ($section === '') { continue; } $position = strtolower((string) ($row->position ?? '')); if (!in_array($position, ['main', 'ta'], true)) { continue; } $assignByTeacher[$teacherId][] = $section . ' (' . $position . ')'; } // Get all staff (not paginated) for issues count calculation $allStaff = $query->get()->toArray(); $issuesCount = 0; foreach ($allStaff as $staff) { $roleName = strtolower((string) ($staff['active_role'] ?? '')); if (in_array($roleName, ['teacher', 'teacher_assistant'], true)) { $userId = (int) ($staff['user_id'] ?? 0); $labels = $assignByTeacher[$userId] ?? []; if (empty($labels)) { $issuesCount++; } } } // Paginate the results $result = $this->paginate($query, $page, $perPage); // Apply transformations to paginated data foreach ($result['data'] as &$staff) { $userId = (int) ($staff['user_id'] ?? 0); $staff['school_id'] = $userId > 0 ? $this->user->getSchoolIdByUserId($userId) : null; $roleName = strtolower((string) ($staff['active_role'] ?? '')); if (in_array($roleName, ['teacher', 'teacher_assistant'], true)) { $labels = $assignByTeacher[$userId] ?? []; if (!empty($labels)) { $staff['class_section'] = implode(', ', array_unique($labels)); $staff['verification_issue'] = false; } else { $staff['class_section'] = 'No class assigned'; $staff['verification_issue'] = true; } } else { $staff['class_section'] = '—'; $staff['verification_issue'] = false; } } return $this->success([ 'data' => $result['data'], 'pagination' => $result['pagination'], 'issues_count' => $issuesCount, 'semester' => $this->semester, 'school_year' => $this->schoolYear, ], 'Staff members retrieved successfully'); } public function show($id = null) { $staff = $this->staff->find($id); if (!$staff) { return $this->respondError('Staff member not found', Response::HTTP_NOT_FOUND); } $staff['school_id'] = $this->user->getSchoolIdByUserId((int) ($staff['user_id'] ?? 0)); return $this->success($staff, 'Staff member retrieved successfully'); } public function store() { $data = $this->payloadData(); if (empty($data)) { return $this->respondError('Invalid request data', Response::HTTP_BAD_REQUEST); } $now = utc_now(); $payload = [ 'firstname' => trim((string) ($data['firstname'] ?? '')), 'lastname' => trim((string) ($data['lastname'] ?? '')), 'email' => trim((string) ($data['email'] ?? '')), 'phone' => trim((string) ($data['phone'] ?? '')), 'role_name' => trim((string) ($data['role_name'] ?? '')), 'school_year' => trim((string) ($data['school_year'] ?? $this->schoolYear)), 'status' => trim((string) ($data['status'] ?? 'Active')), 'created_at' => $now, 'updated_at' => $now, ]; // Set active_role from role_name if provided if (!empty($payload['role_name'])) { $payload['active_role'] = strtolower($payload['role_name']); } try { $inserted = $this->staff->insert($payload); if (!$inserted) { return $this->respondError('Failed to create staff member', Response::HTTP_INTERNAL_SERVER_ERROR); } // Fetch the created record $staff = $this->staff->where('created_at', $now)->orderBy('id', 'DESC')->first(); return $this->success($staff, 'Staff member added successfully', Response::HTTP_CREATED); } catch (\Throwable $e) { \Illuminate\Support\Facades\Log::error('Staff creation error: ' . $e->getMessage()); return $this->respondError('Failed to create staff member', Response::HTTP_INTERNAL_SERVER_ERROR); } } public function update($id = null) { $staff = $this->staff->find($id); if (!$staff) { return $this->respondError('Staff member not found', Response::HTTP_NOT_FOUND); } $data = $this->payloadData(); if (empty($data)) { return $this->respondError('Invalid request data', Response::HTTP_BAD_REQUEST); } // Only update fields provided by the request to avoid wiping data $updateData = []; $allowedFields = ['firstname', 'lastname', 'email', 'phone', 'role_name', 'school_year', 'status']; foreach ($allowedFields as $field) { if (array_key_exists($field, $data)) { $value = $data[$field]; if ($value !== null && $value !== '') { $updateData[$field] = trim((string) $value); } } } // Set active_role from role_name if role_name is being updated if (isset($updateData['role_name']) && !empty($updateData['role_name'])) { $updateData['active_role'] = strtolower($updateData['role_name']); } // Always bump updated_at $updateData['updated_at'] = utc_now(); if (empty($updateData)) { return $this->respondError('No changes detected', Response::HTTP_BAD_REQUEST); } try { $updated = $this->staff->update($id, $updateData); if (!$updated) { return $this->respondError('Failed to update staff member', Response::HTTP_INTERNAL_SERVER_ERROR); } $updatedStaff = $this->staff->find($id); return $this->success($updatedStaff, 'Staff member updated successfully'); } catch (\Throwable $e) { \Illuminate\Support\Facades\Log::error('Staff update error: ' . $e->getMessage()); return $this->respondError('Failed to update staff member', Response::HTTP_INTERNAL_SERVER_ERROR); } } public function destroy($id = null) { $staff = $this->staff->find($id); if (!$staff) { return $this->respondError('Staff member not found', Response::HTTP_NOT_FOUND); } try { $this->staff->delete($id); return $this->respondDeleted(null, 'Staff member deleted successfully'); } catch (\Throwable $e) { \Illuminate\Support\Facades\Log::error('Staff deletion error: ' . $e->getMessage()); return $this->respondError('Failed to delete staff member', Response::HTTP_INTERNAL_SERVER_ERROR); } } }