diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php new file mode 100644 index 0000000..e616845 --- /dev/null +++ b/app/Http/Controllers/AdminController.php @@ -0,0 +1,399 @@ +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(); + + $topMajors = Recommendation::selectRaw(" + JSON_EXTRACT(hasil_rekomendasi, '$[0].jurusan') as major_name, + COUNT(*) as count + ") + ->groupByRaw("JSON_EXTRACT(hasil_rekomendasi, '$[0].jurusan')") + ->orderBy('count', 'desc') + ->take(5) + ->get(); + + return view('admin.dashboard', compact( + 'totalSiswa', + 'totalRekomendasi', + 'totalChatHistory', + 'totalJurusan', + 'recentStudents', + 'recentRecommendations', + 'kelompokStats', + 'topMajors' + )); + } + + // ============================================ + // 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::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|max:255|unique:polije_majors,nama_jurusan', + 'deskripsi' => 'nullable|string|max:1000', + 'keywords' => 'nullable|string', + 'preferensi_studi' => 'nullable|string', + 'prospek_kerja' => 'nullable|string|max:1000', + 'bobot_mtk' => 'nullable|numeric|min:0|max:1', + 'bobot_fisika' => 'nullable|numeric|min:0|max:1', + 'bobot_kimia' => 'nullable|numeric|min:0|max:1', + 'bobot_biologi' => 'nullable|numeric|min:0|max:1', + 'bobot_ekonomi' => 'nullable|numeric|min:0|max:1', + 'bobot_geografi' => 'nullable|numeric|min:0|max:1', + 'bobot_sosiologi' => 'nullable|numeric|min:0|max:1', + 'bobot_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', 'max:255', Rule::unique('polije_majors', 'nama_jurusan')->ignore($jurusan->id)], + 'deskripsi' => 'nullable|string|max:1000', + 'keywords' => 'nullable|string', + 'preferensi_studi' => 'nullable|string', + 'prospek_kerja' => 'nullable|string|max:1000', + 'bobot_mtk' => 'nullable|numeric|min:0|max:1', + 'bobot_fisika' => 'nullable|numeric|min:0|max:1', + 'bobot_kimia' => 'nullable|numeric|min:0|max:1', + 'bobot_biologi' => 'nullable|numeric|min:0|max:1', + 'bobot_ekonomi' => 'nullable|numeric|min:0|max:1', + 'bobot_geografi' => 'nullable|numeric|min:0|max:1', + 'bobot_sosiologi' => 'nullable|numeric|min:0|max:1', + 'bobot_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 + { + $mapelList = ['mtk', 'fisika', 'kimia', 'biologi', 'ekonomi', 'geografi', 'sosiologi', 'sejarah']; + $bobot = []; + + foreach ($mapelList as $mapel) { + $value = $request->input("bobot_{$mapel}"); + if (!is_null($value) && $value !== '') { + $bobot[$mapel] = floatval($value); + } + } + + return $bobot; + } + + // ============================================ + // 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|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|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('prompt', 'like', "%{$search}%") + ->orWhere('response', '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|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!'); + } + +} diff --git a/app/Http/Controllers/AlumniController.php b/app/Http/Controllers/AlumniController.php new file mode 100644 index 0000000..c6941e5 --- /dev/null +++ b/app/Http/Controllers/AlumniController.php @@ -0,0 +1,187 @@ +paginate(20); + $summary = $this->getAlumniSummary(); + + return view('alumni.index', compact('alumni', 'summary')); + } + + /** + * Show form to input/create new alumni + */ + public function create() + { + return view('admin.alumni.create'); + } + + /** + * Store new alumni data + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'nama_alumni' => 'required|string|max:255', + 'nis' => 'nullable|string|max:20', + 'kelompok_asal' => 'required|in:IPA,IPS', + + // Nilai + 'mtk' => 'nullable|numeric|min:0|max:100', + 'fisika' => 'nullable|numeric|min:0|max:100', + 'kimia' => 'nullable|numeric|min:0|max:100', + 'biologi' => 'nullable|numeric|min:0|max:100', + 'ekonomi' => 'nullable|numeric|min:0|max:100', + 'geografi' => 'nullable|numeric|min:0|max:100', + 'sosiologi' => 'nullable|numeric|min:0|max:100', + 'sejarah' => 'nullable|numeric|min:0|max:100', + + // Non-akademik + 'minat' => 'nullable|string|max:255', + 'cita_cita' => 'nullable|string|max:255', + 'preferensi_studi' => 'nullable|in:Praktik_Langsung,DuDi,Project_Based,Blended', + 'prestasi' => 'nullable|string|max:255', + + // Major & Outcome + 'major_masuk' => 'required|string|max:255', + 'ranking_saat_rekomendasi' => 'nullable|integer|min:1|max:9', + 'success_status' => 'nullable|in:sangat_sukses,sukses,cukup,kurang_sukses', + 'catatan' => 'nullable|string|max:500', + ]); + + Alumni::create($validated); + + return redirect()->route('admin.alumni.index')->with('success', 'Alumni berhasil ditambahkan'); + } + + /** + * Show alumni detail + */ + public function show(Alumni $alumnus) + { + return view('alumni.show', compact('alumnus')); + } + + /** + * Show form to edit alumni + */ + public function edit(Alumni $alumni) + { + return view('admin.alumni.edit', compact('alumni')); + } + + /** + * Update alumni data + */ + public function update(Request $request, Alumni $alumni) + { + $validated = $request->validate([ + 'nama_alumni' => 'required|string|max:255', + 'nis' => 'nullable|string|max:20', + 'kelompok_asal' => 'required|in:IPA,IPS', + + 'mtk' => 'nullable|numeric|min:0|max:100', + 'fisika' => 'nullable|numeric|min:0|max:100', + 'kimia' => 'nullable|numeric|min:0|max:100', + 'biologi' => 'nullable|numeric|min:0|max:100', + 'ekonomi' => 'nullable|numeric|min:0|max:100', + 'geografi' => 'nullable|numeric|min:0|max:100', + 'sosiologi' => 'nullable|numeric|min:0|max:100', + 'sejarah' => 'nullable|numeric|min:0|max:100', + + 'minat' => 'nullable|string|max:255', + 'cita_cita' => 'nullable|string|max:255', + 'preferensi_studi' => 'nullable|in:Praktik_Langsung,DuDi,Project_Based,Blended', + 'prestasi' => 'nullable|string|max:255', + + 'major_masuk' => 'required|string|max:255', + 'ranking_saat_rekomendasi' => 'nullable|integer|min:1|max:9', + 'success_status' => 'nullable|in:sangat_sukses,sukses,cukup,kurang_sukses', + 'catatan' => 'nullable|string|max:500', + ]); + + $alumni->update($validated); + + return redirect()->route('admin.alumni.index')->with('success', 'Alumni berhasil diupdate'); + } + + /** + * Delete alumni + */ + public function destroy(Alumni $alumni) + { + $alumni->delete(); + return redirect()->route('admin.alumni.index')->with('success', 'Alumni berhasil dihapus'); + } + + /** + * Get summary analytics untuk alumni + */ + private function getAlumniSummary() + { + $totalAlumni = Alumni::count(); + + $byMajor = Alumni::selectRaw('major_masuk, COUNT(*) as count') + ->groupBy('major_masuk') + ->get(); + + $bySuccess = Alumni::selectRaw('success_status, COUNT(*) as count') + ->groupBy('success_status') + ->get(); + + $prediction_accuracy = $this->calculatePredictionAccuracy(); + + return [ + 'total' => $totalAlumni, + 'by_major' => $byMajor, + 'by_success' => $bySuccess, + 'prediction_accuracy' => $prediction_accuracy, + ]; + } + + /** + * Calculate how accurate was our algorithm prediction + * vs actual major the alumni entered + */ + private function calculatePredictionAccuracy() + { + $alumni = Alumni::whereNotNull('ranking_saat_rekomendasi')->get(); + + if ($alumni->isEmpty()) { + return null; + } + + $correctTop1 = 0; + $correctTop3 = 0; + $correctTop5 = 0; + + foreach ($alumni as $a) { + if ($a->ranking_saat_rekomendasi == 1) { + $correctTop1++; + } + if ($a->ranking_saat_rekomendasi <= 3) { + $correctTop3++; + } + if ($a->ranking_saat_rekomendasi <= 5) { + $correctTop5++; + } + } + + return [ + 'top_1' => round(($correctTop1 / count($alumni)) * 100, 2), + 'top_3' => round(($correctTop3 / count($alumni)) * 100, 2), + 'top_5' => round(($correctTop5 / count($alumni)) * 100, 2), + 'total_alumni_analyzed' => count($alumni), + ]; + } +} diff --git a/app/Http/Controllers/RekomendasiController.php b/app/Http/Controllers/RekomendasiController.php index 71b1bf2..cdb4432 100644 --- a/app/Http/Controllers/RekomendasiController.php +++ b/app/Http/Controllers/RekomendasiController.php @@ -30,117 +30,123 @@ public function index() public function proses(Request $request) { - // --- 1. PREPROCESSING NILAI (Kriteria 1: Akademik) --- - $scores = $request->only(['mtk', 'fisika', 'kimia', 'biologi', 'ekonomi', 'geografi', 'sosiologi', 'sejarah']); - $validScores = array_filter($scores); - $average = count($validScores) > 0 ? array_sum($validScores) / count($validScores) : 0; + // --- VALIDATION --- + // Tentukan kelompok asal siswa + $user = Auth::user(); + $kelompok = $user->kelompok_asal ?? 'IPS'; - // Kategorisasi Nilai berdasarkan config - $nilaiCategories = config('polije.nilai_category', []); - $katNilai = 'Rendah'; - foreach ($nilaiCategories as $category => $range) { - if ($average >= $range['min'] && $average <= $range['max']) { - $katNilai = $category; - break; - } - } + // Validasi berbeda untuk IPA dan IPS + $baseRules = [ + 'minat' => 'required|string|max:255', + 'cita_cita' => 'required|string|max:255', + 'pref_studi' => 'required|in:Sains & Teknologi,Pertanian & Lingkungan,Kesehatan & Ilmu Hayat,Bisnis & Manajemen,Sosial & Humaniora', + 'prestasi' => 'nullable|string|max:255', + ]; - // --- 2. ANALISIS MINAT (Kriteria 2) --- - $minatRaw = strtolower($request->minat ?? ''); - $minatMapped = $this->mapMinat($minatRaw); - - // --- 3. ANALISIS CITA-CITA (Kriteria 3) --- - $citaRaw = strtolower($request->cita_cita ?? ''); - $citaMapped = $this->mapCitaCita($citaRaw); - - // --- 4. PEMETAAN PREFERENSI STUDI (Kriteria 4) --- - $prefStudi = $request->pref_studi ?? 'Blended'; - $prefMapping = config('polije.pref_mapping', []); - - // --- 5. ANALISIS PRESTASI (Kriteria 5) --- - $prestasiRaw = strtolower($request->prestasi ?? ''); - $prestasiScore = $this->scorePrestasiScore($prestasiRaw); - - // --- 6. PERHITUNGAN NAIVE BAYES BERBOBOT --- - $cfg = config('polije.criteria', []); - $logPosteriors = []; - $epsilon = 1e-9; - - foreach ($cfg as $jurusan => $c) { - // Prior: uniform - $prior = 1 / count($cfg); - $logPrior = log(max($prior, $epsilon)); - - // Weights dan match probabilities - $weights = $c['weights'] ?? ['nilai' => 0.40, 'minat' => 0.35, 'pref' => 0.15, 'prestasi' => 0.05, 'cita_cita' => 0.05]; - $matchProb = $c['match_prob'] ?? ['nilai' => 0.80, 'minat' => 0.90, 'pref' => 0.85, 'prestasi' => 0.65, 'cita_cita' => 0.85]; - - // 1. Likelihood untuk Nilai - $p_nilai = ($katNilai == ($c['nilai'] ?? 'Sedang')) ? $matchProb['nilai'] : max(1 - $matchProb['nilai'], $epsilon); - - // 2. Likelihood untuk Minat - $p_minat = ($minatMapped == ($c['minat'] ?? 'Umum')) ? $matchProb['minat'] : max(1 - $matchProb['minat'], $epsilon); - - // 3. Likelihood untuk Preferensi Studi - $prefList = $c['pref'] ?? ['Praktik Langsung', 'DuDi', 'Project Based']; - if (!is_array($prefList)) { - $prefList = [$prefList]; - } - $p_pref = in_array($prefStudi, $prefList) ? $matchProb['pref'] : max(1 - $matchProb['pref'], $epsilon); - - // 4. Likelihood untuk Cita-cita - $citaCitaKeywords = $c['cita_cita_keywords'] ?? []; - $matchCitaCita = false; - if (!empty($citaCitaKeywords)) { - foreach ($citaCitaKeywords as $keyword) { - if (stripos($citaMapped, $keyword) !== false) { - $matchCitaCita = true; - break; - } - } - } - $p_cita_cita = $matchCitaCita ? $matchProb['cita_cita'] : max(1 - $matchProb['cita_cita'], $epsilon); - - // 5. Likelihood untuk Prestasi (boost jika ada prestasi) - $p_prestasi = ($prestasiScore > 0.5) ? $matchProb['prestasi'] : max(1 - $matchProb['prestasi'], $epsilon); - - // Hitung log-likelihood dengan bobot - $logLikelihood = - ($weights['nilai'] ?? 0) * log(max($p_nilai, $epsilon)) + - ($weights['minat'] ?? 0) * log(max($p_minat, $epsilon)) + - ($weights['pref'] ?? 0) * log(max($p_pref, $epsilon)) + - ($weights['cita_cita'] ?? 0) * log(max($p_cita_cita, $epsilon)) + - ($weights['prestasi'] ?? 0) * log(max($p_prestasi, $epsilon)); - - $logPosteriors[$jurusan] = $logPrior + $logLikelihood; - } - - // Convert log-posteriors ke probabilitas (softmax) - $maxLog = max($logPosteriors); - $expVals = []; - $sumExp = 0.0; - foreach ($logPosteriors as $jurusan => $lv) { - $expVals[$jurusan] = exp($lv - $maxLog); - $sumExp += $expVals[$jurusan]; - } - - $hasilAkhir = []; - foreach ($expVals as $jurusan => $val) { - $prob = $val / max($sumExp, $epsilon); - $hasilAkhir[] = [ - 'jurusan' => $jurusan, - 'skor' => round($prob, 4), - 'kecocokan_nilai' => $katNilai, - 'kecocokan_minat' => $minatMapped, - 'kecocokan_pref' => $prefStudi, + if ($kelompok === 'IPA') { + $nilaiRules = [ + 'mtk' => 'required|numeric|between:0,100', + 'fisika' => 'required|numeric|between:0,100', + 'kimia' => 'required|numeric|between:0,100', + 'biologi' => 'required|numeric|between:0,100', + ]; + } else { + $nilaiRules = [ + 'ekonomi' => 'required|numeric|between:0,100', + 'geografi' => 'required|numeric|between:0,100', + 'sosiologi' => 'required|numeric|between:0,100', + 'sejarah' => 'required|numeric|between:0,100', ]; } - // Sort hasil berdasarkan skor (tertinggi dulu) + $request->validate(array_merge($baseRules, $nilaiRules)); + + // --- 1. SKOR NILAI AKADEMIK (40%) - dikumpulkan dulu, dihitung per jurusan --- + if ($kelompok === 'IPA') { + $scores = $request->only(['mtk', 'fisika', 'kimia', 'biologi']); + } else { + $scores = $request->only(['ekonomi', 'geografi', 'sosiologi', 'sejarah']); + } + $validScores = array_filter($scores, fn($v) => !is_null($v) && $v !== ''); + $average = count($validScores) > 0 ? array_sum($validScores) / count($validScores) : 0; + + // Label nilai untuk tampilan + if ($average >= 85) { + $katNilai = 'Tinggi'; + } elseif ($average >= 70) { + $katNilai = 'Sedang'; + } else { + $katNilai = 'Rendah'; + } + + // --- 2. INPUT SISWA --- + $minatRaw = strtolower(trim($request->minat ?? '')); + $citaRaw = strtolower(trim($request->cita_cita ?? '')); + $prefStudi = $request->pref_studi ?? 'Sains & Teknologi'; + $prestasiRaw = strtolower(trim($request->prestasi ?? '')); + $prestasiScore = $this->scorePrestasiScore($prestasiRaw); + + // --- 3. GRADUATED SCORING PER JURUSAN --- + $jurusanList = PolijeMajor::all(); + $hasilAkhir = []; + + // Bobot kriteria + $W_NILAI = 0.40; + $W_MINAT = 0.35; + $W_PREF = 0.15; + $W_CITA = 0.05; + $W_PRESTASI = 0.05; + + foreach ($jurusanList as $jurusan) { + $keywords = $jurusan->keywords ?? []; + $prefList = $jurusan->preferensi_studi ?? []; + $bobotMapel = $jurusan->bobot_mapel ?? []; + + // --- Skor Nilai: per-jurusan weighted --- + $skorNilai = $this->hitungSkorNilaiPerJurusan($scores, $bobotMapel, $average); + + // --- Skor Minat: partial keyword matching --- + $skorMinat = $this->hitungKecocokanKeyword($minatRaw, $keywords); + + // --- Skor Cita-cita: partial keyword matching --- + $skorCita = $this->hitungKecocokanKeyword($citaRaw, $keywords); + + // --- Skor Preferensi Studi --- + if (in_array($prefStudi, $prefList)) { + $skorPref = 1.0; + } elseif (!empty($prefList)) { + $skorPref = 0.3; // Tidak cocok tapi jurusan punya preferensi + } else { + $skorPref = 0.5; // Jurusan tidak mendefinisikan preferensi + } + + // --- Skor Prestasi (sama untuk semua jurusan) --- + $skorPrestasi = $prestasiScore; + + // --- Hitung skor akhir --- + $skorAkhir = ($W_NILAI * $skorNilai) + + ($W_MINAT * $skorMinat) + + ($W_PREF * $skorPref) + + ($W_CITA * $skorCita) + + ($W_PRESTASI * $skorPrestasi); + + $hasilAkhir[] = [ + 'jurusan' => $jurusan->nama_jurusan, + 'skor' => round($skorAkhir, 4), + 'detail' => [ + 'nilai' => round($skorNilai, 4), + 'minat' => round($skorMinat, 4), + 'pref' => round($skorPref, 4), + 'cita' => round($skorCita, 4), + 'prestasi' => round($skorPrestasi, 4), + ], + ]; + } + + // Sort berdasarkan skor tertinggi usort($hasilAkhir, fn($a, $b) => $b['skor'] <=> $a['skor']); - // Simpan data rekomendasi ke database - $user = Auth::user(); + // Simpan ke database if ($user) { Recommendation::create([ 'user_id' => $user->id, @@ -160,7 +166,7 @@ public function proses(Request $request) ]); } - // Simpan data rekomendasi ke session untuk chatbot + // Simpan ke session untuk chatbot if (count($hasilAkhir) > 0) { $topResult = $hasilAkhir[0]; session([ @@ -168,41 +174,91 @@ public function proses(Request $request) 'jurusan' => $topResult['jurusan'], 'skor' => $topResult['skor'], 'nilai' => $katNilai, - 'minat' => $minatMapped, + 'minat' => $request->minat, 'pref_studi' => $prefStudi, ] ]); } - return view('rekomendasi.hasil', compact('hasilAkhir', 'katNilai', 'minatMapped', 'citaMapped', 'prefStudi', 'prestasiScore')); - } - - /** - * Pemetaan minat ke kategori yang dipahami sistem - */ - private function mapMinat(string $minatRaw): string - { - if (preg_match('/(coding|komputer|laptop|web|aplikasi|logika|programming|software|development)/', $minatRaw)) { - return 'Logika & Komputer'; - } elseif (preg_match('/(tanam|kebun|sawah|hewan|ternak|alam|pertanian|agri)/', $minatRaw)) { - return 'Alam & Tanaman'; - } elseif (preg_match('/(obat|sakit|rawat|medis|gizi|sehat|kesehatan|perawat|dokter)/', $minatRaw)) { - return 'Pelayanan & Kesehatan'; - } elseif (preg_match('/(bisnis|uang|jual|kantor|hitung|ekonomi|dagang|usaha|entrepreneur)/', $minatRaw)) { - return 'Manajemen & Bisnis'; - } elseif (preg_match('/(mesin|bengkel|listrik|las|robot|motor|teknik|otomasi|elektronik)/', $minatRaw)) { - return 'Mesin & Listrik'; + // Load top jurusan from DB for deskripsi & prospek_kerja + $topJurusan = null; + if (count($hasilAkhir) > 0) { + $topJurusan = PolijeMajor::where('nama_jurusan', $hasilAkhir[0]['jurusan'])->first(); } - return 'Umum'; + + return view('rekomendasi.hasil', compact('hasilAkhir', 'katNilai', 'average', 'prefStudi', 'prestasiScore', 'topJurusan')); } /** - * Pemetaan cita-cita ke kategori jurusan + * Hitung skor nilai akademik per jurusan dengan bobot mapel + * Jika jurusan punya bobot_mapel, hitung weighted average + * Jika tidak, gunakan rata-rata biasa */ - private function mapCitaCita(string $citaRaw): string + private function hitungSkorNilaiPerJurusan(array $scores, array $bobotMapel, float $averageFallback): float { - // Return raw mapped text untuk matching dengan keywords - return $citaRaw; + // Jika tidak ada bobot khusus, pakai rata-rata biasa + if (empty($bobotMapel)) { + return min($averageFallback / 100, 1.0); + } + + $weightedSum = 0; + $totalWeight = 0; + + foreach ($bobotMapel as $mapel => $bobot) { + $nilai = floatval($scores[$mapel] ?? 0); + $weightedSum += $nilai * $bobot; + $totalWeight += $bobot; + } + + // Untuk mapel yang ada di scores tapi tidak di bobot, beri bobot kecil + foreach ($scores as $mapel => $nilai) { + if (!isset($bobotMapel[$mapel]) && !is_null($nilai) && $nilai !== '') { + $weightedSum += floatval($nilai) * 0.1; + $totalWeight += 0.1; + } + } + + if ($totalWeight <= 0) { + return min($averageFallback / 100, 1.0); + } + + $weightedAvg = $weightedSum / $totalWeight; + return min($weightedAvg / 100, 1.0); + } + + /** + * Hitung kecocokan teks input dengan array keywords jurusan (graduated) + * Returns 0.0 - 1.0 + */ + private function hitungKecocokanKeyword(string $inputText, array $keywords): float + { + if (empty($keywords) || empty($inputText)) { + return 0.0; + } + + $matchCount = 0; + $inputWords = preg_split('/[\s,;.\/\-]+/', $inputText); + + foreach ($keywords as $keyword) { + $kw = strtolower(trim($keyword)); + if (empty($kw)) continue; + + // Check if keyword appears in any input word (partial match) + foreach ($inputWords as $word) { + if (empty($word)) continue; + // Match if input word contains keyword or keyword contains input word (min 3 chars) + if (stripos($inputText, $kw) !== false || + (strlen($word) >= 3 && stripos($kw, $word) !== false)) { + $matchCount++; + break; + } + } + } + + // Graduated score: ratio of matched keywords + // Use sqrt to give more credit for partial matches + $ratio = $matchCount / count($keywords); + return min(sqrt($ratio) * 0.9 + ($matchCount > 0 ? 0.1 : 0), 1.0); } /** diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index c34cdcf..22c9d07 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -63,5 +63,8 @@ class Kernel extends HttpKernel 'signed' => \App\Http\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + 'isAdmin' => \App\Http\Middleware\IsAdmin::class, + 'isBK' => \App\Http\Middleware\IsBK::class, + 'roleRedirect' => \App\Http\Middleware\RedirectBasedOnRole::class, ]; } diff --git a/app/Http/Middleware/IsAdmin.php b/app/Http/Middleware/IsAdmin.php new file mode 100644 index 0000000..8d88d99 --- /dev/null +++ b/app/Http/Middleware/IsAdmin.php @@ -0,0 +1,24 @@ +check() && auth()->user()->role === 'admin') { + return $next($request); + } + + return redirect('/dashboard')->with('error', 'Anda tidak memiliki akses ke panel admin.'); + } +} diff --git a/app/Http/Middleware/IsBK.php b/app/Http/Middleware/IsBK.php new file mode 100644 index 0000000..b75f336 --- /dev/null +++ b/app/Http/Middleware/IsBK.php @@ -0,0 +1,24 @@ +check() && auth()->user()->role === 'bk') { + return $next($request); + } + + return redirect('/dashboard')->with('error', 'Anda tidak memiliki akses ke panel BK.'); + } +} diff --git a/app/Http/Middleware/RedirectBasedOnRole.php b/app/Http/Middleware/RedirectBasedOnRole.php new file mode 100644 index 0000000..76612c6 --- /dev/null +++ b/app/Http/Middleware/RedirectBasedOnRole.php @@ -0,0 +1,35 @@ +check()) { + $user = auth()->user(); + + // Jika sudah di /dashboard, redirect ke panel yang sesuai + if ($request->path() === 'dashboard') { + if ($user->role === 'admin') { + return redirect('/admin/dashboard'); + } elseif ($user->role === 'bk') { + return redirect('/bk/dashboard'); + } + // siswa tetap di /dashboard + } + } + + return $next($request); + } +} diff --git a/app/Models/Alumni.php b/app/Models/Alumni.php new file mode 100644 index 0000000..b988436 --- /dev/null +++ b/app/Models/Alumni.php @@ -0,0 +1,77 @@ + 'float', + 'fisika' => 'float', + 'kimia' => 'float', + 'biologi' => 'float', + 'ekonomi' => 'float', + 'geografi' => 'float', + 'sosiologi' => 'float', + 'sejarah' => 'float', + 'nilai_rata_rata' => 'float', + 'ipk_lulus' => 'float', + 'predicted_score' => 'float', + ]; + + /** + * Hitung nilai rata-rata otomatis + */ + public static function booted() + { + static::saving(function ($alumni) { + // Gather nilai based on kelompok_asal + $nilaiFields = ['mtk']; + + if ($alumni->kelompok_asal == 'IPA') { + $nilaiFields = ['mtk', 'fisika', 'kimia', 'biologi']; + } else { + $nilaiFields = ['mtk', 'ekonomi', 'geografi', 'sosiologi', 'sejarah']; + } + + $nilaiValues = []; + foreach ($nilaiFields as $field) { + if (!is_null($alumni->$field)) { + $nilaiValues[] = $alumni->$field; + } + } + + $alumni->nilai_rata_rata = count($nilaiValues) > 0 + ? round(array_sum($nilaiValues) / count($nilaiValues), 2) + : null; + }); + } +} diff --git a/app/Models/PolijeMajor.php b/app/Models/PolijeMajor.php index 8d229cf..21d3ae6 100644 --- a/app/Models/PolijeMajor.php +++ b/app/Models/PolijeMajor.php @@ -9,6 +9,18 @@ class PolijeMajor extends Model { use HasFactory; - // Tambahkan baris ini agar data bisa masuk - protected $fillable = ['nama_jurusan', 'deskripsi', 'prospek_kerja']; + protected $fillable = [ + 'nama_jurusan', + 'deskripsi', + 'keywords', + 'preferensi_studi', + 'bobot_mapel', + 'prospek_kerja', + ]; + + protected $casts = [ + 'keywords' => 'array', + 'preferensi_studi' => 'array', + 'bobot_mapel' => 'array', + ]; } \ No newline at end of file diff --git a/app/Services/GeminiService.php b/app/Services/GeminiService.php index 9b1f26a..bd3f62a 100644 --- a/app/Services/GeminiService.php +++ b/app/Services/GeminiService.php @@ -4,6 +4,7 @@ use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; +use App\Models\PolijeMajor; class GeminiService { @@ -124,11 +125,11 @@ protected function getFallbackResponse($message, $context = []) $messageLower = strtolower($message); if (strpos($messageLower, 'halo') !== false || strpos($messageLower, 'hai') !== false || strpos($messageLower, 'hallo') !== false || strpos($messageLower, 'hi') !== false) { - $greeting = "Halo! 👋 Saya konselor BK virtual SMA Bima Ambulu. Saya siap membantu kamu soal pemilihan jurusan kuliah. "; + $greeting = "Selamat datang. Saya adalah konselor BK virtual SMA Bima Ambulu yang siap membantu Anda dalam pemilihan jurusan kuliah. "; if ($hasRecommendation) { - $greeting .= "Saya lihat kamu sudah dapat rekomendasi jurusan \"{$jurusan}\" dengan skor {$score}%. Mau bahas lebih lanjut tentang jurusan itu, atau ada pertanyaan lain?"; + $greeting .= "Berdasarkan data yang tersedia, Anda telah memperoleh rekomendasi jurusan \"{$jurusan}\" dengan skor kesesuaian {$score}%. Apakah Anda ingin membahas lebih lanjut mengenai jurusan tersebut, atau ada pertanyaan lain yang ingin disampaikan?"; } else { - $greeting .= "Kamu bisa tanya apa saja tentang jurusan kuliah, prospek karir, atau tips memilih jurusan yang tepat. Yuk, mulai!"; + $greeting .= "Anda dapat mengajukan pertanyaan seputar jurusan kuliah, prospek karier, maupun panduan dalam memilih jurusan yang tepat. Silakan sampaikan pertanyaan Anda."; } return ['success' => true, 'message' => $greeting]; } @@ -137,12 +138,12 @@ protected function getFallbackResponse($message, $context = []) if ($hasRecommendation) { return [ 'success' => true, - 'message' => "Jurusan \"{$jurusan}\" direkomendasikan berdasarkan analisis profil akademik, minat, dan preferensi belajar kamu. Skor kesesuaian {$score}% menunjukkan tingkat kecocokan yang baik antara profil kamu dengan jurusan tersebut. Sistem menghitung ini dari 5 faktor: nilai akademik, minat, preferensi pembelajaran, prestasi, dan cita-cita." + 'message' => "Jurusan \"{$jurusan}\" direkomendasikan berdasarkan analisis komprehensif terhadap profil akademik, minat, serta preferensi studi Anda. Skor kesesuaian sebesar {$score}% menunjukkan tingkat kecocokan yang signifikan antara profil Anda dengan karakteristik jurusan tersebut. Sistem menghitung skor ini berdasarkan lima faktor utama, yaitu: nilai akademik, minat dan bakat, preferensi studi lanjutan, prestasi, dan cita-cita." ]; } return [ 'success' => true, - 'message' => "Untuk menjawab pertanyaan \"mengapa\", sebaiknya kamu lakukan analisis rekomendasi dulu ya. Dari situ, sistem akan mencocokkan profil kamu dengan 9 jurusan yang tersedia. Kamu bisa klik menu 'Analisis Rekomendasi' di dashboard." + 'message' => "Untuk dapat menjawab pertanyaan tersebut secara akurat, disarankan agar Anda terlebih dahulu melakukan analisis rekomendasi. Melalui proses tersebut, sistem akan mencocokkan profil Anda dengan sembilan jurusan yang tersedia di Polije. Silakan akses menu Analisis Rekomendasi pada halaman dashboard." ]; } @@ -150,19 +151,19 @@ protected function getFallbackResponse($message, $context = []) if ($hasRecommendation) { return [ 'success' => true, - 'message' => "Jurusan \"{$jurusan}\" memiliki prospek karir yang baik. Lulusan dari jurusan ini bisa bekerja di berbagai sektor industri yang relevan. Setiap jurusan di perguruan tinggi menyiapkan lulusannya dengan keahlian praktis yang dibutuhkan dunia kerja. Mau tau lebih detail tentang posisi kerja spesifik?" + 'message' => "Jurusan \"{$jurusan}\" memiliki prospek karier yang menjanjikan. Lulusan dari jurusan ini dapat bekerja di berbagai sektor industri yang relevan dengan bidang keahliannya. Setiap program studi di perguruan tinggi dirancang untuk membekali lulusannya dengan kompetensi praktis yang dibutuhkan oleh dunia kerja. Apakah Anda ingin mengetahui lebih detail mengenai posisi pekerjaan spesifik yang dapat ditempuh?" ]; } return [ 'success' => true, - 'message' => "Setiap jurusan punya prospek karir yang berbeda-beda. Misalnya, Teknologi Informasi bisa jadi programmer/developer, Kesehatan bisa jadi tenaga medis, Bisnis bisa jadi manajer/entrepreneur. Jurusan mana yang kamu tertarik? Saya bisa jelaskan lebih detail." + 'message' => "Setiap jurusan memiliki prospek karier yang berbeda. Sebagai contoh, lulusan Teknologi Informasi dapat berkarier sebagai programmer atau developer, lulusan Kesehatan dapat menjadi tenaga medis profesional, dan lulusan Bisnis dapat menempuh karier di bidang manajerial atau kewirausahaan. Jurusan mana yang ingin Anda ketahui lebih lanjut? Saya akan memberikan informasi yang lebih terperinci." ]; } if (strpos($messageLower, 'bingung') !== false || strpos($messageLower, 'galau') !== false || strpos($messageLower, 'tidak tahu') !== false || strpos($messageLower, 'gak tau') !== false) { return [ 'success' => true, - 'message' => "Wajar kok kalau masih bingung! 😊 Coba jawab pertanyaan ini: 1) Mata pelajaran apa yang paling kamu suka? 2) Kegiatan apa yang bikin kamu semangat? 3) Cita-cita kamu apa? Dari situ kita bisa mulai mencari jurusan yang cocok. Atau kamu juga bisa coba fitur 'Analisis Rekomendasi' di dashboard untuk mendapat rekomendasi otomatis." + 'message' => "Perasaan bingung dalam memilih jurusan adalah hal yang wajar dan dialami oleh banyak siswa. Untuk membantu memperjelas arah pilihan Anda, cobalah menjawab beberapa pertanyaan berikut: (1) Mata pelajaran apa yang paling Anda kuasai atau minati? (2) Kegiatan apa yang membuat Anda bersemangat? (3) Apa cita-cita atau tujuan karier Anda? Dari jawaban tersebut, kita dapat mulai mengidentifikasi jurusan yang sesuai. Selain itu, Anda juga dapat memanfaatkan fitur Analisis Rekomendasi di halaman dashboard untuk mendapatkan rekomendasi secara otomatis." ]; } @@ -170,19 +171,19 @@ protected function getFallbackResponse($message, $context = []) if ($hasRecommendation) { return [ 'success' => true, - 'message' => "Untuk sukses di jurusan \"{$jurusan}\", kamu perlu mengembangkan berbagai skill teknis dan non-teknis. Skill teknis tergantung bidang jurusannya, sedangkan skill umum seperti komunikasi, kerja tim, dan problem solving selalu dibutuhkan di semua jurusan. Mau tau skill spesifik yang perlu disiapkan?" + 'message' => "Untuk berhasil di jurusan \"{$jurusan}\", Anda perlu mengembangkan berbagai kompetensi, baik teknis maupun non-teknis. Kompetensi teknis akan bergantung pada spesifikasi bidang jurusan yang dipilih, sedangkan kompetensi umum seperti kemampuan komunikasi, kerja sama tim, dan pemecahan masalah sangat dibutuhkan di semua bidang studi. Apakah Anda ingin mengetahui kompetensi spesifik yang perlu dipersiapkan?" ]; } return [ 'success' => true, - 'message' => "Setiap jurusan butuh skill yang berbeda. Misalnya: TI butuh logika & coding, Kesehatan butuh ketelitian & empati, Bisnis butuh komunikasi & manajemen. Yang pasti, semua jurusan butuh kemampuan belajar mandiri dan kerja tim. Jurusan mana yang ingin kamu ketahui skill-nya?" + 'message' => "Setiap jurusan memerlukan kompetensi yang berbeda. Sebagai contoh, Teknologi Informasi membutuhkan kemampuan logika dan pemrograman, Kesehatan membutuhkan ketelitian dan empati, sedangkan Bisnis memerlukan kemampuan komunikasi dan manajerial. Secara umum, semua jurusan membutuhkan kemampuan belajar mandiri dan kerja sama tim. Jurusan mana yang ingin Anda ketahui kompetensinya secara lebih mendalam?" ]; } if (strpos($messageLower, 'ipa') !== false || strpos($messageLower, 'ips') !== false) { return [ 'success' => true, - 'message' => "Kelompok IPA dan IPS bukan batasan mutlak untuk memilih jurusan kuliah ya. Banyak jurusan yang bisa dimasuki oleh keduanya. Yang penting adalah minat dan kemampuan kamu. Anak IPA bisa masuk bisnis, anak IPS bisa masuk TI. Lakukan analisis rekomendasi untuk melihat jurusan mana yang paling cocok berdasarkan profil lengkap kamu." + 'message' => "Perlu dipahami bahwa kelompok IPA dan IPS bukan merupakan batasan mutlak dalam memilih jurusan kuliah. Banyak program studi yang dapat dimasuki oleh siswa dari kedua kelompok tersebut. Faktor yang lebih menentukan adalah minat, kemampuan, dan kompetensi yang Anda miliki. Siswa IPA dapat memilih bidang bisnis, dan sebaliknya siswa IPS dapat menempuh bidang teknologi informasi. Silakan manfaatkan fitur Analisis Rekomendasi untuk melihat jurusan yang paling sesuai berdasarkan profil lengkap Anda." ]; } @@ -190,23 +191,24 @@ protected function getFallbackResponse($message, $context = []) if ($hasRecommendation) { return [ 'success' => true, - 'message' => "Saya konselor BK virtual SMA Bima Ambulu. Berdasarkan analisis, jurusan \"{$jurusan}\" cocok untuk kamu dengan skor {$score}%. Kamu bisa tanya tentang: prospek karir, skill yang dibutuhkan, perbandingan jurusan, atau apapun tentang persiapan kuliah. Saya siap membantu! 😊" + 'message' => "Saya adalah konselor BK virtual SMA Bima Ambulu. Berdasarkan hasil analisis, jurusan \"{$jurusan}\" memiliki kesesuaian tertinggi dengan profil Anda, yaitu sebesar {$score}%. Anda dapat berkonsultasi mengenai prospek karier, kompetensi yang dibutuhkan, perbandingan antar jurusan, atau hal lain terkait persiapan pendidikan tinggi. Silakan sampaikan pertanyaan Anda." ]; } return [ 'success' => true, - 'message' => "Saya konselor BK virtual SMA Bima Ambulu, siap membantu kamu memilih jurusan kuliah! 😊 Kamu bisa bertanya tentang: jurusan apa yang cocok, prospek karir, skill yang dibutuhkan, atau tips memilih jurusan. Untuk rekomendasi personal, coba fitur 'Analisis Rekomendasi' di dashboard ya." + 'message' => "Saya adalah konselor BK virtual SMA Bima Ambulu, siap membantu Anda dalam proses pemilihan jurusan kuliah. Anda dapat bertanya seputar kesesuaian jurusan, prospek karier, kompetensi yang dibutuhkan, maupun panduan dalam menentukan pilihan studi. Untuk mendapatkan rekomendasi yang dipersonalisasi, silakan gunakan fitur Analisis Rekomendasi di halaman dashboard." ]; } protected function buildSystemPrompt($context) { - $prompt = "Kamu adalah Pak/Bu Konselor BK (Bimbingan Konseling) di SMA Bima Ambulu. "; - $prompt .= "Kamu adalah guru BK yang HIDUP — bukan robot. "; + $prompt = "Kamu adalah Konselor Bimbingan Konseling (BK) di SMA Bima Ambulu. "; + $prompt .= "Kamu adalah konselor profesional yang memberikan bimbingan secara personal kepada siswa. "; $prompt .= "Kamu MENGARAHKAN siswa, memberikan ANALISIS LOGIS, dan MEYAKINKAN mereka dengan alasan yang masuk akal. "; $prompt .= "Kamu juga bisa menjawab pertanyaan umum di luar topik jurusan (seperti pengetahuan umum, tokoh, dll) secara singkat, lalu arahkan kembali ke topik konseling. "; - $prompt .= "Gunakan bahasa Indonesia santai, hangat, tapi tetap berbobot — seperti guru BK favorit yang ngobrol dengan muridnya. "; + $prompt .= "Gunakan bahasa Indonesia yang FORMAL, AKADEMIK, dan SOPAN — seperti seorang konselor profesional berbicara dengan siswa. "; + $prompt .= "DILARANG menggunakan bahasa gaul, slang, atau terlalu santai. Gunakan kalimat yang baku dan terstruktur. "; // Tambahkan konteks rekomendasi jika ada if (!empty($context['recommendation'])) { @@ -237,10 +239,21 @@ protected function buildSystemPrompt($context) } } - $jurusan = config('polije.criteria', []); - if (!empty($jurusan)) { - $namaJurusan = array_keys($jurusan); - $prompt .= "\n\n9 Jurusan tersedia: " . implode(', ', $namaJurusan) . ". "; + $jurusanList = PolijeMajor::all(); + if ($jurusanList->isNotEmpty()) { + $prompt .= "\n\nDAFTAR JURUSAN POLIJE ({$jurusanList->count()} jurusan):"; + foreach ($jurusanList as $j) { + $prompt .= "\n- {$j->nama_jurusan}"; + if (!empty($j->deskripsi)) { + $prompt .= ": {$j->deskripsi}"; + } + if (!empty($j->prospek_kerja)) { + $prompt .= " Prospek kerja: {$j->prospek_kerja}."; + } + if (!empty($j->keywords) && is_array($j->keywords)) { + $prompt .= " Kata kunci: " . implode(', ', array_slice($j->keywords, 0, 10)) . "."; + } + } } $prompt .= "\n\nCara kamu merespons:"; @@ -252,6 +265,8 @@ protected function buildSystemPrompt($context) $prompt .= "\n6. Jawab RINGKAS (2-3 paragraf). Jangan terlalu panjang kecuali diminta detail."; $prompt .= "\n7. Boleh menjawab pertanyaan di luar topik jurusan secara singkat, lalu kembalikan ke konseling."; $prompt .= "\n8. JANGAN awali setiap respons dengan 'Halo' atau salam — langsung ke inti jawaban (kecuali percakapan baru dimulai)."; + $prompt .= "\n9. DILARANG KERAS menggunakan format markdown seperti **, *, #, ##, atau simbol formatting lainnya. Tulis teks biasa (plain text) saja tanpa formatting markdown."; + $prompt .= "\n10. Gunakan bahasa Indonesia baku dan akademik. Hindari bahasa gaul seperti 'kek', 'banget', 'ngobrol', 'ngomongin', 'gampangnya'. Gunakan padanan formal seperti 'sangat', 'berbincang', 'membahas', 'secara sederhana'."; return $prompt; } diff --git a/config/app.php b/config/app.php index bca112f..6de4729 100644 --- a/config/app.php +++ b/config/app.php @@ -82,7 +82,7 @@ | */ - 'locale' => 'en', + 'locale' => 'id', /* |-------------------------------------------------------------------------- @@ -95,7 +95,7 @@ | */ - 'fallback_locale' => 'en', + 'fallback_locale' => 'id', /* |-------------------------------------------------------------------------- @@ -108,7 +108,7 @@ | */ - 'faker_locale' => 'en_US', + 'faker_locale' => 'id_ID', /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2026_02_12_add_bk_role_to_users.php b/database/migrations/2026_02_12_add_bk_role_to_users.php new file mode 100644 index 0000000..a03dae4 --- /dev/null +++ b/database/migrations/2026_02_12_add_bk_role_to_users.php @@ -0,0 +1,29 @@ +enum('role', ['admin', 'guru', 'bk', 'siswa'])->default('siswa')->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->enum('role', ['admin', 'guru', 'siswa'])->default('siswa')->change(); + }); + } +}; diff --git a/database/migrations/2026_02_12_create_alumni_table.php b/database/migrations/2026_02_12_create_alumni_table.php new file mode 100644 index 0000000..e7ed990 --- /dev/null +++ b/database/migrations/2026_02_12_create_alumni_table.php @@ -0,0 +1,62 @@ +id(); + $table->string('nama_alumni'); + $table->string('nis')->nullable(); + $table->enum('kelompok_asal', ['IPA', 'IPS']); + $table->year('tahun_masuk'); + + // === NILAI SAAT ENTRY === + $table->float('mtk')->nullable(); + $table->float('fisika')->nullable(); + $table->float('kimia')->nullable(); + $table->float('biologi')->nullable(); + $table->float('ekonomi')->nullable(); + $table->float('geografi')->nullable(); + $table->float('sosiologi')->nullable(); + $table->float('sejarah')->nullable(); + $table->float('nilai_rata_rata')->nullable(); // auto-calculated + + // === VARIABEL NON-AKADEMIK SAAT ENTRY === + $table->string('minat')->nullable(); + $table->string('cita_cita')->nullable(); + $table->string('preferensi_studi')->nullable(); // Praktik Langsung, DuDi, Project Based, Blended + $table->string('prestasi')->nullable(); + + // === MAJOR & PREDICTION === + $table->string('major_masuk'); + $table->integer('ranking_saat_rekomendasi')->nullable(); // ranking berapa di list 9 jurusan + $table->float('predicted_score')->nullable(); // score dari algoritma saat itu + + // === OUTCOME & PERFORMANCE === + $table->float('ipk_lulus')->nullable(); // cumulative GPA + $table->year('tahun_lulus')->nullable(); + $table->text('karir_outcome')->nullable(); // deskripsi: jadi apa, kerja dimana + $table->enum('success_status', ['sangat_sukses', 'sukses', 'cukup', 'kurang_sukses'])->nullable(); + $table->text('catatan')->nullable(); + + // === METADATA === + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('alumni'); + } +}; diff --git a/database/migrations/2026_02_12_fix_alumni_table_structure.php b/database/migrations/2026_02_12_fix_alumni_table_structure.php new file mode 100644 index 0000000..9c87461 --- /dev/null +++ b/database/migrations/2026_02_12_fix_alumni_table_structure.php @@ -0,0 +1,61 @@ +id(); + + // === IDENTITAS ALUMNI SMA === + $table->string('nama_alumni'); + $table->string('nis')->nullable()->unique(); // NIS dari SMA Bima Ambulu + $table->enum('kelompok_asal', ['IPA', 'IPS']); + + // === NILAI SAAT SMA (INPUT) === + // IPA: Matematika, Fisika, Kimia, Biologi + $table->float('mtk')->nullable(); + $table->float('fisika')->nullable(); + $table->float('kimia')->nullable(); + $table->float('biologi')->nullable(); + + // IPS: Ekonomi, Geografi, Sosiologi, Sejarah + $table->float('ekonomi')->nullable(); + $table->float('geografi')->nullable(); + $table->float('sosiologi')->nullable(); + $table->float('sejarah')->nullable(); + + $table->float('nilai_rata_rata')->nullable(); // auto-calculated + + // === VARIABEL NON-AKADEMIK SMA (INPUT) === + $table->string('minat')->nullable(); + $table->string('cita_cita')->nullable(); + $table->enum('preferensi_studi', ['Praktik_Langsung', 'DuDi', 'Project_Based', 'Blended'])->nullable(); + $table->text('prestasi')->nullable(); + + // === HASIL KEPUTUSAN MASUK POLIJE (OUTPUT) === + $table->string('major_masuk'); // Jurusan yang dipilih di Polije + $table->integer('ranking_saat_rekomendasi')->nullable(); // Ranking rekomendasi 1-9 + + // === VALIDASI AKURASI EKSTRAKTION === + // Success = ranking rekomendasi cocok dengan pilihan sebenarnya + $table->enum('success_status', ['sangat_sukses', 'sukses', 'cukup', 'kurang_sukses'])->nullable(); + $table->text('catatan')->nullable(); // Notas analyst + + // === METADATA === + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('alumni'); + } +}; diff --git a/database/migrations/2026_02_12_update_alumni_table_structure.php b/database/migrations/2026_02_12_update_alumni_table_structure.php new file mode 100644 index 0000000..ef0ea0f --- /dev/null +++ b/database/migrations/2026_02_12_update_alumni_table_structure.php @@ -0,0 +1,52 @@ +dropColumn('tahun_masuk'); + } + if (Schema::hasColumn('alumni', 'preferensi_studi')) { + $table->dropColumn('preferensi_studi'); + } + if (Schema::hasColumn('alumni', 'tahun_lulus')) { + $table->dropColumn('tahun_lulus'); + } + }); + + Schema::table('alumni', function (Blueprint $table) { + // === INPUT VARIABLES (dari SMA) === + $table->year('tahun_masuk_sma')->nullable()->after('kelompok_asal'); + $table->year('tahun_lulus_sma')->nullable()->after('tahun_masuk_sma'); + + // Minat & Karir + $table->string('minat')->nullable()->change(); + $table->string('cita_cita')->nullable()->change(); + $table->enum('preferensi_studi_lanjutan', ['Praktik_Langsung', 'DuDi', 'Project_Based', 'Blended'])->nullable()->after('cita_cita'); + $table->text('prestasi')->nullable()->change(); + + // === OUTPUT VARIABLES (di Polije) === + $table->year('tahun_masuk_polije')->nullable()->after('major_masuk'); + $table->year('tahun_lulus_polije')->nullable()->after('tahun_masuk_polije'); + + // === METADATA === + if (!Schema::hasColumn('alumni', 'notes')) { + $table->text('notes')->nullable()->after('catatan'); + } + }); + } + + public function down(): void + { + Schema::table('alumni', function (Blueprint $table) { + // + }); + } +}; diff --git a/database/migrations/2026_02_21_100000_add_keywords_to_polije_majors_table.php b/database/migrations/2026_02_21_100000_add_keywords_to_polije_majors_table.php new file mode 100644 index 0000000..eb2d98f --- /dev/null +++ b/database/migrations/2026_02_21_100000_add_keywords_to_polije_majors_table.php @@ -0,0 +1,23 @@ +json('keywords')->nullable()->after('deskripsi'); + $table->json('preferensi_studi')->nullable()->after('keywords'); + }); + } + + public function down(): void + { + Schema::table('polije_majors', function (Blueprint $table) { + $table->dropColumn(['keywords', 'preferensi_studi']); + }); + } +}; diff --git a/database/migrations/2026_02_26_100000_fix_recommendations_hasil_column.php b/database/migrations/2026_02_26_100000_fix_recommendations_hasil_column.php new file mode 100644 index 0000000..b6f5dea --- /dev/null +++ b/database/migrations/2026_02_26_100000_fix_recommendations_hasil_column.php @@ -0,0 +1,22 @@ +longText('hasil_rekomendasi')->nullable()->change(); + }); + } + + public function down(): void + { + Schema::table('recommendations', function (Blueprint $table) { + $table->string('hasil_rekomendasi')->nullable()->change(); + }); + } +}; diff --git a/database/migrations/2026_02_26_120000_add_bobot_mapel_to_polije_majors_table.php b/database/migrations/2026_02_26_120000_add_bobot_mapel_to_polije_majors_table.php new file mode 100644 index 0000000..e4bd50d --- /dev/null +++ b/database/migrations/2026_02_26_120000_add_bobot_mapel_to_polije_majors_table.php @@ -0,0 +1,22 @@ +json('bobot_mapel')->nullable()->after('preferensi_studi'); + }); + } + + public function down(): void + { + Schema::table('polije_majors', function (Blueprint $table) { + $table->dropColumn('bobot_mapel'); + }); + } +}; diff --git a/database/seeders/AdminSeeder.php b/database/seeders/AdminSeeder.php new file mode 100644 index 0000000..8a65d61 --- /dev/null +++ b/database/seeders/AdminSeeder.php @@ -0,0 +1,43 @@ + 'admin@gmail.com'], + [ + 'name' => 'Admin Polije', + 'password' => Hash::make('admin123'), + 'role' => 'admin', + 'email_verified_at' => now(), + ] + ); + + // Create BK (Konselor) User + User::firstOrCreate( + ['email' => 'bk@gmail.com'], + [ + 'name' => 'Konselor BK', + 'password' => Hash::make('bk123'), + 'role' => 'bk', + 'email_verified_at' => now(), + ] + ); + + echo "✅ Admin & BK users created successfully!\n"; + echo "Admin: admin@gmail.com / admin123\n"; + echo "BK: bk@gmail.com / bk123\n"; + } +} diff --git a/database/seeders/AlumniSeeder.php b/database/seeders/AlumniSeeder.php new file mode 100644 index 0000000..4c25bc1 --- /dev/null +++ b/database/seeders/AlumniSeeder.php @@ -0,0 +1,203 @@ +5) + + $alumniData = [ + // === IPA === + [ + 'nama_alumni' => 'Budi Santoso', + 'nis' => 'SMA001', + 'kelompok_asal' => 'IPA', + 'mtk' => 85, + 'fisika' => 82, + 'kimia' => 88, + 'biologi' => 90, + 'minat' => 'Teknologi & Robotika', + 'cita_cita' => 'Software Developer', + 'preferensi_studi' => 'Sains & Teknologi', + 'prestasi' => 'Juara 1 Olimpiade Komputer Nasional', + 'major_masuk' => 'Teknik Informatika', // Masuk jurusan ini + 'ranking_saat_rekomendasi' => 1, // Rekomendasi ranking 1 = COCOK! ✓✓ + 'success_status' => 'sangat_sukses', + 'catatan' => 'Rekomendasi akurat - ranking 1 cocok dengan pilihan', + ], + [ + 'nama_alumni' => 'Siti Nurhaliza', + 'nis' => 'SMA002', + 'kelompok_asal' => 'IPA', + 'mtk' => 90, + 'fisika' => 88, + 'kimia' => 92, + 'biologi' => 94, + 'minat' => 'Kesehatan & Bioteknologi', + 'cita_cita' => 'Biomedical Engineer', + 'preferensi_studi' => 'Kesehatan & Ilmu Hayat', + 'prestasi' => 'Beasiswa Penuh Akademik', + 'major_masuk' => 'Teknik Biomedis', + 'ranking_saat_rekomendasi' => 2, // Cocok ✓ + 'success_status' => 'sangat_sukses', + 'catatan' => 'Rekomendasi akurat - ranking 2 cocok', + ], + [ + 'nama_alumni' => 'Ahmad Wijaya', + 'nis' => 'SMA003', + 'kelompok_asal' => 'IPA', + 'mtk' => 75, + 'fisika' => 78, + 'kimia' => 80, + 'biologi' => 72, + 'minat' => 'Teknik Mesin', + 'cita_cita' => 'Mechanical Engineer', + 'preferensi_studi' => 'Sains & Teknologi', + 'prestasi' => 'Sertifikat Kompetisi Robotika', + 'major_masuk' => 'Teknik Mesin', + 'ranking_saat_rekomendasi' => 3, // Cocok ✓ + 'success_status' => 'sukses', + 'catatan' => 'Rekomendasi cukup akurat - ranking 3 cocok', + ], + [ + 'nama_alumni' => 'Lina Hartini', + 'nis' => 'SMA004', + 'kelompok_asal' => 'IPA', + 'mtk' => 88, + 'fisika' => 86, + 'kimia' => 90, + 'biologi' => 92, + 'minat' => 'Riset & Sains Terapan', + 'cita_cita' => 'Research Scientist', + 'preferensi_studi' => 'Kesehatan & Ilmu Hayat', + 'prestasi' => 'Publikasi Paper Research', + 'major_masuk' => 'Teknik Biomedis', + 'ranking_saat_rekomendasi' => 1, // Cocok ✓✓ + 'success_status' => 'sangat_sukses', + 'catatan' => 'Rekomendasi sangat akurat', + ], + [ + 'nama_alumni' => 'Fajar Maulana', + 'nis' => 'SMA005', + 'kelompok_asal' => 'IPA', + 'mtk' => 72, + 'fisika' => 70, + 'kimia' => 68, + 'biologi' => 65, + 'minat' => 'Teknik Elektro', + 'cita_cita' => 'Electrical Engineer', + 'preferensi_studi' => 'Sains & Teknologi', + 'prestasi' => '-', + 'major_masuk' => 'Teknik Mesin', // BEDA dari rekomendasi ranking 1 + 'ranking_saat_rekomendasi' => 6, // Ranking 6 = kurang cocok + 'success_status' => 'cukup', + 'catatan' => 'Rekomendasi kurang akurat - pilih jurusan berbeda', + ], + + // === IPS === + [ + 'nama_alumni' => 'Rina Handayani', + 'nis' => 'SMA006', + 'kelompok_asal' => 'IPS', + 'ekonomi' => 90, + 'geografi' => 87, + 'sosiologi' => 88, + 'sejarah' => 85, + 'minat' => 'Bisnis & Manajemen', + 'cita_cita' => 'Business Manager', + 'preferensi_studi' => 'Bisnis & Manajemen', + 'prestasi' => 'Juara Debat Nasional', + 'major_masuk' => 'Manajemen Bisnis', + 'ranking_saat_rekomendasi' => 1, // Cocok ✓✓ + 'success_status' => 'sangat_sukses', + 'catatan' => 'Rekomendasi sempurna', + ], + [ + 'nama_alumni' => 'Dewi Prasetya', + 'nis' => 'SMA007', + 'kelompok_asal' => 'IPS', + 'ekonomi' => 85, + 'geografi' => 86, + 'sosiologi' => 84, + 'sejarah' => 88, + 'minat' => 'Akuntansi & Keuangan', + 'cita_cita' => 'Akuntan Publik', + 'preferensi_studi' => 'Bisnis & Manajemen', + 'prestasi' => 'Sertifikasi ACCA', + 'major_masuk' => 'Akuntansi', + 'ranking_saat_rekomendasi' => 2, // Cocok ✓ + 'success_status' => 'sukses', + 'catatan' => 'Rekomendasi akurat', + ], + [ + 'nama_alumni' => 'Rudi Hermawan', + 'nis' => 'SMA008', + 'kelompok_asal' => 'IPS', + 'ekonomi' => 68, + 'geografi' => 71, + 'sosiologi' => 65, + 'sejarah' => 72, + 'minat' => 'Pemerintahan & Administrasi', + 'cita_cita' => 'PNS', + 'preferensi_studi' => 'Sosial & Humaniora', + 'prestasi' => '-', + 'major_masuk' => 'Administrasi Publik', // RANKING JAUH dari pilihan + 'ranking_saat_rekomendasi' => 7, // Ranking 7 = TIDAK COCOK ✗ + 'success_status' => 'kurang_sukses', + 'catatan' => 'Rekomendasi salah - siswa pilih jurusan lain', + ], + [ + 'nama_alumni' => 'Indra Setiawan', + 'nis' => 'SMA009', + 'kelompok_asal' => 'IPS', + 'ekonomi' => 82, + 'geografi' => 80, + 'sosiologi' => 78, + 'sejarah' => 84, + 'minat' => 'Marketing & Digital', + 'cita_cita' => 'Marketing Manager', + 'preferensi_studi' => 'Bisnis & Manajemen', + 'prestasi' => 'Kompetisi Business Plan', + 'major_masuk' => 'Manajemen Bisnis', + 'ranking_saat_rekomendasi' => 2, // Cocok ✓ + 'success_status' => 'sukses', + 'catatan' => 'Rekomendasi akurat', + ], + [ + 'nama_alumni' => 'Maya Suntari', + 'nis' => 'SMA010', + 'kelompok_asal' => 'IPS', + 'ekonomi' => 75, + 'geografi' => 73, + 'sosiologi' => 76, + 'sejarah' => 78, + 'minat' => 'Akuntansi & Keuangan', + 'cita_cita' => 'Accountant', + 'preferensi_studi' => 'Bisnis & Manajemen', + 'prestasi' => 'Buku Tahunan Finance Club', + 'major_masuk' => 'Akuntansi', + 'ranking_saat_rekomendasi' => 3, // Cocok ✓ + 'success_status' => 'sukses', + 'catatan' => 'Rekomendasi cukup akurat - ranking 3', + ], + ]; + + foreach ($alumniData as $data) { + Alumni::firstOrCreate( + ['nis' => $data['nis']], + $data + ); + } + + echo "\n✅ AlumniSeeder: " . count($alumniData) . " alumni SMA Bima Ambulu loaded!\n"; + echo "📊 Fokus: Validasi akurasi rekomendasi (ranking 1-3 = sukses, >5 = gagal)\n"; + } +} diff --git a/database/seeders/PolijeMajorSeeder.php b/database/seeders/PolijeMajorSeeder.php index bd27718..343719d 100644 --- a/database/seeders/PolijeMajorSeeder.php +++ b/database/seeders/PolijeMajorSeeder.php @@ -9,27 +9,119 @@ class PolijeMajorSeeder extends Seeder { public function run(): void { - // Bersihkan data agar tidak duplikat - PolijeMajor::truncate(); - $jurusans = [ - 'Produksi Pertanian', - 'Teknologi Pertanian', - 'Peternakan', - 'Manajemen Agribisnis', - 'Teknologi Informasi', - 'Teknis', - 'Kesehatan', - 'Bahasa, Komunikasi dan Pariwisata', - 'Bisnis', + [ + 'nama_jurusan' => 'Produksi Pertanian', + 'deskripsi' => 'Program studi yang mempelajari teknik budidaya tanaman, pengelolaan lahan pertanian, dan produksi hasil pertanian secara modern.', + 'keywords' => ['pertanian', 'petani', 'kebun', 'sawah', 'panen', 'tanaman', 'budidaya', 'agronomi', 'tanam', 'bercocok tanam', 'alam', 'hortikultura', 'pupuk', 'bibit'], + 'preferensi_studi' => ['Pertanian & Lingkungan'], + 'bobot_mapel' => [ + 'biologi' => 0.40, 'kimia' => 0.30, 'fisika' => 0.15, 'mtk' => 0.15, + 'geografi' => 0.35, 'ekonomi' => 0.30, 'sosiologi' => 0.20, 'sejarah' => 0.15, + ], + 'prospek_kerja' => 'Petani modern, konsultan pertanian, pengelola perkebunan, peneliti pertanian, agronomis.', + ], + [ + 'nama_jurusan' => 'Teknologi Pertanian', + 'deskripsi' => 'Program studi yang mengintegrasikan teknologi dengan pertanian, meliputi mekanisasi pertanian, pengolahan hasil pertanian, dan inovasi teknologi pangan.', + 'keywords' => ['teknologi pertanian', 'mesin pertanian', 'inovasi', 'otomasi', 'pengolahan pangan', 'pangan', 'mekanisasi', 'teknologi pangan', 'alat pertanian', 'rekayasa'], + 'preferensi_studi' => ['Sains & Teknologi', 'Pertanian & Lingkungan'], + 'bobot_mapel' => [ + 'fisika' => 0.35, 'mtk' => 0.30, 'kimia' => 0.20, 'biologi' => 0.15, + 'ekonomi' => 0.30, 'geografi' => 0.30, 'sosiologi' => 0.20, 'sejarah' => 0.20, + ], + 'prospek_kerja' => 'Teknisi pertanian, ahli mekanisasi, quality control pangan, peneliti teknologi pangan.', + ], + [ + 'nama_jurusan' => 'Peternakan', + 'deskripsi' => 'Program studi yang mempelajari pengelolaan dan pemeliharaan ternak, nutrisi hewan, reproduksi, dan pengolahan produk peternakan.', + 'keywords' => ['ternak', 'hewan', 'peternakan', 'peternak', 'sapi', 'ayam', 'unggas', 'kambing', 'susu', 'pakan', 'nutrisi hewan', 'veteriner', 'ikan', 'aquaculture'], + 'preferensi_studi' => ['Pertanian & Lingkungan', 'Kesehatan & Ilmu Hayat'], + 'bobot_mapel' => [ + 'biologi' => 0.45, 'kimia' => 0.25, 'fisika' => 0.15, 'mtk' => 0.15, + 'geografi' => 0.30, 'ekonomi' => 0.30, 'sosiologi' => 0.20, 'sejarah' => 0.20, + ], + 'prospek_kerja' => 'Peternak profesional, konsultan peternakan, manajer peternakan, ahli nutrisi hewan.', + ], + [ + 'nama_jurusan' => 'Manajemen Agribisnis', + 'deskripsi' => 'Program studi yang menggabungkan ilmu pertanian dan bisnis, meliputi pemasaran hasil pertanian, manajemen usaha tani, dan kewirausahaan agribisnis.', + 'keywords' => ['bisnis', 'agribisnis', 'usaha', 'entrepreneur', 'pengusaha', 'dagang', 'jual', 'pemasaran', 'kewirausahaan', 'manajemen', 'ekonomi pertanian', 'pasar'], + 'preferensi_studi' => ['Bisnis & Manajemen', 'Pertanian & Lingkungan'], + 'bobot_mapel' => [ + 'mtk' => 0.35, 'biologi' => 0.25, 'kimia' => 0.20, 'fisika' => 0.20, + 'ekonomi' => 0.45, 'geografi' => 0.20, 'sosiologi' => 0.20, 'sejarah' => 0.15, + ], + 'prospek_kerja' => 'Manajer agribisnis, entrepreneur pertanian, konsultan pemasaran pertanian, analis pasar komoditas.', + ], + [ + 'nama_jurusan' => 'Teknologi Informasi', + 'deskripsi' => 'Program studi yang mempelajari pengembangan perangkat lunak, jaringan komputer, keamanan siber, dan teknologi digital.', + 'keywords' => ['programmer', 'developer', 'coding', 'software', 'web', 'aplikasi', 'komputer', 'it', 'jaringan', 'hacker', 'game', 'data', 'ai', 'robot', 'ngoding', 'laptop', 'teknologi', 'digital', 'internet', 'programming', 'desain grafis'], + 'preferensi_studi' => ['Sains & Teknologi'], + 'bobot_mapel' => [ + 'mtk' => 0.45, 'fisika' => 0.25, 'kimia' => 0.15, 'biologi' => 0.15, + 'ekonomi' => 0.25, 'geografi' => 0.20, 'sosiologi' => 0.25, 'sejarah' => 0.30, + ], + 'prospek_kerja' => 'Software developer, web developer, network engineer, data analyst, cybersecurity specialist.', + ], + [ + 'nama_jurusan' => 'Teknik', + 'deskripsi' => 'Program studi yang mempelajari mesin, kelistrikan, elektronika, dan otomasi industri.', + 'keywords' => ['mesin', 'bengkel', 'listrik', 'las', 'robot', 'motor', 'teknik', 'otomasi', 'elektronik', 'instalasi', 'panel', 'mekanik', 'industri', 'manufaktur', 'pabrik', 'bangunan', 'konstruksi', 'sipil', 'energi'], + 'preferensi_studi' => ['Sains & Teknologi'], + 'bobot_mapel' => [ + 'fisika' => 0.40, 'mtk' => 0.35, 'kimia' => 0.15, 'biologi' => 0.10, + 'ekonomi' => 0.25, 'geografi' => 0.25, 'sosiologi' => 0.20, 'sejarah' => 0.30, + ], + 'prospek_kerja' => 'Teknisi mesin, ahli listrik, engineer industri, maintenance engineer, kontraktor.', + ], + [ + 'nama_jurusan' => 'Kesehatan', + 'deskripsi' => 'Program studi yang mempelajari ilmu kesehatan, gizi, rekam medis, dan pelayanan kesehatan masyarakat.', + 'keywords' => ['dokter', 'perawat', 'medis', 'gizi', 'kesehatan', 'pelayanan', 'terapis', 'obat', 'rumah sakit', 'klinik', 'farmasi', 'nutrisi', 'sanitasi', 'rawat', 'sehat'], + 'preferensi_studi' => ['Kesehatan & Ilmu Hayat'], + 'bobot_mapel' => [ + 'biologi' => 0.40, 'kimia' => 0.35, 'mtk' => 0.15, 'fisika' => 0.10, + 'sosiologi' => 0.30, 'ekonomi' => 0.25, 'geografi' => 0.25, 'sejarah' => 0.20, + ], + 'prospek_kerja' => 'Ahli gizi, perekam medis, tenaga kesehatan, asisten apoteker, sanitarian.', + ], + [ + 'nama_jurusan' => 'Bahasa, Komunikasi, dan Pariwisata', + 'deskripsi' => 'Program studi yang mempelajari bahasa asing, komunikasi, perhotelan, dan industri pariwisata.', + 'keywords' => ['bahasa', 'komunikasi', 'pariwisata', 'tour guide', 'hotel', 'jurnalis', 'marketing', 'inggris', 'penerjemah', 'travel', 'wisata', 'hospitality', 'public speaking', 'media', 'broadcasting'], + 'preferensi_studi' => ['Sosial & Humaniora', 'Bisnis & Manajemen'], + 'bobot_mapel' => [ + 'biologi' => 0.20, 'kimia' => 0.20, 'fisika' => 0.20, 'mtk' => 0.40, + 'sosiologi' => 0.30, 'sejarah' => 0.30, 'geografi' => 0.25, 'ekonomi' => 0.15, + ], + 'prospek_kerja' => 'Tour guide, staf perhotelan, jurnalis, public relation, penerjemah, staf maskapai.', + ], + [ + 'nama_jurusan' => 'Bisnis', + 'deskripsi' => 'Program studi yang mempelajari akuntansi, manajemen bisnis, perbankan, dan administrasi niaga.', + 'keywords' => ['manager', 'pimpinan', 'bisnis', 'accounting', 'marketing', 'sales', 'kantor', 'keuangan', 'bank', 'akuntansi', 'hitung', 'administrasi', 'perbankan', 'ekonomi', 'uang', 'investasi', 'pajak'], + 'preferensi_studi' => ['Bisnis & Manajemen'], + 'bobot_mapel' => [ + 'mtk' => 0.45, 'fisika' => 0.20, 'kimia' => 0.15, 'biologi' => 0.20, + 'ekonomi' => 0.45, 'sosiologi' => 0.20, 'geografi' => 0.15, 'sejarah' => 0.20, + ], + 'prospek_kerja' => 'Akuntan, staf perbankan, manajer bisnis, marketing executive, analis keuangan.', + ], ]; foreach ($jurusans as $jur) { - PolijeMajor::create([ - 'nama_jurusan' => $jur, - 'deskripsi' => 'Program ini berfokus pada keahlian di bidang ' . $jur, - 'prospek_kerja' => 'Lulusan dapat berkarir di bidang terkait ' . $jur, - ]); + PolijeMajor::updateOrCreate( + ['nama_jurusan' => $jur['nama_jurusan']], + [ + 'deskripsi' => $jur['deskripsi'], + 'keywords' => $jur['keywords'], + 'preferensi_studi' => $jur['preferensi_studi'], + 'bobot_mapel' => $jur['bobot_mapel'], + 'prospek_kerja' => $jur['prospek_kerja'], + ] + ); } } } \ No newline at end of file diff --git a/lang/id/auth.php b/lang/id/auth.php new file mode 100644 index 0000000..4770d65 --- /dev/null +++ b/lang/id/auth.php @@ -0,0 +1,17 @@ + 'Kredensial yang diberikan tidak sesuai dengan catatan kami.', + 'password' => 'Password yang diberikan salah.', + 'throttle' => 'Terlalu banyak upaya login. Silakan coba lagi dalam :seconds detik.', +]; diff --git a/lang/id/messages.php b/lang/id/messages.php new file mode 100644 index 0000000..eb9c4cc --- /dev/null +++ b/lang/id/messages.php @@ -0,0 +1,86 @@ + 'Masuk', + 'logout' => 'Keluar', + 'register' => 'Daftar', + 'save' => 'Simpan', + 'cancel' => 'Batal', + 'delete' => 'Hapus', + 'edit' => 'Ubah', + 'update' => 'Perbarui', + 'create' => 'Buat', + 'submit' => 'Kirim', + 'reset' => 'Reset', + 'confirm' => 'Konfirmasi', + 'back' => 'Kembali', + 'next' => 'Selanjutnya', + 'send' => 'Kirim', + 'search' => 'Cari', + 'filter' => 'Filter', + 'download' => 'Unduh', + 'upload' => 'Unggah', + 'delete_account' => 'Hapus Akun', + 'save_changes' => 'Simpan Perubahan', + 'change_password' => 'Ubah Password', + 'forgot_password' => 'Lupa Password?', + 'remember_me' => 'Ingat saya', + 'start_analysis' => 'Mulai Analisis', + 'register_now' => 'Daftar Sekarang', + + // Messages + 'welcome' => 'Selamat Datang', + 'home' => 'Beranda', + 'dashboard' => 'Dashboard', + 'profile' => 'Profil', + 'settings' => 'Pengaturan', + 'help' => 'Bantuan', + 'about' => 'Tentang', + 'contact' => 'Hubungi Kami', + 'success' => 'Berhasil', + 'error' => 'Kesalahan', + 'warning' => 'Peringatan', + 'info' => 'Informasi', + 'loading' => 'Sedang memuat...', + 'no_data' => 'Tidak ada data', + 'are_you_sure' => 'Apakah Anda yakin?', + 'saved_successfully' => 'Berhasil disimpan!', + 'deleted_successfully' => 'Berhasil dihapus!', + 'updated_successfully' => 'Berhasil diperbarui!', + 'created_successfully' => 'Berhasil dibuat!', + 'something_went_wrong' => 'Terjadi kesalahan. Silakan coba lagi.', + + // Authentication + 'email' => 'Email', + 'password' => 'Password', + 'confirm_password' => 'Konfirmasi Password', + 'sign_in' => 'Masuk ke Akun', + 'sign_up' => 'Buat Akun Baru', + 'email_address' => 'Alamat Email', + 'full_name' => 'Nama Lengkap', + 'forgot_your_password' => 'Lupa Password Anda?', + 'check_your_email' => 'Periksa email Anda', + 'password_reset_link' => 'Tautan untuk mengatur ulang password telah dikirim ke email Anda.', + 'reset_password' => 'Atur Ulang Password', + 'email_password_reset_link' => 'Kirim Tautan Atur Ulang Password', + 'confirm_your_password' => 'Konfirmasi Password Anda', + + // Profile + 'profile_information' => 'Informasi Profil', + 'update_your_account_profile_information_and_email_address' => 'Perbarui informasi profil akun Anda dan alamat email.', + 'update_password' => 'Perbarui Password', + 'ensure_your_account_is_using_a_long_random_password' => 'Pastikan akun Anda menggunakan password yang panjang dan acak untuk tetap aman.', + 'current_password' => 'Password Saat Ini', + 'new_password' => 'Password Baru', + 'delete_account_permanently' => 'Hapus Akun Secara Permanen', + 'once_your_account_is_deleted' => 'Setelah akun Anda dihapus, semua sumber daya dan data akan dihapus secara permanen.', + 'this_action_cannot_be_undone' => 'Tindakan ini tidak dapat dibatalkan.', + 'are_you_sure_you_want_to_delete_your_account' => 'Apakah Anda yakin ingin menghapus akun Anda?', + 'your_email_address_is_unverified' => 'Alamat email Anda belum diverifikasi.', + 'click_here_to_resend_verification_email' => 'Klik di sini untuk mengirim ulang email verifikasi.', + 'verification_link_sent' => 'Tautan verifikasi telah dikirim ke email Anda.', + 'password_updated' => 'Password berhasil diperbarui!', + 'saved' => 'Berhasil disimpan!', + 'profile_updated' => 'Profil berhasil diperbarui!', +]; diff --git a/lang/id/validation.php b/lang/id/validation.php new file mode 100644 index 0000000..ff61501 --- /dev/null +++ b/lang/id/validation.php @@ -0,0 +1,149 @@ + 'Kolom :attribute harus diterima.', + 'accepted_if' => 'Kolom :attribute harus diterima ketika :other adalah :value.', + 'active_url' => 'Kolom :attribute harus berupa URL yang valid.', + 'after' => 'Kolom :attribute harus berupa tanggal setelah :date.', + 'after_or_equal' => 'Kolom :attribute harus berupa tanggal setelah atau sama dengan :date.', + 'alpha' => 'Kolom :attribute hanya boleh berisi huruf.', + 'alpha_dash' => 'Kolom :attribute hanya boleh berisi huruf, angka, garis bawah, dan garis putus-putus.', + 'alpha_num' => 'Kolom :attribute hanya boleh berisi huruf dan angka.', + 'array' => 'Kolom :attribute harus berupa larik.', + 'ascii' => 'Kolom :attribute hanya boleh berisi karakter dan simbol ASCII satu byte.', + 'before' => 'Kolom :attribute harus berupa tanggal sebelum :date.', + 'before_or_equal' => 'Kolom :attribute harus berupa tanggal sebelum atau sama dengan :date.', + 'between' => [ + 'numeric' => 'Kolom :attribute harus berada di antara :min dan :max.', + 'file' => 'Kolom :attribute harus berada di antara :min dan :max kilobita.', + 'string' => 'Kolom :attribute harus berada di antara :min dan :max karakter.', + 'array' => 'Kolom :attribute harus memiliki antara :min dan :max item.', + ], + 'boolean' => 'Kolom :attribute harus benar atau salah.', + 'confirmed' => 'Konfirmasi :attribute tidak cocok.', + 'current_password' => 'Password salah.', + 'date' => 'Kolom :attribute bukan tanggal yang valid.', + 'date_equals' => 'Kolom :attribute harus berupa tanggal yang sama dengan :date.', + 'date_format' => 'Kolom :attribute tidak sesuai dengan format :format.', + 'declined' => 'Kolom :attribute harus ditolak.', + 'declined_if' => 'Kolom :attribute harus ditolak ketika :other adalah :value.', + 'different' => 'Kolom :attribute dan :other harus berbeda.', + 'digits' => 'Kolom :attribute harus terdiri dari :digits digit.', + 'digits_between' => 'Kolom :attribute harus berada di antara :min dan :max digit.', + 'dimensions' => 'Kolom :attribute memiliki dimensi gambar yang tidak valid.', + 'distinct' => 'Kolom :attribute memiliki nilai duplikat.', + 'email' => 'Kolom :attribute harus berupa alamat email yang valid.', + 'ends_with' => 'Kolom :attribute harus diakhiri dengan salah satu dari berikut: :values.', + 'exists' => 'Kolom :attribute yang dipilih tidak valid.', + 'file' => 'Kolom :attribute harus berupa file.', + 'filled' => 'Kolom :attribute harus memiliki nilai.', + 'gt' => [ + 'numeric' => 'Kolom :attribute harus lebih besar dari :value.', + 'file' => 'Kolom :attribute harus lebih besar dari :value kilobita.', + 'string' => 'Kolom :attribute harus lebih besar dari :value karakter.', + 'array' => 'Kolom :attribute harus memiliki lebih dari :value item.', + ], + 'gte' => [ + 'numeric' => 'Kolom :attribute harus lebih besar dari atau sama dengan :value.', + 'file' => 'Kolom :attribute harus lebih besar dari atau sama dengan :value kilobita.', + 'string' => 'Kolom :attribute harus lebih besar dari atau sama dengan :value karakter.', + 'array' => 'Kolom :attribute harus memiliki :value item atau lebih.', + ], + 'image' => 'Kolom :attribute harus berupa gambar.', + 'in' => 'Kolom :attribute yang dipilih tidak valid.', + 'in_array' => 'Kolom :attribute harus ada dalam :other.', + 'integer' => 'Kolom :attribute harus berupa integer.', + 'ip' => 'Kolom :attribute harus berupa alamat IP yang valid.', + 'ipv4' => 'Kolom :attribute harus berupa alamat IPv4 yang valid.', + 'ipv6' => 'Kolom :attribute harus berupa alamat IPv6 yang valid.', + 'json' => 'Kolom :attribute harus berupa string JSON yang valid.', + 'lt' => [ + 'numeric' => 'Kolom :attribute harus kurang dari :value.', + 'file' => 'Kolom :attribute harus kurang dari :value kilobita.', + 'string' => 'Kolom :attribute harus kurang dari :value karakter.', + 'array' => 'Kolom :attribute harus memiliki kurang dari :value item.', + ], + 'lte' => [ + 'numeric' => 'Kolom :attribute harus kurang dari atau sama dengan :value.', + 'file' => 'Kolom :attribute harus kurang dari atau sama dengan :value kilobita.', + 'string' => 'Kolom :attribute harus kurang dari atau sama dengan :value karakter.', + 'array' => 'Kolom :attribute tidak boleh memiliki lebih dari :value item.', + ], + 'max' => [ + 'numeric' => 'Kolom :attribute tidak boleh lebih besar dari :max.', + 'file' => 'Kolom :attribute tidak boleh lebih besar dari :max kilobita.', + 'string' => 'Kolom :attribute tidak boleh lebih besar dari :max karakter.', + 'array' => 'Kolom :attribute tidak boleh memiliki lebih dari :max item.', + ], + 'mimes' => 'Kolom :attribute harus berupa file bertipe: :values.', + 'mimetypes' => 'Kolom :attribute harus berupa file bertipe: :values.', + 'min' => [ + 'numeric' => 'Kolom :attribute harus minimal :min.', + 'file' => 'Kolom :attribute harus minimal :min kilobita.', + 'string' => 'Kolom :attribute harus minimal :min karakter.', + 'array' => 'Kolom :attribute harus memiliki minimal :min item.', + ], + 'multiple_of' => 'Kolom :attribute harus merupakan kelipatan dari :value.', + 'not_in' => 'Kolom :attribute yang dipilih tidak valid.', + 'not_regex' => 'Format kolom :attribute tidak valid.', + 'numeric' => 'Kolom :attribute harus berupa angka.', + 'password' => 'Password salah.', + 'present' => 'Kolom :attribute harus ada.', + 'regex' => 'Format kolom :attribute tidak valid.', + 'required' => 'Kolom :attribute harus diisi.', + 'required_if' => 'Kolom :attribute harus diisi ketika :other adalah :value.', + 'required_unless' => 'Kolom :attribute harus diisi kecuali :other adalah :values.', + 'required_with' => 'Kolom :attribute harus diisi ketika :values ada.', + 'required_with_all' => 'Kolom :attribute harus diisi ketika :values ada.', + 'required_without' => 'Kolom :attribute harus diisi ketika :values tidak ada.', + 'required_without_all' => 'Kolom :attribute harus diisi ketika tidak ada dari :values yang ada.', + 'same' => 'Kolom :attribute dan :other harus cocok.', + 'size' => [ + 'numeric' => 'Kolom :attribute harus berukuran :size.', + 'file' => 'Kolom :attribute harus berukuran :size kilobita.', + 'string' => 'Kolom :attribute harus terdiri dari :size karakter.', + 'array' => 'Kolom :attribute harus berisi :size item.', + ], + 'starts_with' => 'Kolom :attribute harus dimulai dengan salah satu dari berikut: :values.', + 'string' => 'Kolom :attribute harus berupa string.', + 'timezone' => 'Kolom :attribute harus berupa zona waktu yang valid.', + 'unique' => 'Kolom :attribute sudah ada.', + 'uploaded' => 'Kolom :attribute gagal diunggah.', + 'url' => 'Format kolom :attribute tidak valid.', + 'uuid' => 'Kolom :attribute harus berupa UUID yang valid.', + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + */ + + 'attributes' => [ + 'name' => 'nama', + 'email' => 'email', + 'password' => 'password', + 'password_confirmation' => 'konfirmasi password', + ], +]; diff --git a/resources/lang/id/auth.php b/resources/lang/id/auth.php new file mode 100644 index 0000000..1c753bd --- /dev/null +++ b/resources/lang/id/auth.php @@ -0,0 +1,7 @@ + 'Email atau password yang Anda masukkan tidak sesuai dengan data kami.', + 'password' => 'Password yang Anda masukkan salah.', + 'throttle' => 'Terlalu banyak upaya login. Silakan coba lagi dalam :seconds detik.', +]; diff --git a/resources/lang/id/messages.php b/resources/lang/id/messages.php new file mode 100644 index 0000000..9af222d --- /dev/null +++ b/resources/lang/id/messages.php @@ -0,0 +1,54 @@ + 'Simpan', + 'cancel' => 'Batal', + 'delete' => 'Hapus', + 'edit' => 'Edit', + 'update' => 'Perbarui', + 'create' => 'Buat', + 'submit' => 'Kirim', + 'confirm' => 'Konfirmasi', + 'back' => 'Kembali', + 'next' => 'Selanjutnya', + 'logout' => 'Keluar', + 'login' => 'Masuk', + 'register' => 'Daftar', + + // Messages + 'welcome' => 'Selamat Datang', + 'dashboard' => 'Dashboard', + 'profile' => 'Profil', + 'settings' => 'Pengaturan', + 'success' => 'Berhasil', + 'error' => 'Kesalahan', + 'warning' => 'Peringatan', + 'loading' => 'Sedang memuat...', + 'no_data' => 'Tidak ada data', + + // Auth + 'email' => 'Email', + 'password' => 'Password', + 'confirm_password' => 'Konfirmasi Password', + 'sign_in' => 'Masuk', + 'sign_up' => 'Daftar', + 'forgot_your_password' => 'Lupa password Anda?', + 'dont_have_account' => 'Belum punya akun?', + 'already_have_account' => 'Sudah punya akun?', + + // Profile + 'profile_information' => 'Informasi Profil', + 'update_password' => 'Perbarui Password', + 'current_password' => 'Password Saat Ini', + 'new_password' => 'Password Baru', + 'delete_account_permanently' => 'Hapus Akun Secara Permanen', + + // Feedback + 'saved_successfully' => 'Berhasil disimpan!', + 'deleted_successfully' => 'Berhasil dihapus!', + 'updated_successfully' => 'Berhasil diperbarui!', + 'created_successfully' => 'Berhasil dibuat!', + 'password_updated' => 'Password berhasil diperbarui!', + 'profile_updated' => 'Profil berhasil diperbarui!', +]; diff --git a/resources/lang/id/validation.php b/resources/lang/id/validation.php new file mode 100644 index 0000000..2ac5653 --- /dev/null +++ b/resources/lang/id/validation.php @@ -0,0 +1,164 @@ + 'Kolom :attribute harus diterima.', + 'accepted_if' => 'Kolom :attribute harus diterima ketika :other adalah :value.', + 'active_url' => 'Kolom :attribute bukan URL yang valid.', + 'after' => 'Kolom :attribute harus berupa tanggal setelah :date.', + 'after_or_equal' => 'Kolom :attribute harus berupa tanggal setelah atau sama dengan :date.', + 'alpha' => 'Kolom :attribute hanya boleh berisi huruf.', + 'alpha_dash' => 'Kolom :attribute hanya boleh berisi huruf, angka, dan garis putus-putus.', + 'alpha_num' => 'Kolom :attribute hanya boleh berisi huruf dan angka.', + 'array' => 'Kolom :attribute harus berupa array.', + 'before' => 'Kolom :attribute harus berupa tanggal sebelum :date.', + 'before_or_equal' => 'Kolom :attribute harus berupa tanggal sebelum atau sama dengan :date.', + 'between' => [ + 'numeric' => 'Kolom :attribute harus berada antara :min dan :max.', + 'file' => 'Kolom :attribute harus berada antara :min dan :max kilobyte.', + 'string' => 'Kolom :attribute harus berada antara :min dan :max karakter.', + 'array' => 'Kolom :attribute harus memiliki antara :min dan :max item.', + ], + 'boolean' => 'Kolom :attribute harus bernilai true atau false.', + 'confirmed' => 'Kolom :attribute tidak cocok.', + 'current_password' => 'Password salah.', + 'date' => 'Kolom :attribute bukan tanggal yang valid.', + 'date_equals' => 'Kolom :attribute harus berupa tanggal yang sama dengan :date.', + 'date_format' => 'Kolom :attribute tidak cocok dengan format :format.', + 'declined' => 'Kolom :attribute harus ditolak.', + 'declined_if' => 'Kolom :attribute harus ditolak ketika :other adalah :value.', + 'different' => 'Kolom :attribute dan :other harus berbeda.', + 'digits' => 'Kolom :attribute harus :digits digit.', + 'digits_between' => 'Kolom :attribute harus antara :min dan :max digit.', + 'dimensions' => 'Kolom :attribute memiliki dimensi gambar yang tidak valid.', + 'distinct' => 'Kolom :attribute memiliki nilai duplikat.', + 'email' => 'Kolom :attribute harus berupa alamat email yang valid.', + 'ends_with' => 'Kolom :attribute harus diakhiri dengan salah satu dari: :values.', + 'exists' => 'Kolom :attribute yang dipilih tidak valid.', + 'file' => 'Kolom :attribute harus berupa file.', + 'filled' => 'Kolom :attribute harus memiliki nilai.', + 'gt' => [ + 'numeric' => 'Kolom :attribute harus lebih besar dari :value.', + 'file' => 'Kolom :attribute harus lebih besar dari :value kilobyte.', + 'string' => 'Kolom :attribute harus lebih besar dari :value karakter.', + 'array' => 'Kolom :attribute harus memiliki lebih dari :value item.', + ], + 'gte' => [ + 'numeric' => 'Kolom :attribute harus lebih besar dari atau sama dengan :value.', + 'file' => 'Kolom :attribute harus lebih besar dari atau sama dengan :value kilobyte.', + 'string' => 'Kolom :attribute harus lebih besar dari atau sama dengan :value karakter.', + 'array' => 'Kolom :attribute harus memiliki :value item atau lebih.', + ], + 'image' => 'Kolom :attribute harus berupa gambar.', + 'in' => 'Kolom :attribute yang dipilih tidak valid.', + 'in_array' => 'Kolom :attribute harus ada di :other.', + 'integer' => 'Kolom :attribute harus berupa bilangan bulat.', + 'ip' => 'Kolom :attribute harus berupa alamat IP yang valid.', + 'ipv4' => 'Kolom :attribute harus berupa alamat IPv4 yang valid.', + 'ipv6' => 'Kolom :attribute harus berupa alamat IPv6 yang valid.', + 'json' => 'Kolom :attribute harus berupa string JSON yang valid.', + 'lt' => [ + 'numeric' => 'Kolom :attribute harus kurang dari :value.', + 'file' => 'Kolom :attribute harus kurang dari :value kilobyte.', + 'string' => 'Kolom :attribute harus kurang dari :value karakter.', + 'array' => 'Kolom :attribute harus memiliki kurang dari :value item.', + ], + 'lte' => [ + 'numeric' => 'Kolom :attribute harus kurang dari atau sama dengan :value.', + 'file' => 'Kolom :attribute harus kurang dari atau sama dengan :value kilobyte.', + 'string' => 'Kolom :attribute harus kurang dari atau sama dengan :value karakter.', + 'array' => 'Kolom :attribute tidak boleh memiliki lebih dari :value item.', + ], + 'max' => [ + 'numeric' => 'Kolom :attribute tidak boleh lebih besar dari :max.', + 'file' => 'Kolom :attribute tidak boleh lebih besar dari :max kilobyte.', + 'string' => 'Kolom :attribute tidak boleh lebih dari :max karakter.', + 'array' => 'Kolom :attribute tidak boleh memiliki lebih dari :max item.', + ], + 'mimes' => 'Kolom :attribute harus berupa file bertipe: :values.', + 'mimetypes' => 'Kolom :attribute harus berupa file bertipe: :values.', + 'min' => [ + 'numeric' => 'Kolom :attribute harus minimal :min.', + 'file' => 'Kolom :attribute harus minimal :min kilobyte.', + 'string' => 'Kolom :attribute harus minimal :min karakter.', + 'array' => 'Kolom :attribute harus memiliki minimal :min item.', + ], + 'multiple_of' => 'Kolom :attribute harus merupakan kelipatan dari :value.', + 'not_in' => 'Kolom :attribute yang dipilih tidak valid.', + 'not_regex' => 'Format kolom :attribute tidak valid.', + 'numeric' => 'Kolom :attribute harus berupa angka.', + 'password' => 'Password salah.', + 'present' => 'Kolom :attribute harus ada.', + 'regex' => 'Format kolom :attribute tidak valid.', + 'required' => 'Kolom :attribute wajib diisi.', + 'required_if' => 'Kolom :attribute wajib diisi ketika :other adalah :value.', + 'required_unless' => 'Kolom :attribute wajib diisi kecuali :other adalah :values.', + 'required_with' => 'Kolom :attribute wajib diisi ketika :values ada.', + 'required_with_all' => 'Kolom :attribute wajib diisi ketika :values ada.', + 'required_without' => 'Kolom :attribute wajib diisi ketika :values tidak ada.', + 'required_without_all' => 'Kolom :attribute wajib diisi ketika tidak ada dari :values yang ada.', + 'same' => 'Kolom :attribute dan :other harus cocok.', + 'size' => [ + 'numeric' => 'Kolom :attribute harus :size.', + 'file' => 'Kolom :attribute harus :size kilobyte.', + 'string' => 'Kolom :attribute harus :size karakter.', + 'array' => 'Kolom :attribute harus mengandung :size item.', + ], + 'starts_with' => 'Kolom :attribute harus dimulai dengan salah satu dari: :values.', + 'string' => 'Kolom :attribute harus berupa string.', + 'timezone' => 'Kolom :attribute harus berupa timezone yang valid.', + 'unique' => 'Nilai :attribute sudah terdaftar.', + 'uploaded' => 'Upload kolom :attribute gagal.', + 'url' => 'Format kolom :attribute tidak valid.', + 'uuid' => 'Kolom :attribute harus berupa UUID yang valid.', + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This allows you to quickly + | specify a custom validation message for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap our attribute names with + | something more reader friendly such as Email Address instead of "email". + | This simply helps us make our message more expressive and easier to read. + | + */ + + 'attributes' => [ + 'name' => 'nama', + 'email' => 'email', + 'password' => 'password', + 'password_confirmation' => 'konfirmasi password', + 'nis' => 'NIS', + 'kelompok_asal' => 'kelompok asal', + 'minat' => 'minat', + 'cita_cita' => 'cita-cita', + 'pref_studi' => 'preferensi pembelajaran', + 'prestasi' => 'prestasi', + 'mtk' => 'nilai matematika', + 'fisika' => 'nilai fisika', + 'kimia' => 'nilai kimia', + 'biologi' => 'nilai biologi', + 'ekonomi' => 'nilai ekonomi', + 'geografi' => 'nilai geografi', + 'sosiologi' => 'nilai sosiologi', + 'sejarah' => 'nilai sejarah', + 'foto' => 'foto', + 'current_password' => 'password saat ini', + ], +]; diff --git a/resources/views/admin/chat-history.blade.php b/resources/views/admin/chat-history.blade.php new file mode 100644 index 0000000..240eb17 --- /dev/null +++ b/resources/views/admin/chat-history.blade.php @@ -0,0 +1,73 @@ +@extends('admin.layouts.app') + +@section('title', 'Chat History - ' . ($user->name ?? 'Unknown')) + +@section('content') +
+
+

