count(); $totalRekomendasi = Recommendation::count(); $totalChatHistory = ChatHistory::count(); $totalJurusan = PolijeMajor::count(); $recentStudents = User::where('role', 'siswa') ->orderBy('created_at', 'desc') ->take(5) ->get(); $recentRecommendations = Recommendation::with('user') ->orderBy('created_at', 'desc') ->take(5) ->get(); $kelompokStats = User::where('role', 'siswa') ->selectRaw('kelompok_asal, COUNT(*) as count') ->groupBy('kelompok_asal') ->get(); // Rekomendasi per kelompok $rekomendasiPerKelompok = Recommendation::selectRaw( 'users.kelompok_asal, COUNT(*) as count' ) ->join('users', 'rekomendasi.user_id', '=', 'users.id') ->groupBy('users.kelompok_asal') ->get(); $topMajors = Recommendation::selectRaw(" JSON_EXTRACT(hasil_rekomendasi, '$[0].jurusan') as major_name, COUNT(*) as count ") ->groupByRaw("JSON_EXTRACT(hasil_rekomendasi, '$[0].jurusan')") ->whereRaw("JSON_EXTRACT(hasil_rekomendasi, '\$[0].jurusan') IS NOT NULL") ->whereRaw("JSON_EXTRACT(hasil_rekomendasi, '\$[0].jurusan') != 'null'") ->orderBy('count', 'desc') ->take(5) ->get(); // Data untuk chart - semua jurusan (filter out NULL values) $allMajorsChart = Recommendation::selectRaw(" JSON_EXTRACT(hasil_rekomendasi, '\$[0].jurusan') as major_name, COUNT(*) as count ") ->groupByRaw("JSON_EXTRACT(hasil_rekomendasi, '\$[0].jurusan')") ->whereRaw("JSON_EXTRACT(hasil_rekomendasi, '\$[0].jurusan') IS NOT NULL") ->whereRaw("JSON_EXTRACT(hasil_rekomendasi, '\$[0].jurusan') != 'null'") ->orderBy('count', 'desc') ->get(); // Persiapkan data untuk Chart.js - aggregate & fix major names $majorData = []; foreach ($allMajorsChart as $item) { $name = trim($item->major_name, '" '); // Skip empty/null values if (empty($name) || $name === 'null') { continue; } // Normalize: handle all variants $normalizedName = $name; if (stripos($name, 'Teknik Informatika') === 0 || stripos($name, 'Teknologi Informasi') === 0) { $normalizedName = 'Teknologi Informasi'; } if (!isset($majorData[$normalizedName])) { $majorData[$normalizedName] = 0; } $majorData[$normalizedName] += (int)$item->count; } // Sort by count descending arsort($majorData); $chartMajorNames = array_keys($majorData); $chartMajorCounts = array_values($majorData); $chartKelompokNames = $kelompokStats->pluck('kelompok_asal')->toArray(); $chartKelompokCounts = $kelompokStats->pluck('count')->toArray(); // Top majors untuk horizontal bar chart - aggregate & fix $topMajorData = []; foreach ($topMajors as $item) { $name = trim($item->major_name, '" '); // Skip empty/null values if (empty($name) || $name === 'null') { continue; } // Normalize: handle all variants $normalizedName = $name; if (stripos($name, 'Teknik Informatika') === 0 || stripos($name, 'Teknologi Informasi') === 0) { $normalizedName = 'Teknologi Informasi'; } if (!isset($topMajorData[$normalizedName])) { $topMajorData[$normalizedName] = 0; } $topMajorData[$normalizedName] += (int)$item->count; } arsort($topMajorData); $topMajorsChart = array_keys($topMajorData); $topMajorsCounts = array_values($topMajorData); return view('admin.dashboard', compact( 'totalSiswa', 'totalRekomendasi', 'totalChatHistory', 'totalJurusan', 'recentStudents', 'recentRecommendations', 'kelompokStats', 'topMajors', 'chartMajorNames', 'chartMajorCounts', 'chartKelompokNames', 'chartKelompokCounts', 'topMajorsChart', 'topMajorsCounts', 'rekomendasiPerKelompok' )); } // ============================================ // 2. MANAJEMEN DATA SISWA // ============================================ public function students(Request $request) { $query = User::where('role', 'siswa') ->withCount('recommendations', 'chatHistories'); if ($request->filled('search')) { $search = $request->search; $query->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhere('nis', 'like', "%{$search}%"); }); } if ($request->filled('kelompok')) { $query->where('kelompok_asal', $request->kelompok); } $students = $query->orderBy('created_at', 'desc')->paginate(20); return view('admin.students.index', compact('students')); } public function studentDetail($id) { $student = User::where('role', 'siswa')->findOrFail($id); $recommendations = Recommendation::where('user_id', $id) ->orderBy('created_at', 'desc') ->get(); $chatHistories = ChatHistory::where('user_id', $id) ->orderBy('created_at', 'desc') ->get(); return view('admin.students.detail', compact('student', 'recommendations', 'chatHistories')); } public function chatHistory($id) { $user = User::findOrFail($id); $chatHistories = ChatHistory::where('user_id', $id) ->orderBy('created_at', 'asc') ->get(); return view('admin.chat-history', compact('user', 'chatHistories')); } // ============================================ // 3. MANAJEMEN JURUSAN (CRUD dari database) // ============================================ public function jurusan() { $jurusanList = PolijeMajor::orderBy('nama_jurusan')->get(); return view('admin.jurusan.index', compact('jurusanList')); } public function jurusanCreate() { return view('admin.jurusan.create'); } public function jurusanStore(Request $request) { $request->validate([ 'nama_jurusan' => 'required|string|min:3|max:255|unique:jurusan_polije,nama_jurusan', 'deskripsi' => 'nullable|string|max:10000', 'keywords' => 'nullable|string', 'preferensi_studi' => 'nullable|string', 'prospek_kerja' => 'nullable|string|max:1000', 'bobot_mapel' => 'nullable|array', 'bobot_mapel.ipa' => 'nullable|array', 'bobot_mapel.ipa.mtk' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ipa.fisika' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ipa.kimia' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ipa.biologi' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ips' => 'nullable|array', 'bobot_mapel.ips.ekonomi' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ips.geografi' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ips.sosiologi' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ips.sejarah' => 'nullable|numeric|min:0|max:1', ]); PolijeMajor::create([ 'nama_jurusan' => $request->nama_jurusan, 'deskripsi' => $request->deskripsi, 'keywords' => $this->parseTagInput($request->keywords), 'preferensi_studi' => $this->parseTagInput($request->preferensi_studi), 'prospek_kerja' => $request->prospek_kerja, 'bobot_mapel' => $this->parseBobotMapel($request), ]); return redirect()->route('admin.jurusan')->with('success', 'Jurusan berhasil ditambahkan!'); } public function jurusanEdit($id) { $jurusan = PolijeMajor::findOrFail($id); return view('admin.jurusan.edit', compact('jurusan')); } public function jurusanUpdate(Request $request, $id) { $jurusan = PolijeMajor::findOrFail($id); $request->validate([ 'nama_jurusan' => ['required', 'string', 'min:3', 'max:255', Rule::unique('jurusan_polije', 'nama_jurusan')->ignore($jurusan->id)], 'deskripsi' => 'nullable|string|max:10000', 'keywords' => 'nullable|string', 'preferensi_studi' => 'nullable|string', 'prospek_kerja' => 'nullable|string|max:1000', 'bobot_mapel' => 'nullable|array', 'bobot_mapel.ipa' => 'nullable|array', 'bobot_mapel.ipa.mtk' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ipa.fisika' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ipa.kimia' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ipa.biologi' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ips' => 'nullable|array', 'bobot_mapel.ips.ekonomi' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ips.geografi' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ips.sosiologi' => 'nullable|numeric|min:0|max:1', 'bobot_mapel.ips.sejarah' => 'nullable|numeric|min:0|max:1', ]); $jurusan->update([ 'nama_jurusan' => $request->nama_jurusan, 'deskripsi' => $request->deskripsi, 'keywords' => $this->parseTagInput($request->keywords), 'preferensi_studi' => $this->parseTagInput($request->preferensi_studi), 'prospek_kerja' => $request->prospek_kerja, 'bobot_mapel' => $this->parseBobotMapel($request), ]); return redirect()->route('admin.jurusan')->with('success', 'Jurusan berhasil diperbarui!'); } public function jurusanDestroy($id) { $jurusan = PolijeMajor::findOrFail($id); $jurusan->delete(); return redirect()->route('admin.jurusan')->with('success', 'Jurusan berhasil dihapus!'); } /** * Parse comma-separated tag input into array */ private function parseTagInput(?string $input): array { if (empty($input)) { return []; } return array_values(array_filter(array_map('trim', explode(',', $input)))); } /** * Parse bobot mapel from request into structured array */ private function parseBobotMapel(Request $request): array { $ipaSubjects = ['mtk', 'fisika', 'kimia', 'biologi']; $ipsSubjects = ['ekonomi', 'geografi', 'sosiologi', 'sejarah']; $ipaInput = $request->input('bobot_mapel.ipa'); $ipsInput = $request->input('bobot_mapel.ips'); if (is_array($ipaInput) || is_array($ipsInput)) { return [ 'ipa' => $this->normalizeBobotGroup(is_array($ipaInput) ? $ipaInput : [], $ipaSubjects), 'ips' => $this->normalizeBobotGroup(is_array($ipsInput) ? $ipsInput : [], $ipsSubjects), ]; } return [ 'ipa' => $this->normalizeBobotGroup([ 'mtk' => $request->input('bobot_mtk'), 'fisika' => $request->input('bobot_fisika'), 'kimia' => $request->input('bobot_kimia'), 'biologi' => $request->input('bobot_biologi'), ], $ipaSubjects), 'ips' => $this->normalizeBobotGroup([ 'ekonomi' => $request->input('bobot_ekonomi'), 'geografi' => $request->input('bobot_geografi'), 'sosiologi' => $request->input('bobot_sosiologi'), 'sejarah' => $request->input('bobot_sejarah'), ], $ipsSubjects), ]; } private function normalizeBobotGroup(array $values, array $subjects): array { $normalized = []; foreach ($subjects as $subject) { $value = $values[$subject] ?? null; $normalized[$subject] = is_numeric($value) ? (float) $value : 0.0; } return $normalized; } // ============================================ // 4. MANAJEMEN AKUN GURU BK // ============================================ public function guruBK() { $guruBK = User::where('role', 'bk')->orderBy('created_at', 'desc')->paginate(20); return view('admin.guru-bk.index', compact('guruBK')); } public function guruBKCreate() { return view('admin.guru-bk.create'); } public function guruBKStore(Request $request) { $request->validate([ 'name' => 'required|string|min:3|max:255', 'email' => 'required|email|unique:users,email', 'password' => 'required|string|min:8|confirmed', ]); User::create([ 'name' => $request->name, 'email' => $request->email, 'password' => Hash::make($request->password), 'role' => 'bk', ]); return redirect()->route('admin.guru-bk')->with('success', 'Akun Guru BK berhasil dibuat!'); } public function guruBKEdit($id) { $guruBK = User::where('role', 'bk')->findOrFail($id); return view('admin.guru-bk.edit', compact('guruBK')); } public function guruBKUpdate(Request $request, $id) { $guruBK = User::where('role', 'bk')->findOrFail($id); $request->validate([ 'name' => 'required|string|min:3|max:255', 'email' => ['required', 'email', Rule::unique('users')->ignore($guruBK->id)], 'password' => 'nullable|string|min:8|confirmed', ]); $guruBK->name = $request->name; $guruBK->email = $request->email; if ($request->filled('password')) { $guruBK->password = Hash::make($request->password); } $guruBK->save(); return redirect()->route('admin.guru-bk')->with('success', 'Akun Guru BK berhasil diperbarui!'); } public function guruBKDestroy($id) { $guruBK = User::where('role', 'bk')->findOrFail($id); $guruBK->delete(); return redirect()->route('admin.guru-bk')->with('success', 'Akun Guru BK berhasil dihapus!'); } // ============================================ // 5. RIWAYAT REKOMENDASI SISWA // ============================================ public function riwayatRekomendasi(Request $request) { $query = Recommendation::with('user'); if ($request->filled('search')) { $search = $request->search; $query->whereHas('user', function ($q) use ($search) { $q->where('name', 'like', "%{$search}%"); }); } $recommendations = $query->orderBy('created_at', 'desc')->paginate(20); $uniqueStudents = Recommendation::distinct('user_id')->count('user_id'); // Find top major $topMajorRow = Recommendation::selectRaw(" JSON_EXTRACT(hasil_rekomendasi, '$[0].jurusan') as major_name, COUNT(*) as count ") ->groupByRaw("JSON_EXTRACT(hasil_rekomendasi, '$[0].jurusan')") ->orderBy('count', 'desc') ->first(); $topMajor = $topMajorRow ? trim($topMajorRow->major_name, '"') : null; return view('admin.riwayat-rekomendasi.index', compact('recommendations', 'uniqueStudents', 'topMajor')); } // ============================================ // 6. RIWAYAT KONSULTASI CHATBOT // ============================================ public function riwayatChatbot(Request $request) { $query = ChatHistory::with('user'); if ($request->filled('search')) { $search = $request->search; $query->where(function ($q) use ($search) { $q->where('pertanyaan', 'like', "%{$search}%") ->orWhere('jawaban', 'like', "%{$search}%") ->orWhereHas('user', function ($q2) use ($search) { $q2->where('name', 'like', "%{$search}%"); }); }); } $chatHistories = $query->orderBy('created_at', 'desc')->paginate(20); $uniqueStudents = ChatHistory::distinct('user_id')->count('user_id'); $todayCount = ChatHistory::whereDate('created_at', today())->count(); return view('admin.riwayat-chatbot.index', compact('chatHistories', 'uniqueStudents', 'todayCount')); } // ============================================ // 7. PROFIL ADMIN // ============================================ public function profil() { $admin = Auth::user(); return view('admin.profil.index', compact('admin')); } public function updateProfil(Request $request) { $admin = Auth::user(); $request->validate([ 'name' => 'required|string|min:3|max:255', 'email' => ['required', 'email', Rule::unique('users')->ignore($admin->id)], ]); $admin->name = $request->name; $admin->email = $request->email; $admin->save(); return redirect()->route('admin.profil')->with('success', 'Profil berhasil diperbarui!'); } public function updatePassword(Request $request) { $request->validate([ 'current_password' => 'required', 'password' => 'required|string|min:8|confirmed', ]); $admin = Auth::user(); if (!Hash::check($request->current_password, $admin->password)) { return back()->withErrors(['current_password' => 'Password lama salah.']); } $admin->password = Hash::make($request->password); $admin->save(); return redirect()->route('admin.profil')->with('success', 'Password berhasil diubah!'); } // ============================================ // 8. LOGOUT SEMUA USER // ============================================ public function logoutAllUsers() { // Logout semua session user \DB::table('sessions')->truncate(); Auth::logout(); return redirect()->route('login')->with('success', 'Semua user telah berhasil logout!'); } }