validate([ 'fcm_token' => 'required|string', // 'platform' => 'nullable|string', // device_id DIHAPUS ]); $user = $request->user(); $user->update([ 'device_token' => $validated['fcm_token'], // 'platform' => $validated['platform'] ?? 'android', // device_id tidak dipakai ]); return response()->json([ 'success' => true, 'message' => 'FCM token stored successfully', ]); } public function getTopicFromClass(Request $request, $classId) { // request check Authorization header for Bearer token $user = $request->user(); if (!$user) { return response()->json(['success' => false, 'message' => 'Unauthorized'], 401); } $class = Classes::find($classId); if (!$class) { return response()->json(['success' => false, 'message' => 'Class not found'], 404); } if (!$class->fcm_topic) { return response()->json(['success' => false, 'message' => 'Class does not have an FCM topic'], 400); } // Logic untuk subscribe ke topic bisa ditambahkan di sini // Misalnya simpan di database atau langsung panggil service FCM return response()->json([ 'success' => true, 'message' => "Subscribed to class topic: {$class->fcm_topic}", 'data' => [ 'class_id' => $class->id, 'class_name' => $class->name, 'fcm_topic' => $class->fcm_topic, ] ], 200); } public function login(Request $request) { try { $user = User::where('email', $request->email)->first(); // cek apakah email user ditemukan if (!$user) { return response()->json([ 'success' => false, 'message' => 'Akun tidak ditemukan' ], 401); } // cek apakah password benar if (!$user || !Hash::check($request->password, $user->password)) { return response()->json([ 'success' => false, 'message' => 'Email atau password tidak sesuai' ], 401); } // check if user is banned if ($user->is_banned) { return response()->json([ 'success' => false, 'message' => 'Akun anda telah diblokir. Silakan hubungi administrator untuk informasi lebih lanjut.' ], 403); } $token = $user->createToken('auth_token')->plainTextToken; $this->syncTopicSubscribe($user); $userData = $user->load('teacher', 'student.class'); return response()->json([ 'success' => true, 'message' => 'Login successful', 'access_token' => $token, 'token_type' => 'Bearer', 'user' => $userData ], 200); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'Something went wrong', 'error' => $e->getMessage() // bisa dihapus di production untuk keamanan ], 500); } } public function register(Request $request) { DB::beginTransaction(); try { //code... $messages = [ 'name.required' => 'Nama harus diisi', 'name.max' => 'Nama maksimal :max karakter', 'email.required' => 'Email harus diisi', 'email.email' => 'Format email tidak valid', 'email.unique' => 'Akun ini sudah terdaftar', 'password.required' => 'Password harus diisi', 'password.min' => 'Password minimal :min karakter', 'role.required' => 'Role harus dipilih', 'role.in' => 'Role tidak valid', ]; $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:6', 'role' => 'required|in:teacher,student', ], $messages); // buat akun user $user = User::create([ 'email' => $request->email, 'password' => Hash::make($request->password), 'role' => $request->role, 'profile_picture' => $request->profile_picture ?? null, ]); // buat record sesuai role if ($user->role == 'teacher') { $messagesTeacher = [ 'nip.required' => 'NIP harus diisi', 'nip.max' => 'NIP maksimal :max karakter', 'nip.unique' => 'NIP sudah pernah dipakai', 'subject.required' => 'Mata pelajaran harus diisi', 'subject.max' => 'Mata pelajaran maksimal :max karakter', ]; $request->validate([ 'nip' => 'required|string|max:50|unique:teachers,nip', 'subject' => 'required|string|max:100', ], $messagesTeacher); $user->teacher()->create([ 'id_user' => $user->id, 'name' => $request->name, 'nip' => $request->nip, 'subject' => $request->subject, ]); } else if ($user->role == 'student') { $messagesStudent = [ 'nisn.required' => 'NISN harus diisi', 'nisn.max' => 'NISN maksimal :max karakter', 'nisn.unique' => 'Akun ini sudah terdaftar', 'id_class.required' => 'Kelas harus dipilih', 'id_class.integer' => 'ID kelas tidak valid', 'entry_year.required' => 'Tahun masuk harus diisi', 'entry_year.integer' => 'Tahun masuk tidak valid', ]; $request->validate([ 'nisn' => 'required|string|max:50|unique:students,nisn', 'id_class' => 'required|integer', 'entry_year' => 'required|integer', ], $messagesStudent); $user->student()->create([ 'id_user' => $user->id, 'name' => $request->name, 'nisn' => $request->nisn, 'id_class' => $request->id_class, 'entry_year' => $request->entry_year, 'pictures' => $request->pictures ?? null, ]); } $this->syncTopicSubscribe($user); DB::commit(); $token = $user->createToken('auth_token')->plainTextToken; return response()->json([ 'success' => true, 'message' => 'Anda berhasil register', 'access_token' => $token, 'token_type' => 'Bearer', 'user' => $user->load('teacher', 'student') ], 201); } catch (\Illuminate\Validation\ValidationException $e) { DB::rollBack(); return response()->json([ 'success' => false, 'message' => 'Validation error', 'errors' => $e->errors(), // detail error validasi ], 422); } catch (\Exception $e) { DB::rollBack(); return response()->json([ 'success' => false, 'message' => 'Registration failed', 'error' => $e->getMessage(), // optional, bisa di-hide kalau production ], 500); } } public function feedback(Request $request) { return response()->json([ 'message' => 'Halo student, ini feedback testing!', 'user' => $request->user()->only(['id', 'name', 'email', 'role']), ]); } private function syncTopicSubscribe(User $user): ?string { $user->loadMissing('student.class'); if (!$user->student) { return null; } $topics = ['student_notifications']; if ($topic = $user->student->class?->fcm_topic) { $topics[] = $topic; } $topics = array_unique(array_filter($topics)); if (empty($topics)) { return null; } $topicSubscribe = implode(', ', $topics); if ($user->topic_subscribe !== $topicSubscribe) { $user->update(['topic_subscribe' => $topicSubscribe]); } $user->topic_subscribe = $topicSubscribe; return $topicSubscribe; } // get student notification list data from notification table. // note filter: user_id = auth user id, or his class id. public function getStudentNotifications(Request $request) { $user = $request->user(); $student = $user->student; if (!$user || !$student) { return response()->json(['success' => false, 'message' => 'Unauthorized'], 401); } // validate query parameters $request->validate([ 'start_date' => 'required|date', 'end_date' => 'required|date', ]); $classId = $user->student->id_class; $startDate = $request->input('start_date'); $endDate = Carbon::parse($request->input('end_date'))->endOfDay(); // filter by class id and date range $notifications = DB::table('notifications') ->where(function ($query) use ($user, $classId) { $query->where('receiver_id', $user->id) ->orWhere('class_id', $classId) ->orWhere(function ($q) { $q->whereNull('receiver_id') ->whereNull('class_id'); }); }) ->whereBetween('created_at', [$startDate, $endDate]) ->orderBy('created_at', 'desc') ->get(); return response()->json([ 'success' => true, 'message' => 'Notifications fetched successfully', 'data' => $notifications, ], 200); } // get activity teacher history from notification table with related sender_id with the user. public function getTeacherActivity(Request $request) { $user = $request->user(); if (!$user || !$user->teacher) { return response()->json(['success' => false, 'message' => 'Unauthorized'], 401); } // validate query parameters $request->validate([ 'start_date' => 'required|date', 'end_date' => 'required|date', ]); $startDate = $request->input('start_date'); $endDate = Carbon::parse($request->input('end_date'))->endOfDay(); // filter by sender_id and date range $activities = DB::table('notifications') ->where('sender_id', $user->id) ->whereBetween('created_at', [$startDate, $endDate]) ->orderBy('created_at', 'desc') ->get(); return response()->json([ 'success' => true, 'message' => 'Activities fetched successfully', 'data' => $activities, ], 200); } // update profile user, for all (teacher, student, admin) public function updateProfilePhoto(Request $request) { // auth check $user = $request->user(); if (!$user) { return response()->json(['success' => false, 'message' => 'Unauthorized'], 401); } // check user role for separated storage path, for example: profile_pictures/teacher, profile_pictures/student, etc. $rolePath = match ($user->role) { 'teacher' => 'teacher', 'student' => 'student', 'admin' => 'admin', default => 'others', }; // input file check using request validation // nullable|file|mimes:jpg,jpeg,png|max:2048', $request->validate([ 'profile_picture' => 'required|file|mimes:jpg,jpeg,png|max:2048', ]); try { $file = $request->file('profile_picture'); // store to storage:disk public, with unique name $fileName = 'profile_' . $user->id . '_' . time() . '.' . $file->getClientOriginalExtension(); Storage::disk('public')->putFileAs('profile_pictures/' . $rolePath, $file, $fileName); // update user profile picture path $user->update(['profile_picture' => $fileName]); return response()->json([ 'success' => true, 'message' => 'Profile picture updated successfully', 'data' => [ 'profile_picture' => $fileName, 'profile_picture_url' => asset('storage/profile_pictures/' . $rolePath . '/' . $fileName), ] ], 200); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'Failed to update profile picture', 'error' => $e->getMessage(), ], 500); } } }