💬 Chat History

+

{{ $user->name ?? 'Unknown' }}

+
+ + ← Profil Siswa + +
+ + +
+
+

Total Chat

+

{{ $chatHistories->count() }}

+
+
+

Pertanyaan

+

{{ $chatHistories->count() }}

+
+
+

Jawaban

+

{{ $chatHistories->count() }}

+
+
+ + +
+ @forelse($chatHistories->sortBy('created_at') as $chat) +
+

+ {{ $chat->created_at->format('d/m/Y H:i') }} +

+ + +
+
+

Siswa 📤

+

{{ $chat->prompt }}

+
+
+ + +
+
+

Konselor BK 📥

+

{{ $chat->response }}

+
+
+
+ + @if(!$loop->last) +
+ @endif + @empty +
+

📭 Belum ada chat history untuk siswa ini

+
+ @endforelse +
+ + +
+

+ ℹ️ Info: Chat history menunjukkan interaksi siswa dengan Konselor BK AI +

+
+@endsection diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php new file mode 100644 index 0000000..dad1093 --- /dev/null +++ b/resources/views/admin/dashboard.blade.php @@ -0,0 +1,136 @@ +@extends('admin.layouts.app') + +@section('title', 'Dashboard') + +@section('content') + +
+
+

👥 Total Siswa

