From 0d8a13d3ac08e75b3222dda2c1613e02d616ac47 Mon Sep 17 00:00:00 2001
From: GalangSefin <158027366+GalangSefin@users.noreply.github.com>
Date: Tue, 7 Jul 2026 10:48:13 +0700
Subject: [PATCH] upload all changes in project sirekom
---
.htaccess | 15 +
.../Controllers/Admin/SiswaController.php | 2 +-
.../Api/Admin/AlumniController.php | 229 ++++---
.../Api/Admin/DashboardAdminController.php | 103 ++--
.../Controllers/Api/Admin/GuruController.php | 260 ++++++--
.../Api/Admin/JurusanController.php | 215 +++++--
.../Api/Admin/ProspekKerjaController.php | 237 +++++---
.../Api/Admin/RiwayatController.php | 85 ++-
.../Controllers/Api/Admin/SiswaController.php | 211 ++++---
app/Http/Controllers/Api/AuthController.php | 18 +-
.../Student/DashboardStudentController.php | 40 +-
.../Student/RecommendationApiController.php | 223 ++++---
app/Models/Alumni.php | 17 +-
app/Models/Guru.php | 29 +-
app/Models/Jurusan.php | 2 +-
app/Models/Siswa.php | 12 +-
app/Models/User.php | 5 +-
app/Services/PythonRecommendationService.php | 89 +++
config/services.php | 19 +-
flask_api/requirements.txt | 2 +-
index.php | 18 +
resources/views/admin/alumni/alumni.blade.php | 99 ++-
.../views/admin/alumni/alumnicreate.blade.php | 120 +++-
.../views/admin/alumni/alumniedit.blade.php | 171 +++++-
resources/views/admin/dashboard.blade.php | 61 +-
resources/views/admin/guru/guru.blade.php | 93 ++-
.../views/admin/guru/gurucreate.blade.php | 148 ++++-
resources/views/admin/guru/guruedit.blade.php | 161 ++++-
.../views/admin/jurusan/jurusan.blade.php | 119 ++--
.../admin/jurusan/jurusancreate.blade.php | 124 +++-
.../views/admin/jurusan/jurusanedit.blade.php | 172 +++++-
.../admin/prospek/prospekkerja.blade.php | 102 +++-
.../prospek/prospekkerjacreate.blade.php | 191 +++++-
.../admin/prospek/prospekkerjaedit.blade.php | 238 ++++++--
resources/views/admin/riwayat.blade.php | 213 ++++---
resources/views/admin/siswa/siswa.blade.php | 324 ++++------
.../views/admin/siswa/siswacreate.blade.php | 185 ++++--
.../views/admin/siswa/siswaedit.blade.php | 232 ++++---
resources/views/auth/login.blade.php | 34 +-
resources/views/student/dashboard.blade.php | 144 +++--
resources/views/student/hasil.blade.php | 113 ++--
resources/views/student/prediksi.blade.php | 567 ++++++++----------
routes/api.php | 80 ++-
routes/web.php | 8 +-
44 files changed, 3858 insertions(+), 1672 deletions(-)
create mode 100644 .htaccess
create mode 100644 app/Services/PythonRecommendationService.php
create mode 100644 index.php
diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..a3564e1
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,15 @@
+
+
+ Options -MultiViews -Indexes
+
+
+ RewriteEngine On
+
+ RewriteCond %{HTTP:Authorization} .
+ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+
+ RewriteRule ^ index.php [L]
+
\ No newline at end of file
diff --git a/app/Http/Controllers/Admin/SiswaController.php b/app/Http/Controllers/Admin/SiswaController.php
index 36c8194..fa1f989 100644
--- a/app/Http/Controllers/Admin/SiswaController.php
+++ b/app/Http/Controllers/Admin/SiswaController.php
@@ -16,7 +16,7 @@ public function create()
return view('admin.siswa.siswacreate');
}
- public function edit($id)
+ public function edit($id)
{
return view('admin.siswa.siswaedit', compact('id'));
}
diff --git a/app/Http/Controllers/Api/Admin/AlumniController.php b/app/Http/Controllers/Api/Admin/AlumniController.php
index 24c01ba..b2396ca 100644
--- a/app/Http/Controllers/Api/Admin/AlumniController.php
+++ b/app/Http/Controllers/Api/Admin/AlumniController.php
@@ -6,102 +6,195 @@
use App\Models\Alumni;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
+use Throwable;
class AlumniController extends Controller
{
public function index(Request $request)
{
- $perPage = (int) $request->input('per_page', 7);
- $perPage = in_array($perPage, [5, 7]) ? $perPage : 7;
+ try {
+ $perPage = (int) $request->input('per_page', 7);
+ $perPage = in_array($perPage, [5, 7, 10, 25, 50, 100]) ? $perPage : 7;
- $search = trim((string) $request->input('search', ''));
+ $search = trim((string) $request->input('search', ''));
- $query = Alumni::query()->orderBy('id', 'desc');
+ $query = Alumni::query()->orderBy('id', 'desc');
- if ($search !== '') {
- $query->where(function ($q) use ($search) {
- $q->where('id_alumni', 'like', "%{$search}%")
- ->orWhere('nama_alumni', 'like', "%{$search}%")
- ->orWhere('perguruan_tinggi', 'like', "%{$search}%")
- ->orWhere('jurusan_diambil', 'like', "%{$search}%");
- });
+ if ($search !== '') {
+ $query->where(function ($q) use ($search) {
+ $q->where('id_alumni', 'like', "%{$search}%")
+ ->orWhere('nama_alumni', 'like', "%{$search}%")
+ ->orWhere('perguruan_tinggi', 'like', "%{$search}%")
+ ->orWhere('jurusan_diambil', 'like', "%{$search}%");
+ });
+ }
+
+ $alumni = $query->paginate($perPage);
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data alumni berhasil dimuat.',
+ 'data' => $alumni->items(),
+ 'current_page' => $alumni->currentPage(),
+ 'last_page' => $alumni->lastPage(),
+ 'per_page' => $alumni->perPage(),
+ 'total' => $alumni->total(),
+ 'from' => $alumni->firstItem(),
+ 'to' => $alumni->lastItem(),
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Gagal memuat data alumni.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- $alumni = $query->paginate($perPage);
-
- return response()->json([
- 'success' => true,
- 'data' => $alumni->items(),
- 'current_page' => $alumni->currentPage(),
- 'last_page' => $alumni->lastPage(),
- 'per_page' => $alumni->perPage(),
- 'total' => $alumni->total(),
- 'from' => $alumni->firstItem(),
- 'to' => $alumni->lastItem(),
- ], 200);
}
public function store(Request $request)
{
- $validator = Validator::make($request->all(), [
- 'nama_alumni' => 'required|string|max:255',
- 'perguruan_tinggi' => 'required|string',
- 'jurusan_diambil' => 'required|string',
- ], [
- 'nama_alumni.required' => 'Nama alumni wajib diisi.',
- 'perguruan_tinggi.required' => 'Nama Perguruan Tinggi harus diisi.',
- 'jurusan_diambil.required' => 'Jurusan yang diambil tidak boleh kosong.',
- ]);
+ try {
+ $validator = Validator::make($request->all(), [
+ 'nama_alumni' => 'required|string|max:255',
+ 'perguruan_tinggi' => 'required|string|max:255',
+ 'jurusan_diambil' => 'required|string|max:255',
+ ], [
+ 'nama_alumni.required' => 'Nama alumni wajib diisi.',
+ 'perguruan_tinggi.required' => 'Nama perguruan tinggi wajib diisi.',
+ 'jurusan_diambil.required' => 'Jurusan yang diambil wajib diisi.',
+ ]);
- if ($validator->fails()) {
- return response()->json(['success' => false, 'errors' => $validator->errors()], 422);
+ if ($validator->fails()) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validasi gagal.',
+ 'errors' => $validator->errors(),
+ ], 422);
+ }
+
+ $alumni = new Alumni();
+ $alumni->nama_alumni = $request->input('nama_alumni');
+ $alumni->perguruan_tinggi = $request->input('perguruan_tinggi');
+ $alumni->jurusan_diambil = $request->input('jurusan_diambil');
+ $alumni->profil_nilai = $request->input('profil_nilai');
+ $alumni->profil_minat = $request->input('profil_minat');
+ $alumni->save();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data alumni berhasil ditambahkan.',
+ 'data' => $alumni,
+ ], 201);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat menyimpan data alumni.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
+ }
- $alumni = Alumni::create($request->all());
+ public function show($id)
+ {
+ try {
+ $alumni = Alumni::find($id);
- return response()->json([
- 'success' => true,
- 'message' => 'Alumni ' . $alumni->id_alumni . ' berhasil ditambahkan.',
- 'data' => $alumni
- ], 201);
+ if (!$alumni) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data alumni tidak ditemukan.',
+ ], 404);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data alumni berhasil ditemukan.',
+ 'data' => $alumni,
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat mengambil data alumni.',
+ 'error' => $e->getMessage(),
+ ], 500);
+ }
}
public function update(Request $request, $id)
{
- $alumni = Alumni::find($id);
+ try {
+ $alumni = Alumni::find($id);
- if (!$alumni) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$alumni) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data alumni tidak ditemukan.',
+ ], 404);
+ }
+
+ $validator = Validator::make($request->all(), [
+ 'nama_alumni' => 'required|string|max:255',
+ 'perguruan_tinggi' => 'required|string|max:255',
+ 'jurusan_diambil' => 'required|string|max:255',
+ ], [
+ 'nama_alumni.required' => 'Nama alumni wajib diisi.',
+ 'perguruan_tinggi.required' => 'Nama perguruan tinggi wajib diisi.',
+ 'jurusan_diambil.required' => 'Jurusan yang diambil wajib diisi.',
+ ]);
+
+ if ($validator->fails()) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validasi gagal.',
+ 'errors' => $validator->errors(),
+ ], 422);
+ }
+
+ $alumni->nama_alumni = $request->input('nama_alumni');
+ $alumni->perguruan_tinggi = $request->input('perguruan_tinggi');
+ $alumni->jurusan_diambil = $request->input('jurusan_diambil');
+ $alumni->profil_nilai = $request->input('profil_nilai');
+ $alumni->profil_minat = $request->input('profil_minat');
+ $alumni->save();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data alumni berhasil diperbarui.',
+ 'data' => $alumni,
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat memperbarui data alumni.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- $validator = Validator::make($request->all(), [
- 'nama_alumni' => 'required|string|max:255',
- 'perguruan_tinggi' => 'required|string',
- 'jurusan_diambil' => 'required|string',
- ]);
-
- if ($validator->fails()) {
- return response()->json(['success' => false, 'errors' => $validator->errors()], 422);
- }
-
- $alumni->update($request->all());
-
- return response()->json([
- 'success' => true,
- 'message' => 'Data ' . $alumni->id_alumni . ' berhasil diperbarui'
- ], 200);
}
public function destroy($id)
{
- $alumni = Alumni::find($id);
+ try {
+ $alumni = Alumni::find($id);
- if (!$alumni) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$alumni) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data alumni tidak ditemukan.',
+ ], 404);
+ }
+
+ $alumni->delete();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data alumni berhasil dihapus.',
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data alumni tidak dapat dihapus karena masih digunakan atau terjadi kesalahan server.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- $alumni->delete();
-
- return response()->json(['success' => true, 'message' => 'Data Alumni berhasil dihapus'], 200);
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/Admin/DashboardAdminController.php b/app/Http/Controllers/Api/Admin/DashboardAdminController.php
index 8b96aba..c0bcd70 100644
--- a/app/Http/Controllers/Api/Admin/DashboardAdminController.php
+++ b/app/Http/Controllers/Api/Admin/DashboardAdminController.php
@@ -3,40 +3,41 @@
namespace App\Http\Controllers\Api\Admin;
use App\Http\Controllers\Controller;
-use App\Models\User;
use App\Models\RiwayatRekomendasi;
+use App\Models\Siswa;
+use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
-use Exception;
+use Throwable;
class DashboardAdminController extends Controller
{
public function index(Request $request)
{
try {
- $tableName = (new RiwayatRekomendasi())->getTable();
+ $riwayatModel = new RiwayatRekomendasi();
+ $tableName = $riwayatModel->getTable();
- $totalSiswa = User::whereIn('role', ['siswa', 'student'])->count();
+ $totalSiswa = 0;
+
+ try {
+ $siswaModel = new Siswa();
+ if (Schema::hasTable($siswaModel->getTable())) {
+ $totalSiswa = Siswa::count();
+ }
+ } catch (Throwable $e) {
+ $totalSiswa = 0;
+ }
if ($totalSiswa === 0) {
- $totalSiswa = RiwayatRekomendasi::distinct('user_id')->count('user_id');
- }
-
- $totalRekomendasi = RiwayatRekomendasi::count();
-
- $kolomAkurasi = null;
-
- if (Schema::hasColumn($tableName, 'akurasi_prediksi')) {
- $kolomAkurasi = 'akurasi_prediksi';
- } elseif (Schema::hasColumn($tableName, 'probabilitas')) {
- $kolomAkurasi = 'probabilitas';
- } elseif (Schema::hasColumn($tableName, 'akurasi')) {
- $kolomAkurasi = 'akurasi';
+ $totalSiswa = User::whereIn('role', ['siswa', 'student'])->count();
}
+ $totalRekomendasi = Schema::hasTable($tableName) ? RiwayatRekomendasi::count() : 0;
+ $kolomAkurasi = $this->getKolomAkurasi($tableName);
$rataAkurasi = null;
- if ($kolomAkurasi) {
+ if ($kolomAkurasi && Schema::hasTable($tableName)) {
$avg = RiwayatRekomendasi::whereNotNull($kolomAkurasi)->avg($kolomAkurasi);
$rataAkurasi = $avg !== null ? round((float) $avg, 2) : null;
}
@@ -45,44 +46,64 @@ public function index(Request $request)
? 'Berdasarkan rata-rata tingkat keyakinan dari seluruh hasil rekomendasi.'
: 'Belum ada nilai keyakinan prediksi yang tersimpan. Lakukan prediksi baru terlebih dahulu.';
- $selectDistribusi = 'hasil_rekomendasi_jurusan as jurusan, COUNT(*) as total';
+ $distribusiJurusan = collect();
- if ($kolomAkurasi) {
- $selectDistribusi .= ', ROUND(AVG(' . $kolomAkurasi . '), 2) as rata_akurasi';
+ if (Schema::hasTable($tableName) && Schema::hasColumn($tableName, 'hasil_rekomendasi_jurusan')) {
+ $selectDistribusi = 'hasil_rekomendasi_jurusan as jurusan, COUNT(*) as total';
+
+ if ($kolomAkurasi) {
+ $selectDistribusi .= ', ROUND(AVG(' . $kolomAkurasi . '), 2) as rata_akurasi';
+ }
+
+ $distribusiJurusan = RiwayatRekomendasi::selectRaw($selectDistribusi)
+ ->whereNotNull('hasil_rekomendasi_jurusan')
+ ->groupBy('hasil_rekomendasi_jurusan')
+ ->orderBy('total', 'desc')
+ ->limit(10)
+ ->get()
+ ->map(function ($item) use ($kolomAkurasi) {
+ return [
+ 'jurusan' => $item->jurusan,
+ 'total' => (int) $item->total,
+ 'rata_akurasi' => $kolomAkurasi && $item->rata_akurasi !== null
+ ? round((float) $item->rata_akurasi, 2)
+ : null,
+ ];
+ });
}
- $distribusiJurusan = RiwayatRekomendasi::selectRaw($selectDistribusi)
- ->whereNotNull('hasil_rekomendasi_jurusan')
- ->groupBy('hasil_rekomendasi_jurusan')
- ->orderBy('total', 'desc')
- ->limit(10)
- ->get()
- ->map(function ($item) use ($kolomAkurasi) {
- return [
- 'jurusan' => $item->jurusan,
- 'total' => (int) $item->total,
- 'rata_akurasi' => $kolomAkurasi && $item->rata_akurasi !== null
- ? round((float) $item->rata_akurasi, 2)
- : null,
- ];
- });
-
return response()->json([
'success' => true,
+ 'message' => 'Statistik dashboard berhasil dimuat.',
'data' => [
'total_siswa' => $totalSiswa,
'rekomendasi_diproses' => $totalRekomendasi,
'rata_rata_akurasi' => $rataAkurasi,
'akurasi_status' => $akurasiStatus,
'distribusi_jurusan' => $distribusiJurusan,
- ]
+ ],
], 200);
-
- } catch (Exception $e) {
+ } catch (Throwable $e) {
return response()->json([
'success' => false,
- 'message' => 'Error: ' . $e->getMessage()
+ 'message' => 'Gagal memuat statistik dashboard.',
+ 'error' => $e->getMessage(),
], 500);
}
}
+
+ private function getKolomAkurasi(string $tableName): ?string
+ {
+ if (!Schema::hasTable($tableName)) {
+ return null;
+ }
+
+ foreach (['akurasi_prediksi', 'probabilitas', 'akurasi'] as $column) {
+ if (Schema::hasColumn($tableName, $column)) {
+ return $column;
+ }
+ }
+
+ return null;
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/Admin/GuruController.php b/app/Http/Controllers/Api/Admin/GuruController.php
index f184040..bff4f41 100644
--- a/app/Http/Controllers/Api/Admin/GuruController.php
+++ b/app/Http/Controllers/Api/Admin/GuruController.php
@@ -4,98 +4,242 @@
use App\Http\Controllers\Controller;
use App\Models\Guru;
+use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Facades\DB;
+use Throwable;
class GuruController extends Controller
{
+ /**
+ * Display a listing of the resource.
+ */
public function index(Request $request)
{
- $perPage = (int) $request->input('per_page', 7);
- $perPage = in_array($perPage, [5, 7]) ? $perPage : 7;
+ try {
+ $perPage = (int) $request->input('per_page', 7);
+ $perPage = in_array($perPage, [5, 7, 10, 25]) ? $perPage : 7;
- $search = trim((string) $request->input('search', ''));
+ $search = trim((string) $request->input('search', ''));
- $query = Guru::query()->orderBy('id', 'desc');
+ $query = Guru::query()->with('user')->orderBy('id', 'desc');
- if ($search !== '') {
- $query->where(function ($q) use ($search) {
- $q->where('id_guru', 'like', "%{$search}%")
- ->orWhere('nama_guru', 'like', "%{$search}%");
- });
+ if ($search !== '') {
+ $query->where(function ($q) use ($search) {
+ $q->where('id_guru', 'like', "%{$search}%")
+ ->orWhere('nama_guru', 'like', "%{$search}%");
+ });
+ }
+
+ $guru = $query->paginate($perPage);
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data guru berhasil dimuat.',
+ 'data' => $guru->items(),
+ 'current_page' => $guru->currentPage(),
+ 'last_page' => $guru->lastPage(),
+ 'per_page' => $guru->perPage(),
+ 'total' => $guru->total(),
+ ], 200);
+
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat memuat data guru.',
+ 'error' => $e->getMessage()
+ ], 500);
}
-
- $guru = $query->paginate($perPage);
-
- return response()->json([
- 'success' => true,
- 'data' => $guru->items(),
- 'current_page' => $guru->currentPage(),
- 'last_page' => $guru->lastPage(),
- 'per_page' => $guru->perPage(),
- 'total' => $guru->total(),
- 'from' => $guru->firstItem(),
- 'to' => $guru->lastItem(),
- ], 200);
}
+ /**
+ * Store a newly created resource in storage.
+ */
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'nama_guru' => 'required|string|max:255',
- ], [
- 'nama_guru.required' => 'Nama guru tidak boleh kosong.',
+ 'username' => 'required|string|max:255|unique:users,username',
+ 'email' => 'required|string|email|max:255|unique:users,email',
+ 'password' => 'required|string|min:8',
]);
if ($validator->fails()) {
- return response()->json(['success' => false, 'errors' => $validator->errors()], 422);
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validasi gagal.',
+ 'errors' => $validator->errors()
+ ], 422);
}
- $guru = Guru::create($request->all());
+ DB::beginTransaction();
- return response()->json([
- 'success' => true,
- 'message' => 'Guru ' . $guru->id_guru . ' berhasil ditambahkan.',
- 'data' => $guru
- ], 201);
+ try {
+ // Create User Akun
+ $user = User::create([
+ 'name' => $request->nama_guru,
+ 'username' => $request->username,
+ 'email' => $request->email,
+ 'password' => Hash::make($request->password),
+ 'role' => 'admin',
+ ]);
+
+ // Create Data Guru terikat dengan user_id
+ $guru = Guru::create([
+ 'user_id' => $user->id,
+ 'nama_guru' => $request->nama_guru,
+ ]);
+
+ DB::commit();
+
+ $guru->load('user');
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data guru dan akun login berhasil disimpan.',
+ 'data' => $guru
+ ], 201);
+
+ } catch (Throwable $e) {
+ DB::rollBack();
+
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat menyimpan data guru.',
+ 'error' => $e->getMessage()
+ ], 500);
+ }
}
+ /**
+ * Display the specified resource.
+ */
+ public function show($id)
+ {
+ try {
+ $guru = Guru::with('user')->find($id);
+
+ if (!$guru) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data guru tidak ditemukan.'
+ ], 404);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Detail data guru berhasil ditemukan.',
+ 'data' => $guru
+ ], 200);
+
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat mengambil detail data guru.',
+ 'error' => $e->getMessage()
+ ], 500);
+ }
+ }
+
+ /**
+ * Update the specified resource in storage.
+ */
public function update(Request $request, $id)
{
- $guru = Guru::find($id);
+ try {
+ $guru = Guru::find($id);
- if (!$guru) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$guru) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data guru tidak ditemukan.'
+ ], 404);
+ }
+
+ $validator = Validator::make($request->all(), [
+ 'nama_guru' => 'required|string|max:255',
+ ]);
+
+ if ($validator->fails()) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validasi gagal.',
+ 'errors' => $validator->errors()
+ ], 422);
+ }
+
+ DB::beginTransaction();
+
+ $guru->update([
+ 'nama_guru' => $request->nama_guru
+ ]);
+
+ if ($guru->user) {
+ $guru->user->update([
+ 'name' => $request->nama_guru
+ ]);
+ }
+
+ DB::commit();
+ $guru->load('user');
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data guru berhasil diperbarui.',
+ 'data' => $guru
+ ], 200);
+
+ } catch (Throwable $e) {
+ DB::rollBack();
+
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat memperbarui data guru.',
+ 'error' => $e->getMessage()
+ ], 500);
}
-
- $validator = Validator::make($request->all(), [
- 'nama_guru' => 'required|string|max:255',
- ], [
- 'nama_guru.required' => 'Update gagal, nama guru harus diisi.',
- ]);
-
- if ($validator->fails()) {
- return response()->json(['success' => false, 'errors' => $validator->errors()], 422);
- }
-
- $guru->update($request->all());
-
- return response()->json([
- 'success' => true,
- 'message' => 'Data ' . $guru->id_guru . ' berhasil diperbarui'
- ], 200);
}
+ /**
+ * Remove the specified resource from storage.
+ */
public function destroy($id)
{
- $guru = Guru::find($id);
+ try {
+ $guru = Guru::find($id);
- if (!$guru) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$guru) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data guru tidak ditemukan.'
+ ], 404);
+ }
+
+ DB::beginTransaction();
+
+ if ($guru->user_id) {
+ User::destroy($guru->user_id);
+ }
+
+ $guru->delete();
+
+ DB::commit();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data guru dan akun login berhasil dihapus.'
+ ], 200);
+
+ } catch (Throwable $e) {
+ DB::rollBack();
+
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data guru tidak dapat dihapus atau terjadi kesalahan server.',
+ 'error' => $e->getMessage()
+ ], 500);
}
-
- $guru->delete();
-
- return response()->json(['success' => true, 'message' => 'Data Guru berhasil dihapus'], 200);
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/Admin/JurusanController.php b/app/Http/Controllers/Api/Admin/JurusanController.php
index 93a5edf..db141ad 100644
--- a/app/Http/Controllers/Api/Admin/JurusanController.php
+++ b/app/Http/Controllers/Api/Admin/JurusanController.php
@@ -6,98 +6,191 @@
use App\Models\Jurusan;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
+use Illuminate\Validation\Rule;
+use Throwable;
class JurusanController extends Controller
{
public function index(Request $request)
{
- $perPage = (int) $request->input('per_page', 7);
- $perPage = in_array($perPage, [5, 7]) ? $perPage : 7;
+ try {
+ $perPage = (int) $request->input('per_page', 7);
+ $perPage = in_array($perPage, [5, 7, 10, 25, 50, 100]) ? $perPage : 7;
- $search = trim((string) $request->input('search', ''));
+ $search = trim((string) $request->input('search', ''));
- $query = Jurusan::query()->orderBy('id', 'desc');
+ // Mengambil data jurusan beserta semua prospek kerja yang terkait dengannya
+ $query = Jurusan::with('prospeks')->orderBy('id', 'desc');
- if ($search !== '') {
- $query->where(function ($q) use ($search) {
- $q->where('nama_jurusan', 'like', "%{$search}%")
- ->orWhere('rumpun', 'like', "%{$search}%")
- ->orWhere('prospek_karir', 'like', "%{$search}%");
- });
+ if ($search !== '') {
+ $query->where(function ($q) use ($search) {
+ $q->where('nama_jurusan', 'like', "%{$search}%")
+ ->orWhere('rumpun', 'like', "%{$search}%")
+ // Memperbaiki pencarian agar mencari ke dalam relasi tabel prospeks
+ ->orWhereHas('prospeks', function ($relationQuery) use ($search) {
+ $relationQuery->where('nama_profesi', 'like', "%{$search}%");
+ });
+ });
+ }
+
+ $jurusan = $query->paginate($perPage);
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data jurusan berhasil dimuat.',
+ 'data' => $jurusan->items(),
+ 'current_page' => $jurusan->currentPage(),
+ 'last_page' => $jurusan->lastPage(),
+ 'per_page' => $jurusan->perPage(),
+ 'total' => $jurusan->total(),
+ 'from' => $jurusan->firstItem(),
+ 'to' => $jurusan->lastItem(),
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat memuat data jurusan.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- $jurusan = $query->paginate($perPage);
-
- return response()->json([
- 'success' => true,
- 'data' => $jurusan->items(),
- 'current_page' => $jurusan->currentPage(),
- 'last_page' => $jurusan->lastPage(),
- 'per_page' => $jurusan->perPage(),
- 'total' => $jurusan->total(),
- 'from' => $jurusan->firstItem(),
- 'to' => $jurusan->lastItem(),
- ], 200);
}
-
public function store(Request $request)
{
- $validator = Validator::make($request->all(), [
- 'nama_jurusan' => 'required|string|max:255',
- 'rumpun' => 'required|string',
- ]);
+ try {
+ $validator = Validator::make($request->all(), [
+ 'nama_jurusan' => 'required|string|max:255',
+ 'rumpun' => ['required', 'string', Rule::in(['Saintek', 'Soshum'])],
+ ], [
+ 'nama_jurusan.required' => 'Nama jurusan wajib diisi.',
+ 'rumpun.required' => 'Rumpun jurusan wajib dipilih.',
+ 'rumpun.in' => 'Rumpun hanya boleh Saintek atau Soshum.',
+ ]);
- if ($validator->fails()) {
- return response()->json(['success' => false, 'errors' => $validator->errors()], 422);
+ if ($validator->fails()) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validasi gagal.',
+ 'errors' => $validator->errors(),
+ ], 422);
+ }
+
+ $jurusan = new Jurusan();
+ $jurusan->nama_jurusan = $request->input('nama_jurusan');
+ $jurusan->rumpun = $request->input('rumpun');
+ $jurusan->save();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data jurusan berhasil ditambahkan.',
+ 'data' => $jurusan,
+ ], 201);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat menyimpan data jurusan.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- $jurusan = Jurusan::create($request->all());
-
- return response()->json([
- 'success' => true,
- 'message' => 'Jurusan berhasil ditambahkan',
- 'data' => $jurusan
- ], 201);
}
public function show($id)
{
- $jurusan = Jurusan::find($id);
+ try {
+ $jurusan = Jurusan::find($id);
- if (!$jurusan) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$jurusan) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data jurusan tidak ditemukan.',
+ ], 404);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data jurusan berhasil ditemukan.',
+ 'data' => $jurusan,
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat mengambil data jurusan.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- return response()->json(['success' => true, 'data' => $jurusan], 200);
}
public function update(Request $request, $id)
{
- $jurusan = Jurusan::find($id);
+ try {
+ $jurusan = Jurusan::find($id);
- if (!$jurusan) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$jurusan) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data jurusan tidak ditemukan.',
+ ], 404);
+ }
+
+ $validator = Validator::make($request->all(), [
+ 'nama_jurusan' => [
+ 'required',
+ 'string',
+ 'max:255',
+ Rule::unique('jurusans', 'nama_jurusan')->ignore($id),
+ ],
+ 'rumpun' => 'required|in:Saintek,Soshum',
+ ]);
+
+ if ($validator->fails()) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validasi gagal.',
+ 'errors' => $validator->errors(),
+ ], 422);
+ }
+
+ $jurusan->nama_jurusan = $request->input('nama_jurusan');
+ $jurusan->rumpun = $request->input('rumpun');
+ $jurusan->save();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data jurusan berhasil diperbarui.',
+ 'data' => $jurusan,
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat memperbarui data jurusan.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- $jurusan->update($request->all());
-
- return response()->json([
- 'success' => true,
- 'message' => 'Jurusan berhasil diperbarui',
- 'data' => $jurusan
- ], 200);
}
public function destroy($id)
{
- $jurusan = Jurusan::find($id);
+ try {
+ $jurusan = Jurusan::find($id);
- if (!$jurusan) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$jurusan) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data jurusan tidak ditemukan.',
+ ], 404);
+ }
+
+ $jurusan->delete();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data jurusan berhasil dihapus.',
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data jurusan tidak dapat dihapus karena masih digunakan pada data lain atau terjadi kesalahan server.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- $jurusan->delete();
-
- return response()->json(['success' => true, 'message' => 'Jurusan berhasil dihapus'], 200);
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/Admin/ProspekKerjaController.php b/app/Http/Controllers/Api/Admin/ProspekKerjaController.php
index b8cf3f1..c3ddc21 100644
--- a/app/Http/Controllers/Api/Admin/ProspekKerjaController.php
+++ b/app/Http/Controllers/Api/Admin/ProspekKerjaController.php
@@ -6,114 +6,199 @@
use App\Models\ProspekKerja;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
+use Throwable;
class ProspekKerjaController extends Controller
{
public function index(Request $request)
{
- $perPage = (int) $request->input('per_page', 7);
- $perPage = in_array($perPage, [5, 7]) ? $perPage : 7;
+ try {
+ $perPage = (int) $request->input('per_page', 7);
+ $perPage = in_array($perPage, [5, 7, 10, 25, 50, 100]) ? $perPage : 7;
- $search = trim((string) $request->input('search', ''));
+ $search = trim((string) $request->input('search', ''));
- $query = ProspekKerja::with('jurusan')->orderBy('id', 'desc');
+ $query = ProspekKerja::with('jurusan')->orderBy('id', 'desc');
- if ($search !== '') {
- $query->where(function ($q) use ($search) {
- $q->where('nama_profesi', 'like', "%{$search}%")
- ->orWhere('range_gaji', 'like', "%{$search}%")
- ->orWhere('deskripsi_pekerjaan', 'like', "%{$search}%")
- ->orWhereHas('jurusan', function ($jurusan) use ($search) {
- $jurusan->where('nama_jurusan', 'like', "%{$search}%");
- });
- });
+ if ($search !== '') {
+ $query->where(function ($q) use ($search) {
+ $q->where('nama_profesi', 'like', "%{$search}%")
+ ->orWhere('range_gaji', 'like', "%{$search}%")
+ ->orWhere('deskripsi_pekerjaan', 'like', "%{$search}%")
+ ->orWhereHas('jurusan', function ($jurusan) use ($search) {
+ $jurusan->where('nama_jurusan', 'like', "%{$search}%");
+ });
+ });
+ }
+
+ $prospek = $query->paginate($perPage);
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data prospek kerja berhasil dimuat.',
+ 'data' => $prospek->items(),
+ 'current_page' => $prospek->currentPage(),
+ 'last_page' => $prospek->lastPage(),
+ 'per_page' => $prospek->perPage(),
+ 'total' => $prospek->total(),
+ 'from' => $prospek->firstItem(),
+ 'to' => $prospek->lastItem(),
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Gagal memuat data prospek kerja.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- $prospek = $query->paginate($perPage);
-
- return response()->json([
- 'success' => true,
- 'data' => $prospek->items(),
- 'current_page' => $prospek->currentPage(),
- 'last_page' => $prospek->lastPage(),
- 'per_page' => $prospek->perPage(),
- 'total' => $prospek->total(),
- 'from' => $prospek->firstItem(),
- 'to' => $prospek->lastItem(),
- ], 200);
}
public function store(Request $request)
{
- $validator = Validator::make($request->all(), [
- 'jurusan_id' => 'required|exists:jurusans,id',
- 'nama_profesi' => 'required|string|max:255',
- 'range_gaji' => 'nullable|string|max:255',
- 'deskripsi_pekerjaan' => 'nullable|string',
- ]);
+ try {
+ $validator = Validator::make($request->all(), [
+ 'jurusan_id' => 'required|integer|exists:jurusans,id',
+ 'nama_profesi' => 'required|string|max:255',
+ 'range_gaji' => 'nullable|string|max:255',
+ 'deskripsi_pekerjaan' => 'nullable|string',
+ ], [
+ 'jurusan_id.required' => 'Jurusan wajib dipilih.',
+ 'jurusan_id.exists' => 'Jurusan yang dipilih tidak ditemukan.',
+ 'nama_profesi.required' => 'Nama profesi wajib diisi.',
+ ]);
- if ($validator->fails()) {
- return response()->json(['success' => false, 'errors' => $validator->errors()], 422);
+ if ($validator->fails()) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validasi gagal.',
+ 'errors' => $validator->errors(),
+ ], 422);
+ }
+
+ $prospek = new ProspekKerja();
+ $prospek->jurusan_id = $request->input('jurusan_id');
+ $prospek->nama_profesi = $request->input('nama_profesi');
+ $prospek->range_gaji = $request->input('range_gaji');
+ $prospek->deskripsi_pekerjaan = $request->input('deskripsi_pekerjaan');
+ $prospek->save();
+ $prospek->load('jurusan');
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data prospek kerja berhasil ditambahkan.',
+ 'data' => $prospek,
+ ], 201);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat menyimpan data prospek kerja.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- $prospek = ProspekKerja::create($request->all());
-
- return response()->json([
- 'success' => true,
- 'message' => 'Prospek Kerja berhasil ditambahkan',
- 'data' => $prospek
- ], 201);
}
public function show($id)
{
- $prospek = ProspekKerja::find($id);
+ try {
+ $prospek = ProspekKerja::with('jurusan')->find($id);
- if (!$prospek) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$prospek) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data prospek kerja tidak ditemukan.',
+ ], 404);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data prospek kerja berhasil ditemukan.',
+ 'data' => $prospek,
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat mengambil data prospek kerja.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- return response()->json(['success' => true, 'data' => $prospek], 200);
}
public function update(Request $request, $id)
{
- $prospek = ProspekKerja::find($id);
+ try {
+ $prospek = ProspekKerja::find($id);
- if (!$prospek) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$prospek) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data prospek kerja tidak ditemukan.',
+ ], 404);
+ }
+
+ $validator = Validator::make($request->all(), [
+ 'jurusan_id' => 'required|integer|exists:jurusans,id',
+ 'nama_profesi' => 'required|string|max:255',
+ 'range_gaji' => 'nullable|string|max:255',
+ 'deskripsi_pekerjaan' => 'nullable|string',
+ ], [
+ 'jurusan_id.required' => 'Jurusan wajib dipilih.',
+ 'jurusan_id.exists' => 'Jurusan yang dipilih tidak ditemukan.',
+ 'nama_profesi.required' => 'Nama profesi wajib diisi.',
+ ]);
+
+ if ($validator->fails()) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validasi gagal.',
+ 'errors' => $validator->errors(),
+ ], 422);
+ }
+
+ $prospek->jurusan_id = $request->input('jurusan_id');
+ $prospek->nama_profesi = $request->input('nama_profesi');
+ $prospek->range_gaji = $request->input('range_gaji');
+ $prospek->deskripsi_pekerjaan = $request->input('deskripsi_pekerjaan');
+ $prospek->save();
+ $prospek->load('jurusan');
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data prospek kerja berhasil diperbarui.',
+ 'data' => $prospek,
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Terjadi kesalahan saat memperbarui data prospek kerja.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- $validator = Validator::make($request->all(), [
- 'jurusan_id' => 'required|exists:jurusans,id',
- 'nama_profesi' => 'required|string|max:255',
- 'range_gaji' => 'nullable|string|max:255',
- 'deskripsi_pekerjaan' => 'nullable|string',
- ]);
-
- if ($validator->fails()) {
- return response()->json(['success' => false, 'errors' => $validator->errors()], 422);
- }
-
- $prospek->update($request->all());
-
- return response()->json([
- 'success' => true,
- 'message' => 'Prospek Kerja berhasil diperbarui',
- 'data' => $prospek
- ], 200);
}
public function destroy($id)
{
- $prospek = ProspekKerja::find($id);
+ try {
+ $prospek = ProspekKerja::find($id);
- if (!$prospek) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$prospek) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data prospek kerja tidak ditemukan.',
+ ], 404);
+ }
+
+ $prospek->delete();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data prospek kerja berhasil dihapus.',
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data prospek kerja tidak dapat dihapus karena masih digunakan atau terjadi kesalahan server.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
-
- $prospek->delete();
-
- return response()->json(['success' => true, 'message' => 'Prospek Kerja berhasil dihapus'], 200);
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/Admin/RiwayatController.php b/app/Http/Controllers/Api/Admin/RiwayatController.php
index 2f5fc69..74e84fc 100644
--- a/app/Http/Controllers/Api/Admin/RiwayatController.php
+++ b/app/Http/Controllers/Api/Admin/RiwayatController.php
@@ -6,7 +6,7 @@
use App\Models\RiwayatRekomendasi;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
-use Exception;
+use Throwable;
class RiwayatController extends Controller
{
@@ -14,7 +14,7 @@ public function index(Request $request)
{
try {
$perPage = (int) $request->input('per_page', 7);
- $perPage = in_array($perPage, [5, 7]) ? $perPage : 7;
+ $perPage = in_array($perPage, [5, 7, 10, 25, 50, 100]) ? $perPage : 7;
$search = trim((string) $request->input('search', ''));
@@ -49,6 +49,7 @@ public function index(Request $request)
return response()->json([
'success' => true,
+ 'message' => 'Data riwayat berhasil dimuat.',
'data' => $riwayat->items(),
'current_page' => $riwayat->currentPage(),
'last_page' => $riwayat->lastPage(),
@@ -57,11 +58,11 @@ public function index(Request $request)
'from' => $riwayat->firstItem(),
'to' => $riwayat->lastItem(),
], 200);
-
- } catch (Exception $e) {
+ } catch (Throwable $e) {
return response()->json([
'success' => false,
- 'message' => 'Error: ' . $e->getMessage()
+ 'message' => 'Gagal memuat data riwayat.',
+ 'error' => $e->getMessage(),
], 500);
}
}
@@ -69,10 +70,18 @@ public function index(Request $request)
public function show($id)
{
try {
- $detail = RiwayatRekomendasi::with('user:id,name,email')->findOrFail($id);
+ $detail = RiwayatRekomendasi::with('user:id,name,email')->find($id);
+
+ if (!$detail) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data detail riwayat tidak ditemukan.',
+ ], 404);
+ }
return response()->json([
'success' => true,
+ 'message' => 'Detail riwayat berhasil ditemukan.',
'data' => [
'id' => $detail->id,
'tanggal' => $detail->created_at ? $detail->created_at->format('d M Y H:i:s') : '-',
@@ -85,13 +94,20 @@ public function show($id)
'kondisi_ekonomi' => $this->getLabelEkonomi($detail->gaji_ortu),
'gaji_ortu' => $detail->gaji_ortu,
'nilai_ringkas' => [
- 'Matematika' => $detail->matematika,
+ 'Agama' => $detail->agama,
+ 'PKN' => $detail->pkn,
'B. Indonesia' => $detail->bahasa_indonesia,
'B. Inggris' => $detail->bahasa_inggris,
+ 'Matematika' => $detail->matematika,
'Fisika' => $detail->fisika,
'Kimia' => $detail->kimia,
'Biologi' => $detail->biologi,
+ 'Sejarah' => $detail->sejarah,
+ 'Geografi' => $detail->geografi,
'Ekonomi' => $detail->ekonomi_mapel,
+ 'Sosiologi' => $detail->sosiologi,
+ 'Seni Budaya' => $detail->seni_budaya,
+ 'PJOK' => $detail->pjok,
'Informatika' => $detail->informatika,
],
'riasec' => [
@@ -102,14 +118,41 @@ public function show($id)
'Enterprising' => $detail->riasec_e,
'Conventional' => $detail->riasec_c,
],
- ]
+ ],
], 200);
-
- } catch (Exception $e) {
+ } catch (Throwable $e) {
return response()->json([
'success' => false,
- 'message' => 'Data detail tidak ditemukan: ' . $e->getMessage()
- ], 404);
+ 'message' => 'Terjadi kesalahan saat mengambil detail riwayat.',
+ 'error' => $e->getMessage(),
+ ], 500);
+ }
+ }
+
+ public function destroy($id)
+ {
+ try {
+ $riwayat = RiwayatRekomendasi::find($id);
+
+ if (!$riwayat) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data riwayat tidak ditemukan.',
+ ], 404);
+ }
+
+ $riwayat->delete();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data riwayat berhasil dihapus.',
+ ], 200);
+ } catch (Throwable $e) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Data riwayat gagal dihapus.',
+ 'error' => $e->getMessage(),
+ ], 500);
}
}
@@ -117,15 +160,13 @@ private function getAkurasiPrediksi($riwayat): ?float
{
$tableName = (new RiwayatRekomendasi())->getTable();
- if (!Schema::hasColumn($tableName, 'akurasi_prediksi')) {
- return null;
+ foreach (['akurasi_prediksi', 'probabilitas', 'akurasi'] as $column) {
+ if (Schema::hasColumn($tableName, $column) && $riwayat->{$column} !== null) {
+ return round((float) $riwayat->{$column}, 2);
+ }
}
- if ($riwayat->akurasi_prediksi === null) {
- return null;
- }
-
- return round((float) $riwayat->akurasi_prediksi, 2);
+ return null;
}
private function getRataNilai($riwayat): float
@@ -148,7 +189,7 @@ private function getRataNilai($riwayat): float
$riwayat->informatika,
];
- $nilai = array_filter($nilai, fn ($item) => $item !== null);
+ $nilai = array_filter($nilai, fn ($item) => $item !== null && $item !== '');
if (count($nilai) === 0) {
return 0;
@@ -175,6 +216,10 @@ private function getMinatUtama($riwayat): string
private function getLabelEkonomi($gaji): string
{
+ if ($gaji === null || $gaji === '') {
+ return '-';
+ }
+
$gaji = (float) $gaji;
if ($gaji < 2000000) {
diff --git a/app/Http/Controllers/Api/Admin/SiswaController.php b/app/Http/Controllers/Api/Admin/SiswaController.php
index bf05164..724474c 100644
--- a/app/Http/Controllers/Api/Admin/SiswaController.php
+++ b/app/Http/Controllers/Api/Admin/SiswaController.php
@@ -4,134 +4,185 @@
use App\Http\Controllers\Controller;
use App\Models\Siswa;
+use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Validation\Rule;
+use Throwable;
class SiswaController extends Controller
{
public function index(Request $request)
{
- $perPage = (int) $request->input('per_page', 7);
- $perPage = in_array($perPage, [5, 7]) ? $perPage : 7;
+ try {
+ $perPage = (int) $request->input('per_page', 7);
+ $perPage = in_array($perPage, [5, 7, 10, 25, 50, 100]) ? $perPage : 7;
+ $search = trim((string) $request->input('search', ''));
- $search = trim((string) $request->input('search', ''));
+ $query = Siswa::with('user')->orderBy('id', 'desc');
- $query = Siswa::with(['user', 'jurusan'])->orderBy('id', 'desc');
+ if ($search !== '') {
+ $query->where(function ($q) use ($search) {
+ $q->where('nama_lengkap', 'like', "%{$search}%")
+ ->orWhere('nisn', 'like', "%{$search}%")
+ ->orWhere('kelas', 'like', "%{$search}%")
+ ->orWhereHas('user', function ($user) use ($search) {
+ $user->where('username', 'like', "%{$search}%");
+ });
+ });
+ }
- if ($search !== '') {
- $query->where(function ($q) use ($search) {
- $q->where('id_siswa', 'like', "%{$search}%")
- ->orWhere('nama_lengkap', 'like', "%{$search}%")
- ->orWhere('nisn', 'like', "%{$search}%")
- ->orWhere('kelas', 'like', "%{$search}%")
- ->orWhere('peminatan', 'like', "%{$search}%")
- ->orWhereHas('user', function ($user) use ($search) {
- $user->where('name', 'like', "%{$search}%")
- ->orWhere('email', 'like', "%{$search}%");
- })
- ->orWhereHas('jurusan', function ($jurusan) use ($search) {
- $jurusan->where('nama_jurusan', 'like', "%{$search}%");
- });
- });
+ $siswa = $query->paginate($perPage);
+
+ return response()->json(['success' => true, 'data' => $siswa], 200);
+ } catch (Throwable $e) {
+ return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
}
-
- $siswa = $query->paginate($perPage);
-
- return response()->json([
- 'success' => true,
- 'data' => $siswa->items(),
- 'current_page' => $siswa->currentPage(),
- 'last_page' => $siswa->lastPage(),
- 'per_page' => $siswa->perPage(),
- 'total' => $siswa->total(),
- 'from' => $siswa->firstItem(),
- 'to' => $siswa->lastItem(),
- ], 200);
}
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
- 'user_id' => 'required|exists:users,id|unique:siswas,user_id',
- 'jurusan_id' => 'required|exists:jurusans,id',
- 'nama_lengkap' => 'required|string|max:255',
- 'nisn' => 'required|string|unique:siswas,nisn',
- 'kelas' => 'required|string',
- 'peminatan' => 'required|string',
- ], [
- 'user_id.unique' => 'Gagal! Akun User ID ini sudah terikat dengan data siswa lain.',
- 'nisn.unique' => 'Gagal! NISN ' . $request->nisn . ' sudah terdaftar di sistem.',
- 'nama_lengkap.required' => 'Kolom nama lengkap tidak boleh dikosongkan.',
- 'nisn.required' => 'Kolom NISN wajib diisi.',
- 'user_id.exists' => 'User ID yang Anda masukkan tidak terdaftar di database.',
+ 'username' => ['required', Rule::unique(User::class, 'username')],
+ 'password' => 'required|min:6',
+ 'nama_lengkap' => 'required',
+ 'nisn' => ['required', Rule::unique(Siswa::class, 'nisn')],
+ 'kelas' => 'required',
]);
if ($validator->fails()) {
- return response()->json([
- 'success' => false,
- 'message' => 'Validasi gagal',
- 'errors' => $validator->errors()
- ], 422);
+ return response()->json(['success' => false, 'message' => 'Validasi gagal', 'errors' => $validator->errors()], 422);
}
- $siswa = Siswa::create($request->all());
+ DB::beginTransaction();
+ try {
+ $usernameClean = str_replace(' ', '', $request->username);
+ $otomatisEmail = strtolower($usernameClean) . '@gmail.com';
- return response()->json([
- 'success' => true,
- 'message' => 'Siswa ' . $siswa->id_siswa . ' (' . $siswa->nama_lengkap . ') berhasil ditambahkan.',
- 'data' => $siswa
- ], 201);
+ $user = User::create([
+ 'name' => $request->nama_lengkap,
+ 'username' => $request->username,
+ 'email' => $otomatisEmail,
+ 'password' => Hash::make($request->password),
+ 'role' => 'siswa',
+ ]);
+
+ Siswa::create([
+ 'user_id' => $user->id,
+ 'nama_lengkap' => $request->nama_lengkap,
+ 'nisn' => $request->nisn,
+ 'kelas' => $request->kelas,
+ ]);
+
+ DB::commit();
+ return response()->json(['success' => true, 'message' => 'Data siswa berhasil ditambahkan.'], 201);
+ } catch (\Throwable $e) {
+ DB::rollBack();
+ return response()->json(['success' => false, 'message' => 'Gagal menyimpan data.', 'error' => $e->getMessage()], 500);
+ }
}
public function show($id)
{
- $siswa = Siswa::find($id);
+ try {
+ $siswa = Siswa::with('user')->find($id);
- if (!$siswa) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$siswa) {
+ return response()->json(['success' => false, 'message' => 'Data siswa tidak ditemukan.'], 404);
+ }
+
+ return response()->json(['success' => true, 'data' => $siswa], 200);
+ } catch (Throwable $e) {
+ return response()->json(['success' => false, 'message' => 'Gagal memuat rincian.', 'error' => $e->getMessage()], 500);
}
-
- return response()->json(['success' => true, 'data' => $siswa], 200);
}
public function update(Request $request, $id)
{
$siswa = Siswa::find($id);
-
+
if (!$siswa) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ return response()->json(['success' => false, 'message' => 'Data siswa tidak ditemukan.'], 404);
}
$validator = Validator::make($request->all(), [
- 'nama_lengkap' => 'required|string|max:255',
- 'nisn' => 'required|string|unique:siswas,nisn,' . $id,
- 'kelas' => 'required|string',
- ], [
- 'nisn.unique' => 'Gagal Update! NISN ' . $request->nisn . ' sudah digunakan siswa lain.'
+ 'username' => [
+ 'nullable',
+ Rule::unique(User::class, 'username')->ignore($siswa->user_id)
+ ],
+ 'nisn' => [
+ 'required',
+ Rule::unique(Siswa::class, 'nisn')->ignore($siswa->id)
+ ],
+ 'nama_lengkap' => 'required',
+ 'kelas' => 'required',
]);
if ($validator->fails()) {
- return response()->json(['success' => false, 'errors' => $validator->errors()], 422);
+ return response()->json(['success' => false, 'message' => 'Validasi gagal', 'errors' => $validator->errors()], 422);
}
- $siswa->update($request->all());
+ DB::beginTransaction();
+ try {
+ if ($siswa->user_id) {
+ $user = User::find($siswa->user_id);
+ if ($user) {
+ $user->name = $request->nama_lengkap;
+
+ if ($request->filled('username')) {
+ $user->username = $request->username;
+ $usernameClean = str_replace(' ', '', $request->username);
+ $user->email = strtolower($usernameClean) . '@gmail.com';
+ }
- return response()->json([
- 'success' => true,
- 'message' => 'Data ' . $siswa->id_siswa . ' berhasil diperbarui'
- ], 200);
+ if ($request->filled('password')) {
+ $user->password = Hash::make($request->password);
+ }
+ $user->save();
+ }
+ }
+
+ $siswa->update([
+ 'nama_lengkap' => $request->nama_lengkap,
+ 'nisn' => $request->nisn,
+ 'kelas' => $request->kelas
+ ]);
+
+ DB::commit();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Data siswa berhasil diperbarui.',
+ 'data' => $siswa->load('user')
+ ], 200);
+ } catch (Throwable $e) {
+ DB::rollBack();
+ return response()->json(['success' => false, 'message' => 'Gagal memperbarui data.', 'error' => $e->getMessage()], 500);
+ }
}
public function destroy($id)
{
- $siswa = Siswa::find($id);
+ try {
+ $siswa = Siswa::find($id);
- if (!$siswa) {
- return response()->json(['success' => false, 'message' => 'Data tidak ditemukan'], 404);
+ if (!$siswa) {
+ return response()->json(['success' => false, 'message' => 'Data tidak ditemukan.'], 404);
+ }
+
+ if ($siswa->user_id) {
+ $userId = $siswa->user_id;
+ $siswa->delete();
+ User::destroy($userId);
+ } else {
+ $siswa->delete();
+ }
+
+ return response()->json(['success' => true, 'message' => 'Data berhasil dihapus.'], 200);
+ } catch (Throwable $e) {
+ return response()->json(['success' => false, 'message' => 'Gagal menghapus.', 'error' => $e->getMessage()], 500);
}
-
- $siswa->delete();
-
- return response()->json(['success' => true, 'message' => 'Data berhasil dihapus'], 200);
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/AuthController.php b/app/Http/Controllers/Api/AuthController.php
index 4cacdab..dfe6391 100644
--- a/app/Http/Controllers/Api/AuthController.php
+++ b/app/Http/Controllers/Api/AuthController.php
@@ -6,7 +6,6 @@
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
-use Illuminate\Validation\ValidationException;
class AuthController extends Controller
{
@@ -26,10 +25,8 @@ public function login(Request $request)
], 401);
}
- // Hapus token lama
$user->tokens()->delete();
- // Buat token baru
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
@@ -37,8 +34,9 @@ public function login(Request $request)
'message' => 'Login berhasil',
'access_token' => $token,
'token_type' => 'Bearer',
- 'role' => $user->role, // Penting untuk redirect di frontend
+ 'role' => $user->role,
'user' => [
+ 'id' => $user->id,
'name' => $user->name,
'username' => $user->username,
]
@@ -47,7 +45,15 @@ public function login(Request $request)
public function logout(Request $request)
{
- $request->user()->currentAccessToken()->delete();
- return response()->json(['message' => 'Berhasil logout']);
+ $token = $request->user()?->currentAccessToken();
+
+ if ($token) {
+ $token->delete();
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Berhasil logout'
+ ]);
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Api/Student/DashboardStudentController.php b/app/Http/Controllers/Api/Student/DashboardStudentController.php
index 9abfd59..711b6a6 100644
--- a/app/Http/Controllers/Api/Student/DashboardStudentController.php
+++ b/app/Http/Controllers/Api/Student/DashboardStudentController.php
@@ -13,9 +13,12 @@ public function index()
{
try {
$user = Auth::user();
-
- if (!$user) {
- return response()->json(['success' => false, 'message' => 'Unauthorized'], 401);
+
+ if (! $user) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Unauthorized'
+ ], 401);
}
$totalAktivitas = RiwayatRekomendasi::where('user_id', $user->id)->count();
@@ -32,7 +35,7 @@ public function index()
return [
'id' => $item->id,
'jurusan' => $item->hasil_rekomendasi_jurusan,
- 'tanggal' => $item->created_at->diffForHumans(),
+ 'tanggal' => $item->created_at ? $item->created_at->diffForHumans() : '-',
];
});
@@ -41,17 +44,30 @@ public function index()
->get()
->map(function ($item, $index) {
$mapel = [
- 'agama', 'pkn', 'bahasa_indonesia', 'bahasa_inggris', 'matematika',
- 'fisika', 'kimia', 'biologi', 'sejarah', 'geografi',
- 'ekonomi_mapel', 'sosiologi', 'seni_budaya', 'pjok', 'informatika'
+ 'agama',
+ 'pkn',
+ 'bahasa_indonesia',
+ 'bahasa_inggris',
+ 'matematika',
+ 'fisika',
+ 'kimia',
+ 'biologi',
+ 'sejarah',
+ 'geografi',
+ 'ekonomi_mapel',
+ 'sosiologi',
+ 'seni_budaya',
+ 'pjok',
+ 'informatika'
];
$totalNilai = 0;
+
foreach ($mapel as $m) {
- $totalNilai += (float) $item->$m;
+ $totalNilai += (float) ($item->$m ?? 0);
}
-
- $rataRata = $totalNilai / count($mapel);
+
+ $rataRata = count($mapel) > 0 ? $totalNilai / count($mapel) : 0;
return [
'label' => 'Tes ' . ($index + 1),
@@ -63,7 +79,9 @@ public function index()
'success' => true,
'data' => [
'total_aktivitas' => $totalAktivitas,
- 'rekomendasi_terakhir' => $rekomendasiTerakhir ? $rekomendasiTerakhir->hasil_rekomendasi_jurusan : 'Belum Ada Data',
+ 'rekomendasi_terakhir' => $rekomendasiTerakhir
+ ? $rekomendasiTerakhir->hasil_rekomendasi_jurusan
+ : 'Belum Ada Data',
'aktivitas_terbaru' => $aktivitasTerbaru,
'tren_nilai' => $trenNilai
]
diff --git a/app/Http/Controllers/Api/Student/RecommendationApiController.php b/app/Http/Controllers/Api/Student/RecommendationApiController.php
index 4c53ce6..63fe7f6 100644
--- a/app/Http/Controllers/Api/Student/RecommendationApiController.php
+++ b/app/Http/Controllers/Api/Student/RecommendationApiController.php
@@ -4,18 +4,26 @@
use App\Http\Controllers\Controller;
use App\Models\RiwayatRekomendasi;
+use App\Models\Jurusan;
+use App\Services\PythonRecommendationService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
-use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Schema;
class RecommendationApiController extends Controller
{
- public function hitungPrediksi(Request $request)
+ public function health(PythonRecommendationService $pythonService)
+ {
+ $result = $pythonService->health();
+
+ return response()->json($result, $result['success'] ? 200 : 500);
+ }
+
+ public function hitungPrediksi(Request $request, PythonRecommendationService $pythonService)
{
$user = Auth::user();
- if (!$user) {
+ if (! $user) {
return response()->json([
'success' => false,
'message' => 'User belum login.'
@@ -49,49 +57,52 @@ public function hitungPrediksi(Request $request)
'gaji_ortu' => 'required|numeric|min:0',
]);
- $payload = $validated;
- $payload['ekonomi_orang_tua'] = self::getLabelEkonomiSingkat($validated['gaji_ortu']);
+ $payloadPython = $validated;
+
+ $payloadPython['ekonomi_orang_tua'] = self::getLabelEkonomiSingkat($validated['gaji_ortu']);
+
+ unset($payloadPython['gaji_ortu']);
try {
- $response = Http::timeout(15)
- ->acceptJson()
- ->post('http://127.0.0.1:5000/predict', $payload);
+ $pythonResult = $pythonService->predict($payloadPython);
- if (!$response->successful()) {
+ if (! $pythonResult['success']) {
return response()->json([
'success' => false,
- 'message' => 'ML Engine Error: ' . ($response->json()['message'] ?? $response->body())
+ 'message' => 'ML Engine Error: ' . ($pythonResult['message'] ?? 'Tidak dapat menghubungi Flask API.'),
+ 'detail' => $pythonResult,
], 500);
}
- $responseData = $response->json();
+ $responseData = $pythonResult['data'];
- if (!isset($responseData['success']) || !$responseData['success']) {
+ if (! is_array($responseData)) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Respons Flask API tidak valid.'
+ ], 500);
+ }
+
+ if (($responseData['success'] ?? true) === false) {
return response()->json([
'success' => false,
'message' => 'ML Engine Error: ' . ($responseData['message'] ?? 'Unknown Error')
], 500);
}
- $teksJurusan = $responseData['prediksi_jurusan'] ?? null;
+ $teksJurusan = $responseData['prediksi_jurusan']
+ ?? $responseData['rekomendasi']
+ ?? $responseData['jurusan']
+ ?? null;
- if (!$teksJurusan) {
+ if (! $teksJurusan) {
return response()->json([
'success' => false,
'message' => 'Prediksi jurusan tidak ditemukan pada respons Flask.'
], 500);
}
- /*
- * Ambil nilai probabilitas dari Flask.
- * Contoh respons Flask yang disarankan:
- * {
- * "success": true,
- * "prediksi_jurusan": "Teknik Informatika",
- * "probabilitas": 86.45
- * }
- */
- $akurasiPrediksi = self::normalisasiAkurasi($responseData['probabilitas'] ?? null);
+ $akurasiPrediksi = self::ambilAkurasiDariResponse($responseData, $teksJurusan);
$dataRiwayat = [
'user_id' => $user->id,
@@ -123,16 +134,16 @@ public function hitungPrediksi(Request $request)
'hasil_rekomendasi_jurusan' => $teksJurusan,
];
- /*
- * Kolom akurasi_prediksi hanya diisi kalau migration baru sudah dijalankan.
- * Ini mencegah error kalau kolom belum ada.
- */
$tableName = (new RiwayatRekomendasi())->getTable();
if (Schema::hasColumn($tableName, 'akurasi_prediksi')) {
$dataRiwayat['akurasi_prediksi'] = $akurasiPrediksi;
}
+ if (Schema::hasColumn($tableName, 'probabilitas')) {
+ $dataRiwayat['probabilitas'] = $akurasiPrediksi;
+ }
+
$riwayat = RiwayatRekomendasi::create($dataRiwayat);
return response()->json([
@@ -140,7 +151,9 @@ public function hitungPrediksi(Request $request)
'message' => 'Prediksi berhasil dibuat.',
'redirect_url' => route('student.result', ['id' => $riwayat->id]),
'prediksi_jurusan' => $teksJurusan,
- 'akurasi_prediksi' => $akurasiPrediksi
+ 'akurasi_prediksi' => $akurasiPrediksi,
+ 'top_predictions' => $responseData['top_predictions'] ?? null,
+ 'riwayat_id' => $riwayat->id,
]);
} catch (\Throwable $e) {
@@ -153,7 +166,16 @@ public function hitungPrediksi(Request $request)
public function getHasilRekomendasi($id)
{
- $riwayat = RiwayatRekomendasi::findOrFail($id);
+ $user = Auth::user();
+
+ if (! $user) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'User belum login.'
+ ], 401);
+ }
+
+ $riwayat = RiwayatRekomendasi::where('user_id', $user->id)->findOrFail($id);
return response()->json([
'success' => true,
@@ -166,7 +188,7 @@ public static function formatHasilData(RiwayatRekomendasi $riwayat): array
return [
'id' => $riwayat->id,
'hasil_rekomendasi_jurusan' => $riwayat->hasil_rekomendasi_jurusan,
- 'akurasi_prediksi' => $riwayat->akurasi_prediksi,
+ 'akurasi_prediksi' => $riwayat->akurasi_prediksi ?? $riwayat->probabilitas ?? null,
'label_ekonomi' => self::getLabelEkonomiDetail($riwayat->gaji_ortu),
'minat_utama' => self::getMinatUtamaLabel($riwayat),
'prospek_kerja' => self::getProspekKerjaText($riwayat->hasil_rekomendasi_jurusan),
@@ -182,6 +204,34 @@ public static function formatHasilData(RiwayatRekomendasi $riwayat): array
];
}
+ private static function ambilAkurasiDariResponse(array $responseData, ?string $jurusan = null): ?float
+ {
+ $nilaiLangsung = $responseData['probabilitas']
+ ?? $responseData['confidence']
+ ?? $responseData['akurasi_prediksi']
+ ?? null;
+
+ if ($nilaiLangsung !== null) {
+ return self::normalisasiAkurasi($nilaiLangsung);
+ }
+
+ $topPredictions = $responseData['top_predictions'] ?? null;
+
+ if (! is_array($topPredictions) || count($topPredictions) === 0) {
+ return null;
+ }
+
+ if ($jurusan) {
+ foreach ($topPredictions as $item) {
+ if (($item['jurusan'] ?? null) === $jurusan) {
+ return self::normalisasiAkurasi($item['probabilitas'] ?? null);
+ }
+ }
+ }
+
+ return self::normalisasiAkurasi($topPredictions[0]['probabilitas'] ?? null);
+ }
+
private static function normalisasiAkurasi($value): ?float
{
if ($value === null || $value === '') {
@@ -194,28 +244,16 @@ private static function normalisasiAkurasi($value): ?float
$value = trim($value);
}
- if (!is_numeric($value)) {
+ if (! is_numeric($value)) {
return null;
}
$number = (float) $value;
- /*
- * Jika Flask mengirim 0.86, ubah menjadi 86.
- * Jika Flask mengirim 86.45, tetap 86.45.
- */
- if ($number <= 1) {
+ if ($number >= 0 && $number <= 1) {
$number = $number * 100;
}
- if ($number < 0) {
- $number = 0;
- }
-
- if ($number > 100) {
- $number = 100;
- }
-
return round($number, 2);
}
@@ -225,7 +263,9 @@ public static function getLabelEkonomiSingkat($gaji): string
if ($gaji < 2000000) {
return 'Kurang Mampu';
- } elseif ($gaji <= 5000000) {
+ }
+
+ if ($gaji <= 5000000) {
return 'Menengah';
}
@@ -237,60 +277,65 @@ public static function getLabelEkonomiDetail($gaji): string
$gaji = (float) $gaji;
if ($gaji < 2000000) {
- return 'Kurang Mampu (< Rp 2.000.000)';
- } elseif ($gaji <= 5000000) {
- return 'Menengah (Rp 2.000.000 - Rp 5.000.000)';
+ return 'Ekonomi Kurang Mampu';
}
- return 'Mampu (> Rp 5.000.000)';
+ if ($gaji <= 5000000) {
+ return 'Ekonomi Menengah';
+ }
+
+ return 'Ekonomi Mampu';
}
public static function getMinatUtamaLabel(RiwayatRekomendasi $riwayat): string
{
- $riasecScores = [
- 'r' => (float) $riwayat->riasec_r,
- 'i' => (float) $riwayat->riasec_i,
- 'a' => (float) $riwayat->riasec_a,
- 's' => (float) $riwayat->riasec_s,
- 'e' => (float) $riwayat->riasec_e,
- 'c' => (float) $riwayat->riasec_c,
+ $scores = [
+ 'Realistic' => (float) $riwayat->riasec_r,
+ 'Investigative' => (float) $riwayat->riasec_i,
+ 'Artistic' => (float) $riwayat->riasec_a,
+ 'Social' => (float) $riwayat->riasec_s,
+ 'Enterprising' => (float) $riwayat->riasec_e,
+ 'Conventional' => (float) $riwayat->riasec_c,
];
- arsort($riasecScores);
- $topCode = array_key_first($riasecScores);
+ arsort($scores);
- $labels = [
- 'r' => 'Teknis & Lapangan',
- 'i' => 'Sains, Riset & Analitis',
- 'a' => 'Kreatif & Desain',
- 's' => 'Sosial & Pelayanan',
- 'e' => 'Bisnis & Kepemimpinan',
- 'c' => 'Administratif & Terstruktur',
- ];
-
- return $labels[$topCode] ?? 'Belum teridentifikasi';
+ return array_key_first($scores) ?? 'Belum diketahui';
}
- public static function getProspekKerjaText($jurusan): string
- {
- $jurusan = strtolower((string) $jurusan);
- if (str_contains($jurusan, 'ekonomi') || str_contains($jurusan, 'manajemen') || str_contains($jurusan, 'bisnis')) {
- return 'Lulusan bidang ini dapat berkarier sebagai manajer bisnis, staf keuangan, analis pasar, konsultan bisnis, wirausahawan, maupun pegawai instansi pemerintah dan BUMN pada sektor ekonomi dan administrasi.';
- }
-
- if (str_contains($jurusan, 'teknik') || str_contains($jurusan, 'informatika') || str_contains($jurusan, 'komputer')) {
- return 'Lulusan bidang ini memiliki peluang karier sebagai software developer, analis sistem, teknisi, network engineer, data analyst, maupun profesional teknologi di perusahaan swasta, startup, dan instansi pemerintah.';
- }
-
- if (str_contains($jurusan, 'kesehatan') || str_contains($jurusan, 'keperawatan') || str_contains($jurusan, 'farmasi')) {
- return 'Lulusan bidang ini dapat berkarier di rumah sakit, klinik, laboratorium, industri kesehatan, layanan masyarakat, maupun lembaga penelitian kesehatan.';
- }
-
- if (str_contains($jurusan, 'pendidikan') || str_contains($jurusan, 'guru')) {
- return 'Lulusan bidang ini memiliki prospek sebagai guru, tenaga pendidik, instruktur pelatihan, pengembang kurikulum, maupun konsultan pendidikan.';
- }
-
- return 'Prospek karier untuk jurusan ini sangat luas, mulai dari spesialis industri, konsultan ahli, hingga pegawai instansi pemerintah/BUMN di sektor terkait.';
+public static function getProspekKerjaText(?string $jurusan): string
+{
+ if (! $jurusan) {
+ return 'Prospek kerja belum tersedia.';
}
+
+ // 1. Cari data Jurusan di database berdasarkan nama jurusan hasil prediksi
+ $dataJurusan = \App\Models\Jurusan::where('nama_jurusan', $jurusan)->first();
+
+ // 2. Jika jurusan tidak ditemukan atau tidak memiliki data prospek kerja
+ if (! $dataJurusan || $dataJurusan->prospeks->isEmpty()) {
+ return 'Prospek kerja untuk program studi ' . $jurusan . ' belum diinputkan oleh admin.';
+ }
+
+ // 3. Ambil semua data prospek kerja terkait dan susun menjadi format HTML list yang rapi
+ $htmlOutput = '
';
+
+ return $htmlOutput;
+}
}
\ No newline at end of file
diff --git a/app/Models/Alumni.php b/app/Models/Alumni.php
index bacf9a9..c848a1d 100644
--- a/app/Models/Alumni.php
+++ b/app/Models/Alumni.php
@@ -19,13 +19,18 @@ protected static function boot()
{
parent::boot();
+ // Otomatis membuat id_alumni yang unik dengan aman saat data dibuat
static::creating(function ($model) {
- $latest = static::orderBy('id', 'desc')->first();
- if (!$latest) {
- $model->id_alumni = 'AL01';
- } else {
- $number = intval(substr($latest->id_alumni, 2)) + 1;
- $model->id_alumni = 'AL' . str_pad($number, 2, '0', STR_PAD_LEFT);
+ if (empty($model->id_alumni)) {
+ $nextNumber = ((int) static::max('id')) + 1;
+ $candidate = 'ALM' . str_pad($nextNumber, 4, '0', STR_PAD_LEFT);
+
+ while (static::where('id_alumni', $candidate)->exists()) {
+ $nextNumber++;
+ $candidate = 'ALM' . str_pad($nextNumber, 4, '0', STR_PAD_LEFT);
+ }
+
+ $model->id_alumni = $candidate;
}
});
}
diff --git a/app/Models/Guru.php b/app/Models/Guru.php
index 5c86caa..7977f91 100644
--- a/app/Models/Guru.php
+++ b/app/Models/Guru.php
@@ -3,22 +3,37 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Guru extends Model
{
- protected $fillable = ['id_guru', 'nama_guru'];
+ // Memastikan nama tabel sesuai dengan database
+ protected $table = 'gurus';
+
+
+ protected $fillable = ['user_id', 'id_guru', 'nama_guru'];
+
+
+ public function user(): BelongsTo
+ {
+ return $this->belongsTo(User::class, 'user_id');
+ }
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
- $latest = static::orderBy('id', 'desc')->first();
- if (!$latest) {
- $model->id_guru = 'GR01';
- } else {
- $number = intval(substr($latest->id_guru, 2)) + 1;
- $model->id_guru = 'GR' . str_pad($number, 2, '0', STR_PAD_LEFT);
+ if (empty($model->id_guru)) {
+ $nextNumber = ((int) static::max('id')) + 1;
+ $candidate = 'GRU' . str_pad($nextNumber, 4, '0', STR_PAD_LEFT);
+
+ while (static::where('id_guru', $candidate)->exists()) {
+ $nextNumber++;
+ $candidate = 'GRU' . str_pad($nextNumber, 4, '0', STR_PAD_LEFT);
+ }
+
+ $model->id_guru = $candidate;
}
});
}
diff --git a/app/Models/Jurusan.php b/app/Models/Jurusan.php
index 5acc2e7..aa83ed1 100644
--- a/app/Models/Jurusan.php
+++ b/app/Models/Jurusan.php
@@ -6,7 +6,7 @@
class Jurusan extends Model
{
- protected $fillable = ['nama_jurusan', 'rumpun', 'deskripsi'];
+ protected $fillable = ['nama_jurusan', 'rumpun',];
// Relasi: Satu Jurusan memiliki banyak Prospek Kerja
public function prospeks()
diff --git a/app/Models/Siswa.php b/app/Models/Siswa.php
index 67ac74b..d5df437 100644
--- a/app/Models/Siswa.php
+++ b/app/Models/Siswa.php
@@ -5,17 +5,15 @@
use Illuminate\Database\Eloquent\Model;
class Siswa extends Model
+
{
+ protected $table = 'siswas';
protected $fillable = [
'id_siswa',
'user_id',
- 'jurusan_id',
'nama_lengkap',
'nisn',
- 'kelas',
- 'peminatan',
- 'minat_bakat',
- 'ekonomi'
+ 'kelas'
];
protected static function boot()
@@ -39,8 +37,4 @@ protected static function boot()
public function user() {
return $this->belongsTo(User::class);
}
-
- public function jurusan() {
- return $this->belongsTo(Jurusan::class);
- }
}
\ No newline at end of file
diff --git a/app/Models/User.php b/app/Models/User.php
index b379c22..873e05a 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -11,9 +11,8 @@
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
- use HasFactory, Notifiable;
use HasApiTokens, HasFactory, Notifiable;
-
+
/**
* The attributes that are mass assignable.
*
@@ -49,4 +48,4 @@ protected function casts(): array
'password' => 'hashed',
];
}
-}
+}
\ No newline at end of file
diff --git a/app/Services/PythonRecommendationService.php b/app/Services/PythonRecommendationService.php
new file mode 100644
index 0000000..9f7c673
--- /dev/null
+++ b/app/Services/PythonRecommendationService.php
@@ -0,0 +1,89 @@
+baseUrl = rtrim((string) config('services.python_api.url'), '/');
+ $this->timeout = (int) config('services.python_api.timeout', 30);
+ }
+
+ public function health(): array
+ {
+ try {
+ if ($this->baseUrl === '') {
+ return [
+ 'success' => false,
+ 'status_code' => 500,
+ 'data' => null,
+ 'message' => 'PYTHON_API_URL belum diatur pada file .env.',
+ ];
+ }
+
+ $response = Http::timeout($this->timeout)
+ ->acceptJson()
+ ->get($this->baseUrl . '/health');
+
+ return [
+ 'success' => $response->successful(),
+ 'status_code' => $response->status(),
+ 'data' => $response->json(),
+ 'message' => $response->successful()
+ ? 'Laravel berhasil terhubung ke Flask API.'
+ : 'Laravel gagal membaca Flask API.',
+ ];
+ } catch (Throwable $e) {
+ return [
+ 'success' => false,
+ 'status_code' => 500,
+ 'data' => null,
+ 'message' => 'Tidak dapat terhubung ke Flask API: ' . $e->getMessage(),
+ ];
+ }
+ }
+
+ public function predict(array $payload): array
+ {
+ try {
+ if ($this->baseUrl === '') {
+ return [
+ 'success' => false,
+ 'status_code' => 500,
+ 'data' => null,
+ 'message' => 'PYTHON_API_URL belum diatur pada file .env.',
+ ];
+ }
+
+ $response = Http::timeout($this->timeout)
+ ->acceptJson()
+ ->asJson()
+ ->post($this->baseUrl . '/predict', $payload);
+
+ $json = $response->json();
+
+ return [
+ 'success' => $response->successful(),
+ 'status_code' => $response->status(),
+ 'data' => $json,
+ 'message' => $response->successful()
+ ? 'Prediksi berhasil diproses oleh Flask API.'
+ : ($json['message'] ?? $response->body() ?? 'Prediksi gagal diproses oleh Flask API.'),
+ ];
+ } catch (Throwable $e) {
+ return [
+ 'success' => false,
+ 'status_code' => 500,
+ 'data' => null,
+ 'message' => 'Koneksi ke Flask API gagal: ' . $e->getMessage(),
+ ];
+ }
+ }
+}
\ No newline at end of file
diff --git a/config/services.php b/config/services.php
index 6a90eb8..182f531 100644
--- a/config/services.php
+++ b/config/services.php
@@ -6,12 +6,6 @@
|--------------------------------------------------------------------------
| Third Party Services
|--------------------------------------------------------------------------
- |
- | This file is for storing the credentials for third party services such
- | as Mailgun, Postmark, AWS and more. This file provides the de facto
- | location for this type of information, allowing packages to have
- | a conventional file to locate the various service credentials.
- |
*/
'postmark' => [
@@ -35,4 +29,15 @@
],
],
-];
+ /*
+ |--------------------------------------------------------------------------
+ | Python Flask API
+ |--------------------------------------------------------------------------
+ */
+
+ 'python_api' => [
+ 'url' => env('PYTHON_API_URL', 'https://galangadji.pythonanywhere.com'),
+ 'timeout' => env('PYTHON_API_TIMEOUT', 30),
+ ],
+
+];
\ No newline at end of file
diff --git a/flask_api/requirements.txt b/flask_api/requirements.txt
index 4aa5253..6b12213 100644
--- a/flask_api/requirements.txt
+++ b/flask_api/requirements.txt
@@ -2,6 +2,6 @@ flask
flask-cors
pandas
numpy
-scikit-learn==1.6.1
+scikit-learn==1.8.0
joblib
openpyxl
\ No newline at end of file
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..b7aacdb
--- /dev/null
+++ b/index.php
@@ -0,0 +1,18 @@
+usePublicPath(__DIR__);
+
+$app->handleRequest(Request::capture());
\ No newline at end of file
diff --git a/resources/views/admin/alumni/alumni.blade.php b/resources/views/admin/alumni/alumni.blade.php
index 88d42e6..0d9ab86 100644
--- a/resources/views/admin/alumni/alumni.blade.php
+++ b/resources/views/admin/alumni/alumni.blade.php
@@ -45,6 +45,7 @@
Opsi
+
@@ -65,13 +66,39 @@
@endsection
\ No newline at end of file
diff --git a/resources/views/admin/alumni/alumniedit.blade.php b/resources/views/admin/alumni/alumniedit.blade.php
index 6db4408..e779902 100644
--- a/resources/views/admin/alumni/alumniedit.blade.php
+++ b/resources/views/admin/alumni/alumniedit.blade.php
@@ -15,23 +15,32 @@
@@ -40,51 +49,159 @@
-@endpush
@endsection
\ No newline at end of file
diff --git a/resources/views/admin/guru/guru.blade.php b/resources/views/admin/guru/guru.blade.php
index 6674bf8..ce673d6 100644
--- a/resources/views/admin/guru/guru.blade.php
+++ b/resources/views/admin/guru/guru.blade.php
@@ -43,6 +43,7 @@
Opsi
+
@@ -63,13 +64,39 @@
@endsection
\ No newline at end of file
diff --git a/resources/views/admin/guru/guruedit.blade.php b/resources/views/admin/guru/guruedit.blade.php
index 8febcbf..fd8aab0 100644
--- a/resources/views/admin/guru/guruedit.blade.php
+++ b/resources/views/admin/guru/guruedit.blade.php
@@ -15,15 +15,22 @@
@@ -32,47 +39,155 @@
@endsection
\ No newline at end of file
diff --git a/resources/views/admin/jurusan/jurusanedit.blade.php b/resources/views/admin/jurusan/jurusanedit.blade.php
index 60361d8..77562ef 100644
--- a/resources/views/admin/jurusan/jurusanedit.blade.php
+++ b/resources/views/admin/jurusan/jurusanedit.blade.php
@@ -15,11 +15,13 @@
@@ -43,50 +47,156 @@
-@endpush
@endsection
\ No newline at end of file
diff --git a/resources/views/admin/siswa/siswa.blade.php b/resources/views/admin/siswa/siswa.blade.php
index 2b4646d..38a44ec 100644
--- a/resources/views/admin/siswa/siswa.blade.php
+++ b/resources/views/admin/siswa/siswa.blade.php
@@ -21,271 +21,187 @@
-
-
-
-
- No
- ID Siswa
- Nama Lengkap
- NISN
- Kelas
- Opsi
-
-
-
-
-
- Memuat data siswa...
-
-
-
-
-
-
-
diff --git a/resources/views/admin/siswa/siswaedit.blade.php b/resources/views/admin/siswa/siswaedit.blade.php
index e3df4ab..848aec3 100644
--- a/resources/views/admin/siswa/siswaedit.blade.php
+++ b/resources/views/admin/siswa/siswaedit.blade.php
@@ -7,43 +7,76 @@
-
Edit Data Siswa
-
Memuat data...
+
Edit Data Profil Siswa
+
Ubah rincian informasi dan akun profil siswa.
-
-
-
+
+
+
+
+
+
+ Informasi Akun Login
+
+
+
+
+
+ Username Login (Opsional)
+
+
+
+
+ Password Baru (Opsional)
+
+
+
+
+
+ Data Profil Siswa
+
+
+
+
- Nama Lengkap
+ Nama Lengkap
-
-
@@ -52,71 +85,134 @@
@endsection
\ No newline at end of file
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php
index 3330576..a732cb6 100644
--- a/resources/views/auth/login.blade.php
+++ b/resources/views/auth/login.blade.php
@@ -75,20 +75,26 @@ class="input-modern w-full text-slate-900"
-
+
-
@@ -153,7 +75,7 @@
- Isi data berikut jika ingin membuat rekomendasi baru. Jika ingin melihat hasil sebelumnya, klik menu Hasil Rekomendasi pada navbar.
+ Isi data berikut untuk mendapatkan rekomendasi jurusan.
@@ -161,7 +83,6 @@
-
- Isi dengan angka saja tanpa titik atau koma.
-
+ class="w-full rounded-2xl border border-slate-200 bg-white px-4 py-4 text-base font-semibold text-slate-700 outline-none focus:border-emerald-400 focus:ring-4 focus:ring-emerald-100">
-
-
-
-
+
-
-
-
-
-
- ASGARD.
-
-
-
- Sistem rekomendasi jurusan kuliah SMAN 4 Jember berbasis profil siswa dan data akademik.
-
-
-
-
-
-
-
- Sekolah
-
-
-
- SMAN 4 Jember
- Jl. Wolter Monginsidi No.24
- Jember, Jawa Timur
-
-
-
-
-
+
+
© 2026 GALANG SEFIAN ADJI - POLITEKNIK NEGERI JEMBER
@@ -431,39 +256,60 @@ class="w-full sm:w-2/3 bg-emerald-600 hover:bg-emerald-700 text-white font-bold