From f64ea3a4fa988c6f88c4be54d7821dda1e3890c6 Mon Sep 17 00:00:00 2001 From: Lutfi Hakim Date: Thu, 30 Apr 2026 07:51:09 +0700 Subject: [PATCH] Fix: kategori table migration and update admin buku views --- .../Controllers/VisitorKatalogController.php | 68 +- ...025_10_13_000000_create_kategori_table.php | 28 + database/seeders/BukuTamuSeeder.php | 10 + database/seeders/DatabaseSeeder.php | 29 +- resources/views/admin/buku/edit.blade.php | 59 +- resources/views/admin/buku/index.blade.php | 380 ++++---- .../views/admin/peminjaman/create.blade.php | 144 +-- .../views/admin/peminjaman/index.blade.php | 920 ++++++++++-------- .../views/visitor/katalog/index.blade.php | 382 ++++---- .../views/visitor/katalog/show.blade.php | 765 ++++++++------- resources/views/welcome/populer.blade.php | 175 ++-- resources/views/welcome/rekomendasi.blade.php | 147 +-- 12 files changed, 1698 insertions(+), 1409 deletions(-) create mode 100644 database/migrations/2025_10_13_000000_create_kategori_table.php diff --git a/app/Http/Controllers/VisitorKatalogController.php b/app/Http/Controllers/VisitorKatalogController.php index cd07f7c..2343db9 100644 --- a/app/Http/Controllers/VisitorKatalogController.php +++ b/app/Http/Controllers/VisitorKatalogController.php @@ -10,12 +10,12 @@ class VisitorKatalogController extends Controller public function index(Request $request) { $originalSearch = $request->input('search'); - + $bukuPopuler = Buku::withCount('peminjaman') - ->orderBy('peminjaman_count', 'desc') - ->take(5) - ->get(); - + ->orderBy('peminjaman_count', 'desc') + ->take(5) + ->get(); + if (empty($originalSearch)) { $buku = Buku::paginate(12); $search = $originalSearch; @@ -25,19 +25,19 @@ public function index(Request $request) // 1. Text Preprocessing (Stopword Removal) // Array stopword bahasa Indonesia sederhana $stopwords = ['buku', 'tentang', 'yang', 'di', 'ke', 'dari', 'mencari', 'untuk', 'dan', 'ini', 'itu', 'adalah', 'pada', 'dengan', 'sebuah', 'cara']; - + // Membersihkan input: mengubah ke lowercase dan membuang karakter non-alphanumeric $cleanInput = preg_replace('/[^\p{L}\p{N}\s]/u', '', strtolower($originalSearch)); $words = explode(' ', $cleanInput); - + // Filter stopword - $filteredWords = array_filter($words, function($word) use ($stopwords) { + $filteredWords = array_filter($words, function ($word) use ($stopwords) { return !in_array($word, $stopwords) && trim($word) !== ''; }); - + // String hasil preprocessing yang murni siap diproses ke Cosine Similarity $processedSearch = implode(' ', $filteredWords); - + if (empty($processedSearch)) { // Jika setelah dibersihkan query pencarian menjadi kosong (misal pencarian hanya "buku tentang") $buku = new \Illuminate\Pagination\LengthAwarePaginator([], 0, 12, 1, ['path' => $request->url(), 'query' => $request->query()]); @@ -54,7 +54,7 @@ public function index(Request $request) // Hitung nilai Cosine Similarity untuk masing-masing atribut berdasarkan kata kunci yang sudah dibersihkan $judulSimilarity = $this->calculateSimilarity($processedSearch, $b->judul); $pengarangSimilarity = $this->calculateSimilarity($processedSearch, $b->pengarang); - + // Gabungkan penerbit, deskripsi, dan kategori sebagai atribut teks tambahan $teksTambahan = trim(($b->penerbit ?? '') . ' ' . ($b->deskripsi ?? '') . ' ' . ($b->kategori->nama_kategori ?? '')); $tambahanSimilarity = $this->calculateSimilarity($processedSearch, $teksTambahan); @@ -72,7 +72,7 @@ public function index(Request $request) } // Urutkan berdasarkan similarity_score terbesar (descending) - usort($hasil, function($a, $b) { + usort($hasil, function ($a, $b) { return $b->similarity_score <=> $a->similarity_score; }); @@ -98,10 +98,10 @@ public function index(Request $request) private function calculateSimilarity($query, $text) { if (empty(trim($text))) return 0; - + $queryWords = explode(' ', strtolower(preg_replace('/[^\p{L}\p{N}\s]/u', '', $query))); $textWords = explode(' ', strtolower(preg_replace('/[^\p{L}\p{N}\s]/u', '', $text))); - + // Frekuensi kata $vecA = array_count_values(array_filter($queryWords)); $vecB = array_count_values(array_filter($textWords)); @@ -124,10 +124,10 @@ private function calculateSimilarity($query, $text) if ($normA == 0 || $normB == 0) return 0; // Standard Cosine Similarity: $dotProduct / (sqrt($normA) * sqrt($normB)) - // Kelemahannya: Jika judul buku sangat panjang (normB besar), skor akan anjlok (misal < 0.2) + // Kelemahannya: Jika judul buku sangat panjang (normB besar), skor akan anjlok (misal < 0.2) // padahal kata pencariannya cocok 100%. // Modifikasi menjadi Query Coverage (Pembagi dominan adalah panjang query). - return $dotProduct / $normA; + return $dotProduct / $normA; } public function show($id) @@ -135,15 +135,19 @@ public function show($id) $buku = Buku::with(['kategori', 'peminjaman.user'])->findOrFail($id); $lokasi = $this->prediksiLokasiRak($buku->nomor_panggil); $rekomendasi = $this->getRekomendasi($buku); - + return view('visitor.katalog.show', compact('buku', 'lokasi', 'rekomendasi')); } private function getRekomendasi($targetBuku) { - $semuaBuku = Buku::where('id_buku', '!=', $targetBuku->id_buku) - ->whereNotNull('nomor_panggil') - ->get(); + $semuaBuku = Buku::where('id', '!=', $targetBuku->id) + ->get(); + + // Jika nomor_panggil tidak ada, tampilkan rekomendasi berdasarkan buku populer + if (empty($targetBuku->nomor_panggil)) { + return $semuaBuku->take(4); + } $hasil = []; $targetDdc = substr(preg_replace('/[^0-9]/', '', $targetBuku->nomor_panggil), 0, 3); @@ -153,6 +157,8 @@ private function getRekomendasi($targetBuku) } foreach ($semuaBuku as $b) { + if (empty($b->nomor_panggil)) continue; + $pembandingDdc = substr(preg_replace('/[^0-9]/', '', $b->nomor_panggil), 0, 3); if (strlen($pembandingDdc) < 3) continue; @@ -173,7 +179,7 @@ private function getRekomendasi($targetBuku) } } - usort($hasil, function($a, $b) { + usort($hasil, function ($a, $b) { return $b->similarity_score <=> $a->similarity_score; }); @@ -189,23 +195,23 @@ private function prediksiLokasiRak($nomor_panggil) $kode_utama = (int) substr(trim($nomor_panggil), 0, 3); return match (true) { - $kode_utama >= 0 && $kode_utama <= 99 => match(true) { + $kode_utama >= 0 && $kode_utama <= 99 => match (true) { $kode_utama <= 19 => ['rak' => 'Rak 01', 'area' => 'Karya Umum (Bibliografi)', 'kode_rak' => 'rak_000'], $kode_utama <= 50 => ['rak' => 'Rak 02', 'area' => 'Karya Umum (Ensiklopedia)', 'kode_rak' => 'rak_000'], default => ['rak' => 'Rak 03-05', 'area' => 'Karya Umum Lainnya', 'kode_rak' => 'rak_000'], }, - - $kode_utama >= 100 && $kode_utama <= 199 => match(true) { + + $kode_utama >= 100 && $kode_utama <= 199 => match (true) { $kode_utama <= 150 => ['rak' => 'Rak 06-10', 'area' => 'Filsafat', 'kode_rak' => 'rak_100'], default => ['rak' => 'Rak 11-14', 'area' => 'Psikologi', 'kode_rak' => 'rak_100'], }, - $kode_utama >= 200 && $kode_utama <= 299 => match(true) { + $kode_utama >= 200 && $kode_utama <= 299 => match (true) { $kode_utama == 297 => ['rak' => 'Rak 25-32', 'area' => 'Agama Islam', 'kode_rak' => 'rak_200'], default => ['rak' => 'Rak 15-24', 'area' => 'Agama Umum & Lainnya', 'kode_rak' => 'rak_200'], }, - $kode_utama >= 300 && $kode_utama <= 399 => match(true) { + $kode_utama >= 300 && $kode_utama <= 399 => match (true) { $kode_utama <= 330 => ['rak' => 'Rak 33-36', 'area' => 'Sosiologi & Ilmu Politik', 'kode_rak' => 'rak_300'], $kode_utama <= 360 => ['rak' => 'Rak 37-40', 'area' => 'Ekonomi & Hukum', 'kode_rak' => 'rak_300'], default => ['rak' => 'Rak 41-44', 'area' => 'Pendidikan & Adat', 'kode_rak' => 'rak_300'], @@ -215,14 +221,14 @@ private function prediksiLokasiRak($nomor_panggil) $kode_utama >= 500 && $kode_utama <= 599 => ['rak' => 'Rak 46-48', 'area' => 'Ilmu Murni', 'kode_rak' => 'rak_500'], - $kode_utama >= 600 && $kode_utama <= 699 => match(true) { + $kode_utama >= 600 && $kode_utama <= 699 => match (true) { $kode_utama <= 610 => ['rak' => 'Rak 49-53', 'area' => 'Ilmu Kedokteran', 'kode_rak' => 'rak_600'], $kode_utama <= 630 => ['rak' => 'Rak 54-58', 'area' => 'Ilmu Teknik', 'kode_rak' => 'rak_600'], $kode_utama <= 650 => ['rak' => 'Rak 59-63', 'area' => 'Pertanian', 'kode_rak' => 'rak_600'], default => ['rak' => 'Rak 64-68', 'area' => 'Manajemen & Bisnis', 'kode_rak' => 'rak_600'], }, - $kode_utama >= 700 && $kode_utama <= 799 => match(true) { + $kode_utama >= 700 && $kode_utama <= 799 => match (true) { $kode_utama <= 739 => ['rak' => 'Rak 71', 'area' => 'Kesenian Murni', 'kode_rak' => 'rak_700'], $kode_utama <= 769 => ['rak' => 'Rak 72', 'area' => 'Seni Rupa & Kriya', 'kode_rak' => 'rak_700'], $kode_utama <= 789 => ['rak' => 'Rak 73', 'area' => 'Fotografi & Musik', 'kode_rak' => 'rak_700'], @@ -230,8 +236,8 @@ private function prediksiLokasiRak($nomor_panggil) }, $kode_utama >= 800 && $kode_utama <= 899 => ['rak' => 'Rak 77-79', 'area' => 'Kesusastraan (Sastra)', 'kode_rak' => 'rak_800'], - - $kode_utama >= 900 && $kode_utama <= 999 => match(true) { + + $kode_utama >= 900 && $kode_utama <= 999 => match (true) { $kode_utama <= 919 => ['rak' => 'Rak 69, 70', 'area' => 'Geografi & Perjalanan', 'kode_rak' => 'rak_900'], default => ['rak' => 'Rak 80-84', 'area' => 'Sejarah Umum', 'kode_rak' => 'rak_900'], }, @@ -239,4 +245,4 @@ private function prediksiLokasiRak($nomor_panggil) default => ['rak' => 'Rak 75-76', 'area' => 'Koleksi Terbaru', 'kode_rak' => 'rak_baru'], }; } -} \ No newline at end of file +} diff --git a/database/migrations/2025_10_13_000000_create_kategori_table.php b/database/migrations/2025_10_13_000000_create_kategori_table.php new file mode 100644 index 0000000..01f0c66 --- /dev/null +++ b/database/migrations/2025_10_13_000000_create_kategori_table.php @@ -0,0 +1,28 @@ +increments('id_kategori'); + $table->string('nama_kategori'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('kategori'); + } +}; diff --git a/database/seeders/BukuTamuSeeder.php b/database/seeders/BukuTamuSeeder.php index c69d2df..3198957 100644 --- a/database/seeders/BukuTamuSeeder.php +++ b/database/seeders/BukuTamuSeeder.php @@ -19,11 +19,21 @@ public function run(): void DB::table('buku_tamu')->insert([ [ 'id_user' => $userId, + 'nama_tamu' => 'Pengunjung 1', + 'email' => 'pengunjung1@example.com', + 'no_hp' => '081234567890', + 'asal_instansi' => 'Sekolah A', + 'status' => 'Aktif', 'tujuan_kunjungan' => 'Membaca Buku', 'tanggal_kunjungan' => now(), ], [ 'id_user' => $userId, + 'nama_tamu' => 'Pengunjung 2', + 'email' => 'pengunjung2@example.com', + 'no_hp' => '082345678901', + 'asal_instansi' => 'Sekolah B', + 'status' => 'Aktif', 'tujuan_kunjungan' => 'Meminjam Buku', 'tanggal_kunjungan' => now()->subDay(), ], diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 5b251bf..11e4d08 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -16,20 +16,24 @@ class DatabaseSeeder extends Seeder public function run(): void { // Admin user - User::create([ - 'name' => 'Admin', - 'email' => 'admin@perpustakaan.com', - 'password' => Hash::make('password'), - 'role' => 'admin', - ]); + User::updateOrCreate( + ['email' => 'admin@perpustakaan.com'], + [ + 'name' => 'Admin', + 'password' => Hash::make('password'), + 'role' => 'admin', + ] + ); // Regular user - User::create([ - 'name' => 'User Test', - 'email' => 'user@perpustakaan.com', - 'password' => Hash::make('password'), - 'role' => 'user', - ]); + User::updateOrCreate( + ['email' => 'user@perpustakaan.com'], + [ + 'name' => 'User Test', + 'password' => Hash::make('password'), + 'role' => 'user', + ] + ); // Sample buku Buku::create([ @@ -67,4 +71,3 @@ public function run(): void ]); } } - diff --git a/resources/views/admin/buku/edit.blade.php b/resources/views/admin/buku/edit.blade.php index 8638194..b154686 100644 --- a/resources/views/admin/buku/edit.blade.php +++ b/resources/views/admin/buku/edit.blade.php @@ -8,31 +8,35 @@ @if ($errors->any()) - + @endif -
+ @csrf @method('PUT') - +
- +
- - @foreach($kategori as $kat) - @endforeach @@ -48,47 +52,56 @@
- +
- +
- +
- +
- +
- +
- +
- +
- Batal + Batal Simpan Perubahan diff --git a/resources/views/admin/buku/index.blade.php b/resources/views/admin/buku/index.blade.php index beca68b..440e26e 100644 --- a/resources/views/admin/buku/index.blade.php +++ b/resources/views/admin/buku/index.blade.php @@ -3,208 +3,230 @@ @section('title', 'Katalog Buku') @section('content') -
- - - - - - - - -
- - - - Cari - - -
- - - - - BIBID - Judul & Pengarang - Kategori - Stok - Aksi +
+ + + - - @forelse ($buku as $item) - - {{ $item->bibid }} - -
{{ $item->judul }}
-
{{ $item->pengarang }}
- - {{ $item->kategori->nama_kategori ?? 'Umum' }} - {{ $item->eksemplar }} - -
- - Edit - -
- @csrf - @method('DELETE') - -
-
- - - @empty - - - Tidak ada data aset buku ditemukan. - - - @endforelse - +
-
- {{ $buku->withQueryString()->links() }} + + +
+
+ + + Cari + +
- - -