+

{{ $totalSiswa }}

+
+
+

🎯 Total Rekomendasi

+

{{ $totalRekomendasi }}

+
+
+

💬 Chat History

+

{{ $totalChatHistory }}

+
+
+

🎓 Jurusan Tersedia

+

{{ $totalJurusan }}

+
+
+ + +
+
+

📊 Siswa per Kelompok

+
+ @foreach($kelompokStats as $stat) +
+ {{ $stat->kelompok_asal ?? 'Tidak Ada' }} +
+
+ {{ $stat->count }} +
+
+
+ @endforeach +
+
+ + +
+

🎯 Top Recommended Majors

+ @if($topMajors->isNotEmpty()) +
+ @foreach($topMajors as $major) +
+ {{ $major->major_name }} + {{ $major->count }} +
+ @endforeach +
+ @else +

Belum ada data rekomendasi

+ @endif +
+
+ + +
+

👥 Siswa Terbaru

+ @if($recentStudents->isNotEmpty()) +
+ + + + + + + + + + + @foreach($recentStudents as $student) + + + + + + + @endforeach + +
NamaNISKelompokAksi
{{ $student->name }}{{ $student->nis ?? '-' }} + + {{ $student->kelompok_asal ?? '-' }} + + + 👁 Lihat +
+
+ @else +

