currentUser(); if (! $user) { return $this->errorResponse('Unauthorized', null, null, ResponseInterface::HTTP_UNAUTHORIZED); } $studentId = (int) $this->request->getGet('student_id'); $classId = (int) $this->request->getGet('class_id'); $fromDate = $this->request->getGet('from_date'); $toDate = $this->request->getGet('to_date'); $db = \Config\Database::connect(); $builder = $db->table('student_violations AS sv') ->select('sv.id, sv.student_id, sv.class_id, sv.violation_id, sv.reported_by_user_id, sv.occurred_at, sv.notes, s.name AS student_name, c.grade AS class_grade, c.major AS class_major, c.name AS class_name, v.title AS violation_title, v.score AS violation_score, vc.code AS category_code, vc.name AS category_name, u.name AS reporter_name') ->join('students AS s', 's.id = sv.student_id', 'left') ->join('classes AS c', 'c.id = sv.class_id', 'left') ->join('violations AS v', 'v.id = sv.violation_id', 'left') ->join('violation_categories AS vc', 'vc.id = v.category_id', 'left') ->join('users AS u', 'u.id = sv.reported_by_user_id', 'left') ->orderBy('sv.occurred_at', 'DESC') ->orderBy('sv.id', 'DESC'); if ($studentId > 0) { $builder->where('sv.student_id', $studentId); } if ($classId > 0) { $builder->where('sv.class_id', $classId); } if ($fromDate) { $builder->where('sv.occurred_at >=', $fromDate . ' 00:00:00'); } if ($toDate) { $builder->where('sv.occurred_at <=', $toDate . ' 23:59:59'); } $rows = $builder->get()->getResultArray(); $data = array_map(static function (array $r) { $classLabel = null; if ($r['class_grade'] !== null || $r['class_major'] !== null || $r['class_name'] !== null) { $parts = array_filter([ trim((string) ($r['class_grade'] ?? '')), trim((string) ($r['class_major'] ?? '')), trim((string) ($r['class_name'] ?? '')), ]); $classLabel = implode(' ', $parts); } return [ 'id' => (int) $r['id'], 'student_id' => (int) $r['student_id'], 'student_name' => (string) $r['student_name'], 'class_id' => $r['class_id'] !== null ? (int) $r['class_id'] : null, 'class_label' => $classLabel, 'violation_id' => (int) $r['violation_id'], 'category_code' => $r['category_code'], 'category_name' => $r['category_name'], 'violation_title' => $r['violation_title'], 'violation_score' => (int) $r['violation_score'], 'reported_by_user_id' => $r['reported_by_user_id'] !== null ? (int) $r['reported_by_user_id'] : null, 'reported_by_name' => $r['reporter_name'], 'occurred_at' => $r['occurred_at'], 'notes' => $r['notes'], ]; }, $rows); return $this->successResponse($data, 'Student violations'); } /** * POST /api/discipline/student-violations * Body: { student_id, violation_id, occurred_at?, notes? } * Hanya ADMIN, GURU_MAPEL, WALI_KELAS. */ public function create(): ResponseInterface { $auth = new AuthService(); $user = $auth->currentUser(); if (! $user) { return $this->errorResponse('Unauthorized', null, null, ResponseInterface::HTTP_UNAUTHORIZED); } $roleCodes = array_column($user['roles'], 'role_code'); $allowedRoles = ['ADMIN', 'GURU_MAPEL', 'WALI_KELAS']; if (! array_intersect($allowedRoles, $roleCodes)) { return $this->errorResponse('Forbidden', null, null, ResponseInterface::HTTP_FORBIDDEN); } $payload = $this->request->getJSON(true) ?? []; $studentId = (int) ($payload['student_id'] ?? 0); $violationId = (int) ($payload['violation_id'] ?? 0); $occurredAt = $payload['occurred_at'] ?? null; $notes = $payload['notes'] ?? null; if ($studentId <= 0 || $violationId <= 0) { return $this->errorResponse('student_id dan violation_id wajib diisi', null, null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY); } $studentModel = new StudentModel(); $classModel = new ClassModel(); $violationModel = new ViolationModel(); $student = $studentModel->find($studentId); if (! $student) { return $this->errorResponse('Siswa tidak ditemukan', null, null, ResponseInterface::HTTP_NOT_FOUND); } $violation = $violationModel->find($violationId); if (! $violation || (int) $violation['is_active'] !== 1) { return $this->errorResponse('Pelanggaran tidak ditemukan atau tidak aktif', null, null, ResponseInterface::HTTP_NOT_FOUND); } $classId = $student->class_id !== null ? (int) $student->class_id : null; if ($occurredAt === null || trim($occurredAt) === '') { $occurredAt = date('Y-m-d H:i:s'); } $model = new StudentViolationModel(); $model->insert([ 'student_id' => $studentId, 'class_id' => $classId, 'violation_id' => $violationId, 'reported_by_user_id' => (int) $user['id'], 'occurred_at' => $occurredAt, 'notes' => $notes, ]); if ($model->errors()) { return $this->errorResponse(implode(' ', $model->errors()), $model->errors(), null, ResponseInterface::HTTP_UNPROCESSABLE_ENTITY); } return $this->successResponse(null, 'Pelanggaran siswa berhasil dicatat'); } }