Belum ada siswa terdaftar

+ @endif +
+ + +
+

🎯 Rekomendasi Terbaru

+ @if($recentRecommendations->isNotEmpty()) +
+ + + + + + + + + + + @foreach($recentRecommendations as $rec) + @php + $topJurusan = $rec->hasil_rekomendasi[0]['jurusan'] ?? '-'; + $topSkor = round(($rec->hasil_rekomendasi[0]['skor'] ?? 0) * 100, 1); + @endphp + + + + + + + @endforeach + +
SiswaTop RekomendasiSkorTanggal
{{ $rec->user->name }}{{ $topJurusan }} + {{ $topSkor }}% + {{ $rec->created_at->format('d M Y') }}
+
+ @else +

Belum ada rekomendasi

+ @endif +
+@endsection diff --git a/resources/views/admin/guru-bk/create.blade.php b/resources/views/admin/guru-bk/create.blade.php new file mode 100644 index 0000000..b1f59c4 --- /dev/null +++ b/resources/views/admin/guru-bk/create.blade.php @@ -0,0 +1,65 @@ +@extends('admin.layouts.app') + +@section('title', 'Tambah Guru BK') + +@section('content') +
+
+

➕ Tambah Akun Guru BK

+

Buat akun baru untuk guru BK

+
+ + ← Kembali + +
+ +
+
+ @csrf +
+ + + @error('name') {{ $message }} @enderror +
+
+ + + @error('email') {{ $message }} @enderror +
+
+ +
+ + +
+ @error('password') {{ $message }} @enderror +
+
+ +
+ + +
+
+ +
+ + + Batal + +
+
+
+ + @section('scripts') + + @endsection +@endsection diff --git a/resources/views/admin/guru-bk/edit.blade.php b/resources/views/admin/guru-bk/edit.blade.php new file mode 100644 index 0000000..e8111d7 --- /dev/null +++ b/resources/views/admin/guru-bk/edit.blade.php @@ -0,0 +1,66 @@ +@extends('admin.layouts.app') + +@section('title', 'Edit Guru BK') + +@section('content') +
+
+

✏️ Edit Akun Guru BK

+

{{ $guruBK->name }}

+
+ + ← Kembali + +
+ +
+
+ @csrf + @method('PUT') +
+ + + @error('name') {{ $message }} @enderror +
+
+ + + @error('email') {{ $message }} @enderror +
+
+ +
+ + +
+ @error('password') {{ $message }} @enderror +
+
+ +
+ + +
+
+ +
+ + + Batal + +
+
+
+ + @section('scripts') + + @endsection +@endsection diff --git a/resources/views/admin/guru-bk/index.blade.php b/resources/views/admin/guru-bk/index.blade.php new file mode 100644 index 0000000..49c3555 --- /dev/null +++ b/resources/views/admin/guru-bk/index.blade.php @@ -0,0 +1,62 @@ +@extends('admin.layouts.app') + +@section('title', 'Manajemen Akun Guru BK') + +@section('content') +
+
+

👨‍🏫 Manajemen Akun Guru BK

+

Kelola daftar akun guru BK yang memiliki akses sistem

+
+ + + Tambah Guru BK + +
+ + +
+ + + + + + + + + + + + @forelse($guruBK as $index => $guru) + + + + + + + + @empty + + + + @endforelse + +
NoNamaEmailTerdaftarAksi
{{ $guruBK->firstItem() + $index }}{{ $guru->name }}{{ $guru->email }}{{ $guru->created_at->format('d M Y') }} + + Edit + +
+ @csrf + @method('DELETE') + +
+
+ 📭 Belum ada akun guru BK. Tekan "+ Tambah Guru BK" untuk menambahkan. +
+
+ + @if($guruBK->hasPages()) +
{{ $guruBK->links() }}
+ @endif +@endsection diff --git a/resources/views/admin/jurusan/create.blade.php b/resources/views/admin/jurusan/create.blade.php new file mode 100644 index 0000000..05ae23e --- /dev/null +++ b/resources/views/admin/jurusan/create.blade.php @@ -0,0 +1,121 @@ +@extends('admin.layouts.app') + +@section('title', 'Tambah Jurusan') + +@section('content') +
+
+

➕ Tambah Jurusan Baru

+

Tambahkan jurusan baru ke dalam sistem rekomendasi

+
+ + ← Kembali + +
+ + @if($errors->any()) +
+ +
+ @endif + +
+ @csrf + +
+

📋 Informasi Jurusan

+
+
+ + +
+ +
+ + +
+ +
+ + +

Keywords digunakan untuk mencocokkan minat dan cita-cita siswa dengan jurusan ini. Pisahkan dengan koma.

+
+ +
+ + +

Rumpun bidang studi yang cocok untuk jurusan ini. Pilihan: Sains & Teknologi, Pertanian & Lingkungan, Kesehatan & Ilmu Hayat, Bisnis & Manajemen, Sosial & Humaniora

+
+ +
+ + +
+
+
+ + +
+

⚖️ Bobot Mata Pelajaran

+

Tentukan bobot setiap mata pelajaran untuk jurusan ini (0.00 - 1.00). Mata pelajaran yang lebih relevan diberi bobot lebih tinggi. Jumlah total tidak harus 1.0.

+ +
+
+

📐 IPA

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+

📊 IPS

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+ + + Batal + +
+
+@endsection diff --git a/resources/views/admin/jurusan/edit.blade.php b/resources/views/admin/jurusan/edit.blade.php new file mode 100644 index 0000000..af881e6 --- /dev/null +++ b/resources/views/admin/jurusan/edit.blade.php @@ -0,0 +1,132 @@ +@extends('admin.layouts.app') + +@section('title', 'Edit Jurusan') + +@section('content') +
+
+

✏️ Edit Jurusan: {{ $jurusan->nama_jurusan }}

+

Ubah informasi jurusan

+
+ + ← Kembali + +
+ + @if($errors->any()) +
+ +
+ @endif + +
+ @csrf + @method('PUT') + +
+

📋 Informasi Jurusan

+
+
+ + +
+ +
+ + +
+ +
+ + +

Keywords digunakan untuk mencocokkan minat dan cita-cita siswa dengan jurusan ini. Pisahkan dengan koma.

+ @if(!empty($jurusan->keywords)) +
+ @foreach($jurusan->keywords as $kw) + {{ $kw }} + @endforeach +
+ @endif +
+ +
+ + +

Rumpun bidang studi yang cocok: Sains & Teknologi, Pertanian & Lingkungan, Kesehatan & Ilmu Hayat, Bisnis & Manajemen, Sosial & Humaniora

+
+ +
+ + +
+
+
+ + + @php + $bobot = $jurusan->bobot_mapel ?? []; + @endphp +
+

⚖️ Bobot Mata Pelajaran

+

Tentukan bobot setiap mata pelajaran untuk jurusan ini (0.00 - 1.00). Mata pelajaran yang lebih relevan diberi bobot lebih tinggi. Jumlah total tidak harus 1.0.

+ +
+
+

📐 IPA

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+

📊 IPS

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+ + + Batal + +
+
+@endsection diff --git a/resources/views/admin/jurusan/index.blade.php b/resources/views/admin/jurusan/index.blade.php new file mode 100644 index 0000000..89c8371 --- /dev/null +++ b/resources/views/admin/jurusan/index.blade.php @@ -0,0 +1,121 @@ +@extends('admin.layouts.app') + +@section('title', 'Manajemen Jurusan') + +@section('content') + +
+
+

🎓 Manajemen Jurusan

+

Kelola data jurusan Polije (tambah, edit, hapus)

+
+ + + Tambah Jurusan + +
+ + @if(session('success')) +
+

✅ {{ session('success') }}

+
+ @endif + + @if(session('error')) +
+

❌ {{ session('error') }}

+
+ @endif + + +
+ + + + + + + + + + + + @forelse($jurusanList as $index => $jurusan) + + + + + + + + @empty + + + + @endforelse + +
NoNama JurusanAksi
{{ $index + 1 }} +

{{ $jurusan->nama_jurusan }}

+ @if($jurusan->deskripsi) +

{{ Str::limit($jurusan->deskripsi, 80) }}

+ @endif +
+
+ + ✏️ Edit + +
+ @csrf + @method('DELETE') + +
+
+
+ Belum ada data jurusan. Tambah jurusan pertama. +
+
+ + +
+

📊 Kriteria Penilaian Rekomendasi

+

Sistem menggunakan 5 kriteria utama untuk memberikan rekomendasi jurusan yang tepat:

+
+
+

📝 Nilai Akademik (40%)

+

IPA: MTK, Fisika, Kimia, Biologi
IPS: Ekonomi, Geografi, Sosiologi, Sejarah

+
+
+

💡 Minat & Bakat (35%)

+

Dicocokkan dengan keywords jurusan secara graduated

+
+
+

🎯 Preferensi Studi (15%)

+

Praktik Langsung, DuDi, Project Based, Blended Learning

+
+
+

🏆 Prestasi (5%)

+

Prestasi akademik dan non-akademik siswa

+
+
+

💼 Cita-cita (5%)

+

Dicocokkan dengan keywords jurusan secara graduated

+
+
+
+@endsection diff --git a/resources/views/admin/layouts/app.blade.php b/resources/views/admin/layouts/app.blade.php new file mode 100644 index 0000000..21335e3 --- /dev/null +++ b/resources/views/admin/layouts/app.blade.php @@ -0,0 +1,180 @@ + + + + + + + @yield('title', 'Admin Panel') - SPK Jurusan Kuliah + + + @yield('styles') + + + +
+
+
+
+ + +
+

🔧 Admin Panel

+

Sistem Pemilihan Jurusan Kuliah

+
+
+
+ + +
+
+
+
+ +
+ + + + + + + +
+ + @if(session('success')) +
+

✅ {{ session('success') }}

+
+ @endif + @if(session('error')) +
+

❌ {{ session('error') }}

+
+ @endif + + @yield('content') +
+
+ + + + @yield('scripts') + + diff --git a/resources/views/admin/profil/index.blade.php b/resources/views/admin/profil/index.blade.php new file mode 100644 index 0000000..80cb47f --- /dev/null +++ b/resources/views/admin/profil/index.blade.php @@ -0,0 +1,87 @@ +@extends('admin.layouts.app') + +@section('title', 'Profil Admin') + +@section('content') +
+

⚙️ Profil Admin

+

Kelola informasi akun administrator

+
+ +
+ +
+

👤 Informasi Akun

+
+ @csrf + @method('PUT') +
+ + + @error('name') {{ $message }} @enderror +
+
+ + + @error('email') {{ $message }} @enderror +
+
+ + +
+
+ + +
+ +
+
+ + +
+

🔒 Ubah Password

+
+ @csrf + @method('PUT') +
+ +
+ + +
+ @error('current_password') {{ $message }} @enderror +
+
+ +
+ + +
+ @error('password') {{ $message }} @enderror +
+
+ +
+ + +
+
+ +
+
+
+@endsection + +@section('scripts') + +@endsection diff --git a/resources/views/admin/riwayat-chatbot/index.blade.php b/resources/views/admin/riwayat-chatbot/index.blade.php new file mode 100644 index 0000000..5f22383 --- /dev/null +++ b/resources/views/admin/riwayat-chatbot/index.blade.php @@ -0,0 +1,92 @@ +@extends('admin.layouts.app') + +@section('title', 'Riwayat Konsultasi Chatbot') + +@section('content') +
+
+

💬 Riwayat Konsultasi Chatbot

+

Seluruh riwayat konsultasi siswa dengan AI Konselor BK

+
+
+ + +
+
+

Total Percakapan

+

{{ $chatHistories->total() }}

+
+
+

Siswa Unik

+

{{ $uniqueStudents }}

+
+
+

Hari Ini

+

{{ $todayCount }}

+
+
+ + +
+
+ + + @if(request('search')) + + Reset + + @endif +
+
+ + +
+ + + + + + + + + + + + + @forelse($chatHistories as $idx => $chat) + + + + + + + + + @empty + + + + @endforelse + +
NoNama SiswaPertanyaanJawaban AITanggalAksi
{{ $chatHistories->firstItem() + $idx }}{{ $chat->user->name ?? 'Deleted User' }} +
+ {{ \Illuminate\Support\Str::limit($chat->prompt, 80) }} +
+
+
+ {{ \Illuminate\Support\Str::limit($chat->response, 80) }} +
+
{{ $chat->created_at->format('d M Y H:i') }} + 👁 Detail Siswa +
+ Belum ada data konsultasi chatbot +
+
+ + +
+ {{ $chatHistories->withQueryString()->links() }} +
+@endsection diff --git a/resources/views/admin/riwayat-rekomendasi/index.blade.php b/resources/views/admin/riwayat-rekomendasi/index.blade.php new file mode 100644 index 0000000..9130ce7 --- /dev/null +++ b/resources/views/admin/riwayat-rekomendasi/index.blade.php @@ -0,0 +1,108 @@ +@extends('admin.layouts.app') + +@section('title', 'Riwayat Rekomendasi Siswa') + +@section('content') +
+
+

🎯 Riwayat Rekomendasi Siswa

+

Seluruh hasil rekomendasi jurusan yang pernah dilakukan siswa

+
+
+ + +
+
+

Total Rekomendasi

+

{{ $recommendations->total() }}

+
+
+

Siswa Unik

+

{{ $uniqueStudents }}

+
+
+

Jurusan Terpopuler

+

{{ $topMajor ?? '-' }}

+
+
+ + +
+
+ + + @if(request('search')) + + Reset + + @endif +
+
+ + +
+ + + + + + + + + + + + + + @forelse($recommendations as $idx => $rec) + + + + + + + + + + @empty + + + + @endforelse + +
NoNama SiswaKelompokMinatTop 3 RekomendasiTanggalAksi
{{ $recommendations->firstItem() + $idx }}{{ $rec->user->name ?? 'Deleted User' }} + @if($rec->user && $rec->user->kelompok_asal) + + {{ $rec->user->kelompok_asal }} + + @else + - + @endif + {{ \Illuminate\Support\Str::limit($rec->minat, 30) }} + @if($rec->hasil_rekomendasi && is_array($rec->hasil_rekomendasi)) +
+ @foreach(array_slice($rec->hasil_rekomendasi, 0, 3) as $i => $hasil) +
+ {{ $i + 1 }} + {{ $hasil['jurusan'] ?? 'N/A' }} + {{ round(($hasil['skor'] ?? 0) * 100, 1) }}% +
+ @endforeach +
+ @else + - + @endif +
{{ $rec->created_at->format('d M Y H:i') }} + 👁 Detail Siswa +
+ Belum ada data rekomendasi +
+
+ + +
+ {{ $recommendations->withQueryString()->links() }} +
+@endsection diff --git a/resources/views/admin/students/detail.blade.php b/resources/views/admin/students/detail.blade.php new file mode 100644 index 0000000..4c337bf --- /dev/null +++ b/resources/views/admin/students/detail.blade.php @@ -0,0 +1,131 @@ +@extends('admin.layouts.app') + +@section('title', 'Detail Siswa - ' . $student->name) + +@section('content') +
+
+

👤 Detail Siswa

+

{{ $student->name }}

+
+ + ← Kembali + +
+ + +
+
+
+

Nama

+

{{ $student->name }}

+
+
+

Email

+

{{ $student->email }}

+
+
+

NIS

+

{{ $student->nis ?? '-' }}

+
+
+

Kelompok Asal

+ @if($student->kelompok_asal) +

+ + {{ $student->kelompok_asal }} + +

+ @else +

-

+ @endif +
+
+

Foto Profil

+ @if($student->foto) + {{ $student->name }} + @else +

-

+ @endif +
+
+

Terdaftar

+

{{ $student->created_at->format('d M Y H:i') }}

+
+
+
+ + +
+

🎯 Riwayat Rekomendasi ({{ count($recommendations) }})

+ + @if($recommendations->isNotEmpty()) +
+ @foreach($recommendations as $rec) +
+
+

{{ $rec->created_at->format('d M Y H:i') }}

+ Rekomendasi #{{ $loop->index + 1 }} +
+ + @if($rec->hasil_rekomendasi && is_array($rec->hasil_rekomendasi)) +
+

Top 3 Rekomendasi:

+
+ @foreach(array_slice($rec->hasil_rekomendasi, 0, 3) as $idx => $hasil) +
+ {{ $idx + 1 }}. {{ $hasil['jurusan'] ?? 'N/A' }} + + {{ round(($hasil['skor'] ?? 0) * 100, 1) }}% + +
+ @endforeach +
+
+ @endif + +
+

Minat: {{ $rec->minat ?? '-' }} | Cita-cita: {{ $rec->cita_cita ?? '-' }}

+
+
+ @endforeach +
+ @else +

Siswa belum melakukan rekomendasi

+ @endif +
+ + +
+
+

💬 Chat History ({{ count($chatHistories) }})

+ @if(count($chatHistories) > 0) + + Lihat Semua → + + @endif +
+ + @if($chatHistories->isNotEmpty()) +
+ @foreach($chatHistories as $chat) +
+
+

{{ $chat->created_at->format('d M Y H:i') }}

+
+
+

👤 Pertanyaan Siswa:

+

{{ \Illuminate\Support\Str::limit($chat->prompt, 150) }}

+
+
+

🤖 Jawaban AI:

+

{{ \Illuminate\Support\Str::limit($chat->response, 150) }}

+
+
+ @endforeach +
+ @else +

Siswa belum melakukan chat dengan AI

+ @endif +
+@endsection diff --git a/resources/views/admin/students/index.blade.php b/resources/views/admin/students/index.blade.php new file mode 100644 index 0000000..96aa7b1 --- /dev/null +++ b/resources/views/admin/students/index.blade.php @@ -0,0 +1,91 @@ +@extends('admin.layouts.app') + +@section('title', 'Manajemen Data Siswa') + +@section('content') +
+
+

👥 Manajemen Data Siswa

+

Total: {{ $students->total() }} Siswa

+
+
+ + +
+
+ + + + @if(request('search') || request('kelompok')) + + Reset + + @endif +
+
+ + +
+ + + + + + + + + + + + + + @forelse($students as $student) + + + + + + + + + + @empty + + + + @endforelse + +
NamaEmailNISKelompokRekomendasiChatAksi
{{ $student->name }}{{ $student->email }}{{ $student->nis ?? '-' }} + @if($student->kelompok_asal) + + {{ $student->kelompok_asal }} + + @else + - + @endif + + + {{ $student->recommendations_count }} + + + + {{ $student->chat_histories_count }} + + + 👁 Lihat +
+ Tidak ada siswa yang ditemukan +
+
+ + +
+ {{ $students->withQueryString()->links() }} +
+@endsection diff --git a/resources/views/alumni/create.blade.php b/resources/views/alumni/create.blade.php new file mode 100644 index 0000000..a24caea --- /dev/null +++ b/resources/views/alumni/create.blade.php @@ -0,0 +1,236 @@ + + + + + + Input Alumni Baru - Sistem Pemilihan Jurusan + + + + + +
+
+
+

Input Alumni Baru

+

Tambah data alumni untuk validasi algoritma

+
+ + ← Kembali + +
+
+ + +
+ @if($errors->any()) +
+

❌ Ada kesalahan validasi:

+ +
+ @endif + +
+ @csrf + + +
+

📋 Data Dasar Alumni

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

📊 Nilai Saat Entry (Rapor)

+
+
+ + +
+ + +
+
+ + +
+

💡 Variabel Non-Akademik

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

🎯 Rekomendasi & Hasil Algoritma

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

🎓 Outcome Alumni

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+ + Batal + + +
+
+
+ + + + diff --git a/resources/views/alumni/edit.blade.php b/resources/views/alumni/edit.blade.php new file mode 100644 index 0000000..910a857 --- /dev/null +++ b/resources/views/alumni/edit.blade.php @@ -0,0 +1,235 @@ + + + + + + Edit Alumni - Sistem Pemilihan Jurusan + + + + + +
+
+
+

Edit Alumni

+

{{ $alumnus->nama_alumni }}

+
+ + ← Kembali + +
+
+ + +
+ @if($errors->any()) +
+

❌ Ada kesalahan validasi:

+ +
+ @endif + +
+ @csrf @method('PUT') + + +
+

📋 Data Dasar Alumni

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

📊 Nilai Saat Entry (Rapor)

+
+
+ + +
+ + +
+
+ + +
+

💡 Variabel Non-Akademik

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

🎯 Rekomendasi & Hasil Algoritma

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

🎓 Outcome Alumni

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+ + Batal + + +
+
+
+ + + + diff --git a/resources/views/alumni/index.blade.php b/resources/views/alumni/index.blade.php new file mode 100644 index 0000000..b5299d5 --- /dev/null +++ b/resources/views/alumni/index.blade.php @@ -0,0 +1,182 @@ + + + + + + Data Alumni - Sistem Pemilihan Jurusan + + + + + +
+
+
+
+

Data Alumni

+

Validasi & Analisis Bobot Algoritma

+
+ + ➕ Input Alumni Baru + +
+
+
+ + +
+ + @if(session('success')) +
+ {{ session('success') }} +
+ @endif + + + @if($summary) +
+
+

Total Alumni

+

{{ $summary['total'] }}

+
+ + @if($summary['prediction_accuracy']) +
+

Top-1 Accuracy

+

{{ $summary['prediction_accuracy']['top_1'] }}%

+
+
+

Top-3 Accuracy

+

{{ $summary['prediction_accuracy']['top_3'] }}%

+
+
+

Top-5 Accuracy

+

{{ $summary['prediction_accuracy']['top_5'] }}%

+
+ @endif +
+ + + @if($summary['by_major']->isNotEmpty()) +
+

Distribusi Alumni per Jurusan

+
+ @foreach($summary['by_major'] as $major) +
+ {{ $major->major_masuk }} +
+
+ {{ $major->count }} +
+
+
+ @endforeach +
+
+ @endif + @endif + + +
+ + + + + + + + + + + + + + + @forelse($alumni as $a) + + + + + + + + + + + @empty + + + + @endforelse + +
NamaNISKelompokNilai RataMajorRankingSuccessAksi
{{ $a->nama_alumni }}{{ $a->nis ?? '-' }} + + {{ $a->kelompok_asal }} + + {{ $a->nilai_rata_rata ? number_format($a->nilai_rata_rata, 2) : '-' }}{{ $a->major_masuk }} + @if($a->ranking_saat_rekomendasi) + #{{ $a->ranking_saat_rekomendasi }} + @else + - + @endif + + @if($a->success_status) + @switch($a->success_status) + @case('sangat_sukses') + ✓ Sangat + @break + @case('sukses') + ✓ Sukses + @break + @case('cukup') + • Cukup + @break + @case('kurang_sukses') + ✗ Kurang + @break + @endswitch + @else + - + @endif + + 👁 Lihat + ✏ Edit +
+ @csrf @method('DELETE') + +
+
+ Belum ada data alumni. Tambah sekarang +
+
+ + +
+ {{ $alumni->links() }} +
+ + +
+

+ 📊 Data Alumni digunakan untuk:
+ 1. Validasi akurasi algoritma Naive Bayes
+ 2. Analisis faktor-faktor mana yang paling berpengaruh terhadap kesuksesan
+ 3. Re-weighting: menyesuaikan bobot jika data menunjukkan faktor lain lebih penting +

+
+
+ + diff --git a/resources/views/alumni/show.blade.php b/resources/views/alumni/show.blade.php new file mode 100644 index 0000000..673829f --- /dev/null +++ b/resources/views/alumni/show.blade.php @@ -0,0 +1,229 @@ + + + + + + Detail Alumni - {{ $alumnus->nama_alumni }} + + + + + +
+
+
+

Detail Alumni

+

{{ $alumnus->nama_alumni }}

+
+ + ← Kembali + +
+
+ + +
+
+ +
+
+
+

Nama

+

{{ $alumnus->nama_alumni }}

+
+
+

NIS

+

{{ $alumnus->nis ?? '-' }}

+
+
+

Kelompok Asal

+

+ + {{ $alumnus->kelompok_asal }} + +

+
+
+

Tahun Masuk

+

{{ $alumnus->tahun_masuk }}

+
+
+
+ + +
+

📊 Input Data Saat Entry

+
+ @if($alumnus->mtk) +
+

Matematika

+

{{ $alumnus->mtk }}

+
+ @endif + @if($alumnus->fisika) +
+

Fisika

+

{{ $alumnus->fisika }}

+
+ @endif + @if($alumnus->kimia) +
+

Kimia

+

{{ $alumnus->kimia }}

+
+ @endif + @if($alumnus->biologi) +
+

Biologi

+

{{ $alumnus->biologi }}

+
+ @endif + @if($alumnus->ekonomi) +
+

Ekonomi

+

{{ $alumnus->ekonomi }}

+
+ @endif + @if($alumnus->geografi) +
+

Geografi

+

{{ $alumnus->geografi }}

+
+ @endif + @if($alumnus->sosiologi) +
+

Sosiologi

+

{{ $alumnus->sosiologi }}

+
+ @endif + @if($alumnus->sejarah) +
+

Sejarah

+

{{ $alumnus->sejarah }}

+
+ @endif +
+
+

Nilai Rata-rata

+

{{ $alumnus->nilai_rata_rata ? number_format($alumnus->nilai_rata_rata, 2) : '-' }}

+
+
+ + +
+

💡 Variabel Non-Akademik

+
+
+

Minat

+

{{ $alumnus->minat ?? '-' }}

+
+
+

Cita-cita

+

{{ $alumnus->cita_cita ?? '-' }}

+
+
+

Preferensi Studi

+

{{ $alumnus->preferensi_studi ?? '-' }}

+
+
+

Prestasi

+

{{ $alumnus->prestasi ?? '-' }}

+
+
+
+ + +
+

🎯 Hasil Rekomendasi & Outcome

+
+
+

Jurusan Masuk

+

{{ $alumnus->major_masuk }}

+
+
+

Ranking Saat Rekomendasi

+ @if($alumnus->ranking_saat_rekomendasi) +

{{ $alumnus->ranking_saat_rekomendasi }} / 9

+ @else +

-

+ @endif +
+
+
+

Score Prediksi

+

{{ $alumnus->predicted_score ? round($alumnus->predicted_score * 100) . '%' : '-' }}

+
+
+ + +
+

🎓 Outcome Alumni

+
+
+
+

IPK Lulus

+

{{ $alumnus->ipk_lulus ?? '-' }}

+
+
+

Tahun Lulus

+

{{ $alumnus->tahun_lulus ?? '-' }}

+
+
+
+

Status Kesuksesan

+ @if($alumnus->success_status) + @switch($alumnus->success_status) + @case('sangat_sukses') + ✓ Sangat Sukses + @break + @case('sukses') + ✓ Sukses + @break + @case('cukup') + • Cukup + @break + @case('kurang_sukses') + ✗ Kurang Sukses + @break + @endswitch + @else +

-

+ @endif +
+
+

Karir Outcome

+

{{ $alumnus->karir_outcome ?? '-' }}

+
+
+

Catatan

+

{{ $alumnus->catatan ?? '-' }}

+
+
+
+ + +
+ + ✏ Edit + +
+ @csrf @method('DELETE') + +
+
+
+
+ + diff --git a/resources/views/auth/confirm-password.blade.php b/resources/views/auth/confirm-password.blade.php index 3d38186..836580f 100644 --- a/resources/views/auth/confirm-password.blade.php +++ b/resources/views/auth/confirm-password.blade.php @@ -1,6 +1,6 @@
- {{ __('This is a secure area of the application. Please confirm your password before continuing.') }} + {{ __('Ini adalah area aman aplikasi. Silakan konfirmasi password Anda sebelum melanjutkan.') }}
@@ -10,18 +10,28 @@
- +
+ + +
- {{ __('Confirm') }} + {{ __('Konfirmasi') }}
+ + @push('scripts') + + @endpush
diff --git a/resources/views/auth/forgot-password.blade.php b/resources/views/auth/forgot-password.blade.php index cb32e08..5d83561 100644 --- a/resources/views/auth/forgot-password.blade.php +++ b/resources/views/auth/forgot-password.blade.php @@ -1,6 +1,6 @@
- {{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }} + {{ __('Lupa password Anda? Tidak apa-apa. Beri tahu kami alamat email Anda dan kami akan mengirimkan tautan pengaturan ulang password yang akan memungkinkan Anda memilih yang baru.') }}
@@ -18,7 +18,7 @@
- {{ __('Email Password Reset Link') }} + {{ __('Kirim Tautan Atur Ulang Password') }}
diff --git a/resources/views/auth/login-new.blade.php b/resources/views/auth/login-new.blade.php index 95a631f..786a829 100644 --- a/resources/views/auth/login-new.blade.php +++ b/resources/views/auth/login-new.blade.php @@ -19,11 +19,11 @@ display: flex; height: 100vh; width: 100%; - background-color: #6B2C2C; + background-color: #5B7B89; } .left-section { width: 50%; - background-color: #6B2C2C; + background-color: #5B7B89; display: flex; align-items: center; justify-content: center; @@ -234,14 +234,18 @@ placeholder="Masukkan email Anda" /> -
+
- +
+ + +
+ + diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 07cc4a0..9bd8924 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -19,11 +19,11 @@ flex-direction: column; min-height: 100vh; width: 100%; - background-color: #6B2C2C; + background-color: #5B7B89; } .left-section { width: 100%; - background-color: #6B2C2C; + background-color: #5B7B89; display: flex; align-items: center; justify-content: center; @@ -305,14 +305,18 @@ placeholder="Masukkan email Anda" /> -
+
- +
+ + +
+ + diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php index 0e66c01..397f035 100644 --- a/resources/views/auth/register.blade.php +++ b/resources/views/auth/register.blade.php @@ -19,11 +19,11 @@ flex-direction: column; min-height: 100vh; width: 100%; - background-color: #6B2C2C; + background-color: #5B7B89; } .left-section { width: 100%; - background-color: #6B2C2C; + background-color: #5B7B89; display: flex; align-items: center; justify-content: center; @@ -353,25 +353,33 @@ -
+
- +
+ + +
-
+
- +
+ + +
+ + \ No newline at end of file diff --git a/resources/views/auth/reset-password.blade.php b/resources/views/auth/reset-password.blade.php index a6494cc..972ac5e 100644 --- a/resources/views/auth/reset-password.blade.php +++ b/resources/views/auth/reset-password.blade.php @@ -15,25 +15,37 @@
- +
+ + +
- - - +
+ + +
- {{ __('Reset Password') }} + {{ __('Atur Ulang Password') }}
+ + @push('scripts') + + @endpush
diff --git a/resources/views/auth/verify-email.blade.php b/resources/views/auth/verify-email.blade.php index eaf811d..d2fe7ef 100644 --- a/resources/views/auth/verify-email.blade.php +++ b/resources/views/auth/verify-email.blade.php @@ -1,11 +1,11 @@
- {{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }} + {{ __('Terima kasih telah mendaftar! Sebelum mulai, dapatkah Anda memverifikasi alamat email Anda dengan mengklik tautan yang baru saja kami kirimkan kepada Anda? Jika Anda tidak menerima email, kami akan dengan senang hati mengirimkan yang lain.') }}
@if (session('status') == 'verification-link-sent')
- {{ __('A new verification link has been sent to the email address you provided during registration.') }} + {{ __('Tautan verifikasi baru telah dikirim ke alamat email yang Anda berikan saat pendaftaran.') }}
@endif @@ -15,7 +15,7 @@
- {{ __('Resend Verification Email') }} + {{ __('Kirim Ulang Email Verifikasi') }}
@@ -24,7 +24,7 @@ @csrf diff --git a/resources/views/chatbot/index.blade.php b/resources/views/chatbot/index.blade.php index 258523d..95166d1 100644 --- a/resources/views/chatbot/index.blade.php +++ b/resources/views/chatbot/index.blade.php @@ -7,16 +7,16 @@ diff --git a/resources/views/profile/edit.blade.php b/resources/views/profile/edit.blade.php index b9b9ff8..6c99451 100644 --- a/resources/views/profile/edit.blade.php +++ b/resources/views/profile/edit.blade.php @@ -7,30 +7,30 @@ @@ -46,7 +46,7 @@
{{ Auth::user()->name }} - + ← Kembali ke Dashboard
@@ -162,8 +162,11 @@ class="input-focus w-full border border-gray-300 rounded-lg px-4 py-2 text-sm fo
- +
+ + +
@error('current_password', 'updatePassword')

{{ $message }}

@enderror @@ -171,8 +174,11 @@ class="input-focus w-full border border-gray-300 rounded-lg px-4 py-2 text-sm fo
- +
+ + +
@error('password', 'updatePassword')

{{ $message }}

@enderror @@ -180,8 +186,11 @@ class="input-focus w-full border border-gray-300 rounded-lg px-4 py-2 text-sm fo
- +
+ + +
@error('password_confirmation', 'updatePassword')

{{ $message }}

@enderror @@ -253,6 +262,12 @@ function previewFoto(input) { reader.readAsDataURL(input.files[0]); } } + + function togglePasswordVisibility(inputId, buttonElement) { + const input = document.getElementById(inputId); + const isPassword = input.type === 'password'; + input.type = isPassword ? 'text' : 'password'; + } diff --git a/resources/views/profile/partials/delete-user-form.blade.php b/resources/views/profile/partials/delete-user-form.blade.php index edeeb4a..4fc6244 100644 --- a/resources/views/profile/partials/delete-user-form.blade.php +++ b/resources/views/profile/partials/delete-user-form.blade.php @@ -1,18 +1,18 @@

- {{ __('Delete Account') }} + {{ __('Hapus Akun') }}

- {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }} + {{ __('Setelah akun Anda dihapus, semua sumber daya dan data akan dihapus secara permanen. Silakan unduh data atau informasi apa pun yang ingin Anda pertahankan sebelum menghapus akun Anda.') }}

{{ __('Delete Account') }} + >{{ __('Hapus Akun') }}
@@ -20,36 +20,48 @@ @method('delete')

- {{ __('Are you sure you want to delete your account?') }} + {{ __('Apakah Anda yakin ingin menghapus akun Anda?') }}

- {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }} + {{ __('Setelah akun Anda dihapus, semua sumber daya dan data akan dihapus secara permanen. Silakan masukkan password Anda untuk mengonfirmasi bahwa Anda ingin menghapus akun Anda secara permanen.') }}

- +
\n + +
- {{ __('Cancel') }} + {{ __('Batal') }} - {{ __('Delete Account') }} + {{ __('Hapus Akun') }}
+ +@push('scripts') + +@endpush diff --git a/resources/views/profile/partials/update-password-form.blade.php b/resources/views/profile/partials/update-password-form.blade.php index eaca1ac..994bcc4 100644 --- a/resources/views/profile/partials/update-password-form.blade.php +++ b/resources/views/profile/partials/update-password-form.blade.php @@ -1,11 +1,11 @@

- {{ __('Update Password') }} + {{ __('Perbarui Password') }}

- {{ __('Ensure your account is using a long, random password to stay secure.') }} + {{ __('Pastikan akun Anda menggunakan password yang panjang dan acak untuk tetap aman.') }}

@@ -15,19 +15,28 @@
- +
+ + +
- +
+ + +
- +
+ + +
@@ -46,3 +55,13 @@ class="text-sm text-gray-600"
+ +@push('scripts') + +@endpush diff --git a/resources/views/profile/partials/update-profile-information-form.blade.php b/resources/views/profile/partials/update-profile-information-form.blade.php index 165575b..b3457b3 100644 --- a/resources/views/profile/partials/update-profile-information-form.blade.php +++ b/resources/views/profile/partials/update-profile-information-form.blade.php @@ -1,11 +1,11 @@

- {{ __('Profile Information') }} + {{ __('Informasi Profil') }}

- {{ __("Update your account's profile information and email address.") }} + {{ __('Perbarui informasi profil dan alamat email akun Anda.') }}

@@ -77,7 +77,7 @@
- {{ __('Save') }} + {{ __('Simpan') }} @if (session('status') === 'profile-updated')

{{ __('Saved.') }}

+ >{{ __('Berhasil disimpan!') }}

@endif
diff --git a/resources/views/rekomendasi/hasil.blade.php b/resources/views/rekomendasi/hasil.blade.php index cc92346..87f8af9 100644 --- a/resources/views/rekomendasi/hasil.blade.php +++ b/resources/views/rekomendasi/hasil.blade.php @@ -7,19 +7,19 @@ @@ -56,27 +56,30 @@

Nilai Akademik

{{ $katNilai }}

+

Rata-rata: {{ number_format($average, 1) }}

-

Minat

-

{{ $minatMapped }}

-
-
-

Preferensi Belajar

+

Preferensi Studi

{{ $prefStudi }}

-
+

Prestasi

@if($prestasiScore >= 0.8) Tinggi @elseif($prestasiScore >= 0.6) Sedang + @elseif($prestasiScore > 0) + Cukup @else - Minimal + Belum Ada @endif

+
+

Skor Nilai

+

{{ number_format($average / 100 * 100, 1) }}%

+
@@ -139,7 +142,7 @@
@php $topRecommendation = $hasilAkhir[0]; - $criteria = config('polije.criteria.' . $topRecommendation['jurusan'], []); + $detail = $topRecommendation['detail'] ?? []; @endphp
@@ -149,6 +152,9 @@

Skor Kesesuaian: {{ number_format($topRecommendation['skor'] * 100, 1) }}%

+ @if($topJurusan && $topJurusan->deskripsi) +

{{ $topJurusan->deskripsi }}

+ @endif
@@ -160,48 +166,50 @@

Nilai Akademik (40%)

- {{ $katNilai }} + {{ number_format(($detail['nilai'] ?? 0) * 100, 1) }}%
-
+
-

Minat (35%)

- {{ $minatMapped }} +

Minat & Bakat (35%)

+ {{ number_format(($detail['minat'] ?? 0) * 100, 1) }}%
-
+
-

Preferensi Belajar (15%)

- {{ $prefStudi }} +

Preferensi Studi (15%)

+ {{ number_format(($detail['pref'] ?? 0) * 100, 1) }}%
-
+

Cita-cita (5%)

+ {{ number_format(($detail['cita'] ?? 0) * 100, 1) }}%
-
+

Prestasi (5%)

+ {{ number_format(($detail['prestasi'] ?? 0) * 100, 1) }}%
-
+
@@ -211,25 +219,18 @@

Penjelasan:

- Berdasarkan profil Anda dengan nilai akademik {{ $katNilai }}, - minat di bidang {{ $minatMapped }}, dan - preferensi belajar {{ $prefStudi }}, + Berdasarkan profil Anda dengan nilai akademik {{ $katNilai }} (rata-rata {{ number_format($average, 1) }}) + dan preferensi studi {{ $prefStudi }}, sistem menganalisis bahwa {{ $topRecommendation['jurusan'] }} adalah pilihan yang paling sesuai dengan skor {{ number_format($topRecommendation['skor'] * 100, 1) }}%.

- - - @if(isset($criteria['skills_required'])) + + + @if($topJurusan && $topJurusan->prospek_kerja)
-

Skills yang Diperlukan:

-
- @foreach($criteria['skills_required'] as $skill) - - {{ $skill }} - - @endforeach -
+

Prospek Kerja:

+

{{ $topJurusan->prospek_kerja }}

@endif @@ -265,7 +266,7 @@

- Metode: Sistem menggunakan Weighted Naive Bayes dengan 5 kriteria: Nilai (40%), Minat (35%), Preferensi (15%), Cita-cita (5%), Prestasi (5%). + Metode: Sistem menggunakan Graduated Scoring dengan 5 kriteria: Nilai Akademik (40%), Minat & Bakat (35%), Preferensi Studi (15%), Cita-cita (5%), Prestasi (5%). Setiap kriteria dihitung secara proporsional (0-100%) berdasarkan kecocokan keyword.

diff --git a/resources/views/rekomendasi/input.blade.php b/resources/views/rekomendasi/input.blade.php index 57c6cb9..8654392 100644 --- a/resources/views/rekomendasi/input.blade.php +++ b/resources/views/rekomendasi/input.blade.php @@ -7,20 +7,20 @@ @@ -29,7 +29,7 @@
-

Input Data Profil

+

Input Data Rekomendasi

Sistem Pemilihan Jurusan

@@ -52,7 +52,7 @@ Sistem akan menganalisis data Anda dan menampilkan ranking 9 jurusan yang tersedia di Politeknik Negeri Jember.

- +
@@ -63,100 +63,182 @@
+ @if ($errors->any()) +
+

❌ Kesalahan Validasi:

+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif +
@csrf -
- -
-

1. Nilai Rapor (0-100)

-

Masukkan nilai rata-rata mata pelajaran Anda.

+ {{-- ============================================ --}} + {{-- KRITERIA 1: NILAI MATA PELAJARAN --}} + {{-- ============================================ --}} +
+

1. Nilai Mata Pelajaran *

+

+ @if(isset($student) && $student->kelompok_asal == 'IPA') + Siswa IPA — Masukkan nilai rapor (0-100): Matematika, Fisika, Kimia, Biologi. + @else + Siswa IPS — Masukkan nilai rapor (0-100): Ekonomi, Geografi, Sosiologi, Sejarah. + @endif +

-
+ @if(isset($student) && $student->kelompok_asal == 'IPA') + {{-- SISWA IPA: Matematika, Fisika, Kimia, Biologi --}} +
- - + + + @error('mtk') + {{ $message }} + @enderror
+
+ + + @error('fisika') + {{ $message }} + @enderror +
+
+ + + @error('kimia') + {{ $message }} + @enderror +
+
+ + + @error('biologi') + {{ $message }} + @enderror +
+
+ @else + {{-- SISWA IPS: Ekonomi, Geografi, Sosiologi, Sejarah --}} +
+
+ + + @error('ekonomi') + {{ $message }} + @enderror +
+
+ + + @error('geografi') + {{ $message }} + @enderror +
+
+ + + @error('sosiologi') + {{ $message }} + @enderror +
+
+ + + @error('sejarah') + {{ $message }} + @enderror +
+
+ @endif +
- @if(isset($student) && $student->kelompok_asal == 'IPA') -
-
- - -
-
- - -
-
- - -
-
- @else -
-
- - -
-
- - -
-
- - -
-
- - -
-
- @endif +
+ {{-- ============================================ --}} + {{-- KRITERIA 2: MINAT SISWA --}} + {{-- ============================================ --}} +
+

2. Minat Siswa *

+

Tuliskan bidang atau kegiatan yang Anda minati / sukai.

+
+ + +

Pisahkan dengan koma jika lebih dari satu minat

+ @error('minat') + {{ $message }} + @enderror
- + {{-- ============================================ --}} + {{-- KRITERIA 3: PREFERENSI STUDI LANJUTAN --}} + {{-- ============================================ --}}
-

2. Minat & Preferensi

-

Deskripsikan minat dan preferensi belajar Anda.

+

3. Preferensi Studi Lanjutan *

+

+ Preferensi studi lanjutan adalah kecenderungan pilihan Anda terhadap rumpun bidang studi yang ingin ditempuh setelah lulus, berdasarkan pertimbangan minat, bakat, kemampuan, dan prospek karir di masa depan. +

+
+ + +

Pilih rumpun studi yang paling mendekati kecenderungan Anda

+ @error('pref_studi') + {{ $message }} + @enderror +
+
-
-
- - -
+ {{-- ============================================ --}} + {{-- KRITERIA 4: CITA-CITA / PREFERENSI KARIR --}} + {{-- ============================================ --}} +
+

4. Cita-cita / Preferensi Karir *

+

Tuliskan profesi atau karir yang Anda impikan.

+
+ + +

Bisa lebih dari satu, pisahkan dengan koma

+ @error('cita_cita') + {{ $message }} + @enderror +
+
-
- - -
- -
- - -
- -
- - -
+ {{-- ============================================ --}} + {{-- KRITERIA 5: PRESTASI AKADEMIK / NON-AKADEMIK --}} + {{-- ============================================ --}} +
+

5. Prestasi Akademik / Non-Akademik

+

Tuliskan prestasi yang pernah diraih (opsional).

+
+ + +

Kosongkan jika belum ada prestasi

+ @error('prestasi') + {{ $message }} + @enderror
@@ -176,7 +258,7 @@ class="block w-full px-3 sm:px-4 py-2 border border-gray-300 rounded-lg focus-ma

- Metode: Sistem menggunakan Weighted Naive Bayes dengan 5 kriteria: Nilai (40%), Minat (35%), Preferensi (15%), Cita-cita (5%), Prestasi (5%). + Metode: Sistem menggunakan Graduated Scoring dengan 5 kriteria: Nilai Akademik (40%), Minat & Bakat (35%), Preferensi Studi (15%), Cita-cita (5%), Prestasi (5%).

diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php index 3cc265e..055eb19 100644 --- a/resources/views/welcome.blade.php +++ b/resources/views/welcome.blade.php @@ -7,16 +7,16 @@