diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..518f05e --- /dev/null +++ b/.htaccess @@ -0,0 +1,26 @@ + + RewriteEngine On + RewriteBase /E31230887/ + + # 1. Handle Storage (Internal Rewrite) + # Mapping /storage/... ke /public/storage/... secara internal + RewriteRule ^storage/(.*)$ public/storage/$1 [L] + + # 2. Handle Other Static Assets (img, build, images, favicon, robots.txt) + # Menghindari redirect R=302 agar URL tetap bersih tanpa /public/ + RewriteCond %{REQUEST_URI} !^/E31230887/public/ + RewriteRule ^(img|build|images|favicon\.ico|robots\.txt)(.*)$ public/$1$2 [L] + + # 3. Handle Laravel Front Controller + # Jika file fisik tidak ada, lempar ke index.php + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^ index.php [L] + + +Options -Indexes + + + Order allow,deny + Deny from all + diff --git a/app/Http/Controllers/AdminBukuController.php b/app/Http/Controllers/AdminBukuController.php index 845181a..cf74ded 100644 --- a/app/Http/Controllers/AdminBukuController.php +++ b/app/Http/Controllers/AdminBukuController.php @@ -117,41 +117,41 @@ private function tentukanKoordinatRak($nomor_panggil) return match (true) { $kode_utama >= 0 && $kode_utama <= 99 => match (true) { - $kode_utama <= 19 => ['x' => 13.00, 'y' => 22.00], // Rak 01 - $kode_utama <= 50 => ['x' => 18.00, 'y' => 22.00], // Rak 02 - default => ['x' => 25.00, 'y' => 22.00], // Rak 03-05 + $kode_utama <= 19 => ['x' => 12.00, 'y' => 13.00], // Rak 01 + $kode_utama <= 50 => ['x' => 17.00, 'y' => 13.00], // Rak 02 + default => ['x' => 22.00, 'y' => 13.00], // Rak 03-05 }, $kode_utama >= 100 && $kode_utama <= 199 => match (true) { - $kode_utama <= 150 => ['x' => 43.00, 'y' => 20.00], // Rak 06-10 - default => ['x' => 57.00, 'y' => 20.00], // Rak 11-14 + $kode_utama <= 150 => ['x' => 35.00, 'y' => 13.00], // Rak 06-10 + default => ['x' => 35.00, 'y' => 17.00], // Rak 11-14 }, $kode_utama >= 200 && $kode_utama <= 299 => match (true) { - $kode_utama == 297 => ['x' => 25.00, 'y' => 38.00], // Rak 25-32 (Islam) - default => ['x' => 13.00, 'y' => 38.00], // Rak 15-24 + $kode_utama == 297 => ['x' => 14.00, 'y' => 38.00], // Rak 25-32 (Islam) + default => ['x' => 14.00, 'y' => 30.00], // Rak 15-24 }, $kode_utama >= 300 && $kode_utama <= 399 => match (true) { - $kode_utama <= 330 => ['x' => 43.00, 'y' => 30.00], // Rak 33-36 - $kode_utama <= 360 => ['x' => 50.00, 'y' => 30.00], // Rak 37-40 - default => ['x' => 57.00, 'y' => 30.00], // Rak 41-44 + $kode_utama <= 330 => ['x' => 38.00, 'y' => 23.00], // Rak 33-36 + $kode_utama <= 360 => ['x' => 43.00, 'y' => 23.00], // Rak 37-40 + default => ['x' => 48.00, 'y' => 23.00], // Rak 41-44 }, - $kode_utama >= 400 && $kode_utama <= 499 => ['x' => 18.00, 'y' => 62.00], // Rak 45 - $kode_utama >= 500 && $kode_utama <= 599 => ['x' => 50.00, 'y' => 74.00], // Rak 46-48 + $kode_utama >= 400 && $kode_utama <= 499 => ['x' => 14.00, 'y' => 58.00], // Rak 45 + $kode_utama >= 500 && $kode_utama <= 599 => ['x' => 38.00, 'y' => 72.00], // Rak 46-48 $kode_utama >= 600 && $kode_utama <= 699 => match (true) { - $kode_utama <= 610 => ['x' => 35.00, 'y' => 85.00], // Rak 49-53 - $kode_utama <= 630 => ['x' => 45.00, 'y' => 85.00], // Rak 54-58 - $kode_utama <= 650 => ['x' => 55.00, 'y' => 85.00], // Rak 59-63 - default => ['x' => 65.00, 'y' => 85.00], // Rak 64-68 + $kode_utama <= 610 => ['x' => 28.00, 'y' => 83.00], // Rak 49-53 + $kode_utama <= 630 => ['x' => 36.00, 'y' => 83.00], // Rak 54-58 + $kode_utama <= 650 => ['x' => 44.00, 'y' => 83.00], // Rak 59-63 + default => ['x' => 52.00, 'y' => 83.00], // Rak 64-68 }, $kode_utama >= 700 && $kode_utama <= 799 => match (true) { - $kode_utama <= 739 => ['x' => 77.00, 'y' => 22.00], // Rak 71 - $kode_utama <= 769 => ['x' => 82.00, 'y' => 22.00], // Rak 72 - $kode_utama <= 789 => ['x' => 87.00, 'y' => 22.00], // Rak 73 - default => ['x' => 82.00, 'y' => 22.00], // Rak 74 + $kode_utama <= 739 => ['x' => 82.00, 'y' => 12.00], // Rak 71 + $kode_utama <= 769 => ['x' => 86.00, 'y' => 12.00], // Rak 72 + $kode_utama <= 789 => ['x' => 82.00, 'y' => 16.00], // Rak 73 + default => ['x' => 86.00, 'y' => 16.00], // Rak 74 }, - $kode_utama >= 800 && $kode_utama <= 899 => ['x' => 82.00, 'y' => 32.00], // Rak 77-79 + $kode_utama >= 800 && $kode_utama <= 899 => ['x' => 82.00, 'y' => 25.00], // Rak 77-79 $kode_utama >= 900 && $kode_utama <= 999 => match (true) { - $kode_utama <= 919 => ['x' => 77.00, 'y' => 42.00], // Rak 69-70 - default => ['x' => 87.00, 'y' => 42.00], // Rak 80-84 + $kode_utama <= 919 => ['x' => 82.00, 'y' => 30.00], // Rak 69-70 + default => ['x' => 82.00, 'y' => 35.00], // Rak 80-84 }, default => ['x' => null, 'y' => null], }; diff --git a/app/Http/Controllers/AdminPeminjamanController.php b/app/Http/Controllers/AdminPeminjamanController.php index 702460a..9f2c8f5 100644 --- a/app/Http/Controllers/AdminPeminjamanController.php +++ b/app/Http/Controllers/AdminPeminjamanController.php @@ -58,84 +58,49 @@ public function store(Request $request) $validated = $request->validate([ 'id_anggota' => 'required|exists:anggotas,id', 'id_buku' => 'required|exists:buku,id_buku', + 'id_buku_2' => 'nullable|exists:buku,id_buku|different:id_buku', 'tanggal_pinjam' => 'required|date_format:Y-m-d|after_or_equal:2000-01-01|before_or_equal:2100-12-31', 'tanggal_kembali' => 'required|date_format:Y-m-d|after_or_equal:tanggal_pinjam|before_or_equal:2100-12-31', ]); - $validated['status_peminjaman'] = 'Dipinjam'; + $status = 'Dipinjam'; - // 2. Kurangi Stok Buku - $buku = Buku::findOrFail($validated['id_buku']); - $buku->decrement('eksemplar'); + // 2. Kurangi Stok Buku Pertama & Simpan + $buku1 = Buku::findOrFail($validated['id_buku']); + $buku1->decrement('eksemplar'); - // 3. Simpan ke Database - $peminjaman = Peminjaman::create($validated); - $peminjaman->load(['buku', 'anggota']); + $peminjaman1 = Peminjaman::create([ + 'id_anggota' => $validated['id_anggota'], + 'id_buku' => $validated['id_buku'], + 'tanggal_pinjam' => $validated['tanggal_pinjam'], + 'tanggal_kembali' => $validated['tanggal_kembali'], + 'status_peminjaman' => $status, + ]); + $peminjaman1->load(['buku', 'anggota']); - $waSuccess = false; + $waSuccess = $this->kirimWaPeminjaman($peminjaman1, false); - // 4. Proses Kirim WA (Format Struk Teks Resmi) - try { - $targetNum = $peminjaman->anggota->no_hp ?? ''; - $fonnteToken = 'vpzqxF2ZGgTGz9F5UbUS'; // Token Fonnte + // 3. Proses Buku Kedua (Opsional) + if (!empty($validated['id_buku_2'])) { + $buku2 = Buku::findOrFail($validated['id_buku_2']); + $buku2->decrement('eksemplar'); - // Standarisasi Format Nomor ke awalan 62 - if (!empty($targetNum)) { - $targetNum = preg_replace('/^0/', '62', trim($targetNum)); + $peminjaman2 = Peminjaman::create([ + 'id_anggota' => $validated['id_anggota'], + 'id_buku' => $validated['id_buku_2'], + 'tanggal_pinjam' => $validated['tanggal_pinjam'], + 'tanggal_kembali' => $validated['tanggal_kembali'], + 'status_peminjaman' => $status, + ]); + $peminjaman2->load(['buku', 'anggota']); + + $wa2Success = $this->kirimWaPeminjaman($peminjaman2, false); + if ($wa2Success) { + $waSuccess = true; } - - if (!empty($targetNum) && !empty($fonnteToken)) { - // Merangkai Teks Menyerupai Struk Kertas - $pesanStruk = "🏢 *PERPUSTAKAAN DAERAH JEMBER*\n"; - $pesanStruk .= "Jl. Mastrip No. 1, Kabupaten Jember\n"; - $pesanStruk .= "===============================\n\n"; - $pesanStruk .= "📄 *BUKTI PEMINJAMAN BUKU*\n"; - $pesanStruk .= "No. Transaksi : PMJ-{$peminjaman->id_peminjaman}\n"; - $pesanStruk .= "Tanggal Cetak : " . \Carbon\Carbon::now()->format('d-m-Y H:i') . "\n\n"; - $pesanStruk .= "*DATA PEMINJAM*\n"; - $pesanStruk .= "Nama : {$peminjaman->anggota->nama}\n"; - $pesanStruk .= "No. HP : {$peminjaman->anggota->no_hp}\n\n"; - $pesanStruk .= "*DETAIL BUKU*\n"; - $pesanStruk .= "Judul : {$peminjaman->buku->judul}\n"; - $pesanStruk .= "Kode Pengembalian : {$peminjaman->buku->bibid}\n"; - $pesanStruk .= "Pinjam : " . \Carbon\Carbon::parse($peminjaman->tanggal_pinjam)->format('d F Y') . "\n"; - $pesanStruk .= "Kembali: *" . \Carbon\Carbon::parse($peminjaman->tanggal_kembali)->format('d F Y') . "*\n\n"; - $pesanStruk .= "===============================\n"; - $pesanStruk .= "⚠️ *Catatan:*\n"; - $pesanStruk .= "Tunjukkan Kode Pengembalian ke Admin saat pengembalian buku.\n"; - $pesanStruk .= "Harap kembalikan buku tepat waktu.\n"; - $pesanStruk .= "Denda keterlambatan: Rp 1.000/hari.\n\n"; - $pesanStruk .= "Terima kasih atas kunjungan Anda!\n"; - $pesanStruk .= "_Sistem Sarakata - TA 2026_"; - - // Eksekusi Pengiriman via Http Laravel - $response = \Illuminate\Support\Facades\Http::withoutVerifying()->timeout(15)->withHeaders([ - 'Authorization' => $fonnteToken, - ])->post('https://api.fonnte.com/send', [ - 'target' => $targetNum, - 'message' => $pesanStruk, - ]); - - if ($response->successful() && ($response->json('status') == true)) { - $waSuccess = true; - } else { - \Illuminate\Support\Facades\Log::warning("Fonnte Log: Gagal Terkirim", [ - 'target' => $targetNum, - 'http_status' => $response->status(), - 'body' => $response->body() - ]); - } - } else { - \Illuminate\Support\Facades\Log::warning('Fonnte Log: Token / No HP kosong', [ - 'target' => $targetNum ?? 'kosong', - 'token_set' => !empty($fonnteToken), - ]); - } - } catch (\Exception $e) { - \Illuminate\Support\Facades\Log::error("Error WA Pengiriman: " . $e->getMessage()); } - // 5. Notifikasi Kembali ke Layar Admin + // 4. Notifikasi Kembali ke Layar Admin $msg = 'Transaksi peminjaman berhasil dicatat.'; if ($waSuccess) { $msg .= ' Struk WA berhasil terkirim kepada Anggota.'; @@ -310,6 +275,16 @@ public function resendWa($id) { $peminjaman = Peminjaman::with(['buku', 'anggota'])->findOrFail($id); + $success = $this->kirimWaPeminjaman($peminjaman, true); + if ($success) { + return back()->with('success', 'Struk WhatsApp berhasil dikirim ulang ke nomor ' . $peminjaman->anggota->no_hp); + } else { + return back()->with('error', 'Gagal mengirim ulang WA. Silakan cek log Fonnte.'); + } + } + + private function kirimWaPeminjaman($peminjaman, $isSalinan = false) + { try { $targetNum = $peminjaman->anggota->no_hp ?? ''; $fonnteToken = env('FONNTE_TOKEN', 'vpzqxF2ZGgTGz9F5UbUS'); @@ -319,10 +294,12 @@ public function resendWa($id) } if (!empty($targetNum) && !empty($fonnteToken)) { + $tipeStruk = $isSalinan ? "SALINAN BUKTI PEMINJAMAN" : "BUKTI PEMINJAMAN BUKU"; + $pesanStruk = "🏢 *PERPUSTAKAAN DAERAH JEMBER*\n"; $pesanStruk .= "Jl. Mastrip No. 1, Kabupaten Jember\n"; $pesanStruk .= "===============================\n\n"; - $pesanStruk .= "📄 *SALINAN BUKTI PEMINJAMAN*\n"; + $pesanStruk .= "📄 *{$tipeStruk}*\n"; $pesanStruk .= "No. Transaksi : PMJ-{$peminjaman->id_peminjaman}\n"; $pesanStruk .= "Tanggal Cetak : " . \Carbon\Carbon::now()->format('d-m-Y H:i') . "\n\n"; $pesanStruk .= "*DATA PEMINJAM*\n"; @@ -330,12 +307,12 @@ public function resendWa($id) $pesanStruk .= "No. HP : {$peminjaman->anggota->no_hp}\n\n"; $pesanStruk .= "*DETAIL BUKU*\n"; $pesanStruk .= "Judul : {$peminjaman->buku->judul}\n"; - $pesanStruk .= "Kode Panggil : {$peminjaman->buku->bibid}\n"; + $pesanStruk .= "Kode Pengembalian : {$peminjaman->buku->bibid}\n"; $pesanStruk .= "Pinjam : " . \Carbon\Carbon::parse($peminjaman->tanggal_pinjam)->format('d F Y') . "\n"; $pesanStruk .= "Kembali: *" . \Carbon\Carbon::parse($peminjaman->tanggal_kembali)->format('d F Y') . "*\n\n"; $pesanStruk .= "===============================\n"; $pesanStruk .= "⚠️ *Catatan:*\n"; - $pesanStruk .= "Tunjukkan Kode Panggil ke Admin saat pengembalian buku.\n"; + $pesanStruk .= "Tunjukkan Kode Pengembalian ke Admin saat pengembalian buku.\n"; $pesanStruk .= "Harap kembalikan buku tepat waktu.\n"; $pesanStruk .= "Denda keterlambatan: Rp 1.000/hari.\n\n"; $pesanStruk .= "Terima kasih atas kunjungan Anda!\n"; @@ -349,17 +326,18 @@ public function resendWa($id) ]); if ($response->successful() && ($response->json('status') == true)) { - return back()->with('success', 'Struk WhatsApp berhasil dikirim ulang ke nomor ' . $targetNum); + return true; } else { - \Illuminate\Support\Facades\Log::warning("Fonnte Log: Gagal Terkirim Ulang", ['body' => $response->body()]); - return back()->with('error', 'Gagal mengirim ulang WA. API Fonnte menolak koneksi (Status: '.$response->status().').'); + \Illuminate\Support\Facades\Log::warning("Fonnte Log: Gagal Terkirim", [ + 'target' => $targetNum, + 'http_status' => $response->status(), + 'body' => $response->body() + ]); } - } else { - return back()->with('error', 'Nomor HP anggota kosong atau Token Fonnte belum dikonfigurasi.'); } } catch (\Exception $e) { - \Illuminate\Support\Facades\Log::error("Error WA Pengiriman Ulang: " . $e->getMessage()); - return back()->with('error', 'Terjadi kesalahan sistem saat menghubungi server WhatsApp.'); + \Illuminate\Support\Facades\Log::error("Error WA Pengiriman: " . $e->getMessage()); } + return false; } } diff --git a/app/Http/Controllers/LaporanController.php b/app/Http/Controllers/LaporanController.php index 27e56da..fb04f2d 100644 --- a/app/Http/Controllers/LaporanController.php +++ b/app/Http/Controllers/LaporanController.php @@ -8,23 +8,85 @@ class LaporanController extends Controller { - public function kehadiran() + public function kehadiran(Request $request) { - $bukuTamu = BukuTamu::with('user') - ->orderBy('tanggal_kunjungan', 'desc') - ->get(); + $query = BukuTamu::with('user')->orderBy('tanggal_kunjungan', 'desc'); + + // Filter Bulan & Tahun + if ($request->filled('bulan')) { + $query->whereMonth('tanggal_kunjungan', $request->input('bulan')); + } + if ($request->filled('tahun')) { + $query->whereYear('tanggal_kunjungan', $request->input('tahun')); + } + + $bukuTamu = $query->get(); + + // Slice data berdasarkan rentang baris (Limit Cetak) + if ($request->filled('limit_start') || $request->filled('limit_end')) { + $start = $request->filled('limit_start') ? (int)$request->input('limit_start') : 1; + $offset = max(0, $start - 1); + if ($request->filled('limit_end')) { + $end = (int)$request->input('limit_end'); + $length = max(0, $end - $offset); + $bukuTamu = $bukuTamu->slice($offset, $length); + } else { + $bukuTamu = $bukuTamu->slice($offset); + } + } return view('laporan.kehadiran', compact('bukuTamu')); } public function peminjaman(Request $request) { - $query = Peminjaman::with(['anggota', 'user', 'buku'])->orderBy('tanggal_pinjam', 'desc'); + $query = Peminjaman::with(['anggota', 'user', 'buku.kategori'])->orderBy('tanggal_pinjam', 'desc'); - // Optional filtering by month/year if needed (can be added later) + // 1. Filter Bulan & Tahun + if ($request->filled('bulan')) { + $query->whereMonth('tanggal_pinjam', $request->input('bulan')); + } + if ($request->filled('tahun')) { + $query->whereYear('tanggal_pinjam', $request->input('tahun')); + } + + // 2. Filter Kategori Buku (Berdasarkan DDC / Nomor Panggil) + if ($request->filled('id_kategori')) { + $classDigit = $request->input('id_kategori'); // '0', '1', ..., '9' + $query->whereHas('buku', function ($q) use ($classDigit) { + $q->where('nomor_panggil', 'like', "{$classDigit}%"); + }); + } $peminjaman = $query->get(); - return view('laporan.peminjaman', compact('peminjaman')); + // Slice data berdasarkan rentang baris (Limit Cetak) + if ($request->filled('limit_start') || $request->filled('limit_end')) { + $start = $request->filled('limit_start') ? (int)$request->input('limit_start') : 1; + $offset = max(0, $start - 1); + if ($request->filled('limit_end')) { + $end = (int)$request->input('limit_end'); + $length = max(0, $end - $offset); + $peminjaman = $peminjaman->slice($offset, $length); + } else { + $peminjaman = $peminjaman->slice($offset); + } + } + + // Kategori berdasarkan DDC & Lokasi Rak sesuai permintaan user + $categories = [ + '0' => '000-099 : Karya Umum (Rak 01-05)', + '1' => '100-199 : Filsafat & Psikologi (Rak 06-14)', + '2' => '200-299 : Agama (Rak 15-32)', + '3' => '300-399 : Ilmu Sosial (Rak 33-44)', + '4' => '400-499 : Bahasa (Rak 45)', + '5' => '500-599 : Ilmu Murni / Sains (Rak 46-48)', + '6' => '600-699 : Ilmu Terapan (Rak 49-68)', + '7' => '700-799 : Kesenian & Olahraga (Rak 71-74)', + '8' => '800-899 : Sastra (Rak 77-79)', + '9' => '900-999 : Geografi & Sejarah (Rak 69-70, Rak 80-84)' + ]; + + return view('laporan.peminjaman', compact('peminjaman', 'categories')); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 13b704d..1bcb398 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -7,20 +7,19 @@ class AppServiceProvider extends ServiceProvider { - /** - * Register any application services. - */ public function register(): void { // } - /** - * Bootstrap any application services. - */ public function boot(): void { Paginator::useTailwind(); - } + // Paksa URL Utama & HTTPS di Production untuk menjamin asset() & route() konsisten + if (config('app.env') !== 'local') { + \Illuminate\Support\Facades\URL::forceRootUrl(config('app.url')); + \Illuminate\Support\Facades\URL::forceScheme('https'); + } + } } diff --git a/buat_link.php b/buat_link.php new file mode 100644 index 0000000..4e2df68 --- /dev/null +++ b/buat_link.php @@ -0,0 +1,31 @@ +🔧 Memperbaiki Jembatan Storage (Symlink)"; +echo "

Target: $target

"; +echo "

Link: $link

"; + +if (file_exists($link)) { + echo "

⚠️ Menghapus link/folder storage lama...

"; + if (is_link($link)) { + unlink($link); + } else { + // Jika ternyata folder biasa (bukan link), kita amankan dengan ganti nama + rename($link, $link . '_backup_' . time()); + } +} + +if (symlink($target, $link)) { + echo "

✅ Jembatan Storage Berhasil Dibuat!

"; + echo "

Sekarang gambar buku Anda seharusnya sudah muncul di website.

"; + echo "Kembali ke Website"; +} else { + echo "

❌ Gagal membuat jembatan symlink.

"; + echo "

Beberapa hosting membatasi fungsi symlink(). Jika ini terjadi, beri tahu asisten AI Anda untuk menggunakan metode .htaccess redirect.

"; +} diff --git a/check_link.php b/check_link.php new file mode 100644 index 0000000..baf174b --- /dev/null +++ b/check_link.php @@ -0,0 +1,8 @@ +id(); - $table->string('bibid')->nullable(); - $table->string('judul'); - $table->string('edisi')->nullable(); - $table->string('pengarang'); - $table->string('penerbit'); - $table->year('tahun_terbit')->nullable(); - $table->text('deskripsi_fisik')->nullable(); - $table->string('nomor_panggil')->nullable(); - $table->string('konten_digital')->nullable(); - $table->integer('eksemplar')->default(1); - $table->unsignedBigInteger('id_kategori')->nullable(); - $table->string('cover')->nullable(); - $table->float('lokasi_x')->nullable(); - $table->float('lokasi_y')->nullable(); - $table->timestamps(); - }); - } - - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('bukus'); - } -}; diff --git a/debug.php b/debug.php new file mode 100644 index 0000000..ff0d8ac --- /dev/null +++ b/debug.php @@ -0,0 +1,13 @@ +"; +echo "DOCUMENT_ROOT: " . $_SERVER['DOCUMENT_ROOT'] . "\n"; +echo "REQUEST_URI: " . $_SERVER['REQUEST_URI'] . "\n"; +echo "SCRIPT_FILENAME: " . $_SERVER['SCRIPT_FILENAME'] . "\n"; +echo "__DIR__: " . __DIR__ . "\n"; +echo "\n--- Cek keberadaan file ---\n"; +echo "img/denah.webp via __DIR__: " . (file_exists(__DIR__ . '/img/denah.webp') ? '✅ ADA' : '❌ TIDAK ADA') . "\n"; +echo "public/img/denah.webp via __DIR__: " . (file_exists(__DIR__ . '/public/img/denah.webp') ? '✅ ADA' : '❌ TIDAK ADA') . "\n"; +echo "DOCUMENT_ROOT/img/denah.webp: " . (file_exists($_SERVER['DOCUMENT_ROOT'] . '/img/denah.webp') ? '✅ ADA' : '❌ TIDAK ADA') . "\n"; +echo "DOCUMENT_ROOT/E31230887/img/denah.webp: " . (file_exists($_SERVER['DOCUMENT_ROOT'] . '/E31230887/img/denah.webp') ? '✅ ADA' : '❌ TIDAK ADA') . "\n"; +echo ""; diff --git a/index.php b/index.php new file mode 100644 index 0000000..ffdc62c --- /dev/null +++ b/index.php @@ -0,0 +1,4 @@ + + + + + + + +
@@ -34,18 +59,29 @@ class="w-full border-gray-300 rounded-lg shadow-sm focus:border-blue-500 focus:r
-
- - +
+
+ + +
+
+ + +
@@ -83,5 +119,131 @@ class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-8 rounded-lg s btn.innerHTML = 'Memproses...'; btn.classList.add('opacity-50', 'cursor-not-allowed'); }); + + let scanBuffer = ""; + let lastKeyTime = Date.now(); + + // Global barcode scan detection + document.addEventListener('keypress', function(e) { + const target = e.target; + + if (target.tagName === 'INPUT' && target.type !== 'submit' && target.type !== 'button' && target.type !== 'checkbox' && target.type !== 'radio') { + if (!target.classList.contains('ts-control') && !target.closest('.ts-wrapper')) { + return; + } + } + if (target.tagName === 'TEXTAREA' || target.tagName === 'SELECT') { + return; + } + + const currentTime = Date.now(); + + if (currentTime - lastKeyTime > 100) { + scanBuffer = ""; + } + + lastKeyTime = currentTime; + + if (e.key === 'Enter') { + if (scanBuffer.length >= 2) { + e.preventDefault(); + const cleanCode = scanBuffer.trim(); + handleGlobalScan(cleanCode); + scanBuffer = ""; + } + } else { + if (e.key.length === 1) { + scanBuffer += e.key; + } + } + }); + + function handleGlobalScan(bibid) { + const select1 = document.getElementById('select-buku-peminjaman'); + const select2 = document.getElementById('select-buku-peminjaman-2'); + + if (!select1) return; + + function findVal(selectEl) { + const options = selectEl.options; + for (let i = 0; i < options.length; i++) { + if (options[i].getAttribute('data-bibid') === bibid) { + return { value: options[i].value, text: options[i].text }; + } + } + return null; + } + + const item = findVal(select1); + if (!item) { + Swal.fire({ + icon: 'error', + title: 'Buku Tidak Ditemukan', + text: 'Kode buku "' + bibid + '" tidak terdaftar atau tidak tersedia.', + timer: 2000, + showConfirmButton: false, + toast: true, + position: 'top-end' + }); + return; + } + + let targetSelect = null; + if (select1.tomselect && !select1.tomselect.getValue()) { + targetSelect = select1; + } else if (select2 && select2.tomselect && !select2.tomselect.getValue()) { + targetSelect = select2; + } else { + targetSelect = select1; + } + + if (targetSelect && targetSelect.tomselect) { + const otherSelect = (targetSelect === select1) ? select2 : select1; + if (otherSelect && otherSelect.tomselect && otherSelect.tomselect.getValue() === item.value) { + Swal.fire({ + icon: 'warning', + title: 'Buku Sudah Dipilih', + text: 'Buku ini sudah dipilih pada slot lainnya.', + timer: 2000, + showConfirmButton: false, + toast: true, + position: 'top-end' + }); + return; + } + + targetSelect.tomselect.setValue(item.value); + Swal.fire({ + icon: 'success', + title: 'Buku Berhasil Dipilih', + text: item.text, + timer: 1500, + showConfirmButton: false, + toast: true, + position: 'top-end' + }); + } + } + + document.addEventListener('DOMContentLoaded', function() { + // Initialize TomSelect for books + const ts1 = new TomSelect('#select-buku-peminjaman', { maxOptions: null }); + const ts2 = new TomSelect('#select-buku-peminjaman-2', { maxOptions: null }); + + // Focus on first book dropdown + setTimeout(() => { + ts1.focus(); + }, 100); + + // Prevent Enter key from submitting form on text/select inputs + const forms = document.querySelectorAll('form'); + forms.forEach(form => { + form.addEventListener('keydown', function(e) { + if (e.key === 'Enter' && e.target.tagName !== 'BUTTON' && e.target.type !== 'submit') { + e.preventDefault(); + } + }); + }); + }); @endsection diff --git a/resources/views/admin/peminjaman/index.blade.php b/resources/views/admin/peminjaman/index.blade.php index acfb7e2..0587932 100644 --- a/resources/views/admin/peminjaman/index.blade.php +++ b/resources/views/admin/peminjaman/index.blade.php @@ -16,6 +16,16 @@ tanggal_pinjam: '{{ old('tanggal_pinjam') ?? '' }}', tanggal_kembali: '{{ old('tanggal_kembali') ?? '' }}' }, + init() { + this.$watch('isModalPeminjamanOpen', value => { + if (value) { + setTimeout(() => { + const ts1 = document.getElementById('select-buku-peminjaman'); + if (ts1 && ts1.tomselect) ts1.tomselect.focus(); + }, 200); + } + }); + }, openEditModal(id, anggotaId, buku, pinjam, kembali) { this.editData = { id: id, id_anggota: anggotaId, id_buku: buku, tanggal_pinjam: pinjam, tanggal_kembali: kembali }; this.isModalEditOpen = true; @@ -252,19 +262,34 @@ class="mt-1 block w-full border-gray-200 focus:border-indigo-500 focus:ring-indi
-
- - +
+
+ + +
+
+ + +
@@ -376,7 +401,7 @@ class="bg-green-50 text-green-700 p-3 rounded-lg text-center font-bold text-xs m
-
+ @csrf @method('PUT')
@@ -424,7 +449,7 @@ class="w-10 h-10 rounded-full bg-gray-50 hover:bg-red-50 text-gray-400 hover:tex
- + @csrf @method('PUT') @@ -517,7 +542,7 @@ class="text-gray-400 hover:text-gray-600 transition-colors"> buku akan otomatis dikembalikan jika status masih dipinjam.

- + @csrf @method('DELETE') - -
+ @if(session('error')) + + @endif + + @if(isset($peminjaman)) +
+
+

Data Transaksi Ditemukan

+ #{{ $peminjaman->id_peminjaman }} +
+ +
+
+
+ Peminjam + {{ $peminjaman->anggota?->nama ?? ($peminjaman->user?->name ?? 'Anonim') }} +
+
+ Aset Buku + {{ $buku->judul }} + {{ $buku->bibid }} +
+
+ Tenggat Waktu + + {{ \Carbon\Carbon::parse($peminjaman->tanggal_kembali)->format('d F Y') }} + +
+
+ +
+
+ Panduan + Pengembalian Rak Fisik + {{ $lokasi['rak'] }} + {{ $lokasi['area'] }} +
+ + @if($denda > 0) +
+ Denda Keterlambatan + Rp + {{ number_format($denda, 0, ',', '.') }} +
+ @endif +
+
+ +
+
+ @csrf + @method('PUT') + +
+
+
+ @endif
- @endif -
-@endsection +@endsection \ No newline at end of file diff --git a/resources/views/laporan/kehadiran.blade.php b/resources/views/laporan/kehadiran.blade.php index 39e4f8a..35e70a1 100644 --- a/resources/views/laporan/kehadiran.blade.php +++ b/resources/views/laporan/kehadiran.blade.php @@ -5,14 +5,78 @@ @section('content') -
-

Berikut adalah daftar rekapitulasi kehadiran pengunjung dan anggota perpustakaan.

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + @if(request()->anyFilled(['bulan', 'tahun', 'limit_start', 'limit_end'])) + + + + @endif +
+
+ +
+

Berikut adalah daftar rekapitulasi kehadiran pengunjung yang disaring.

+ + + No @@ -24,7 +88,7 @@ @forelse($bukuTamu as $item) - {{ $loop->iteration }} + {{ (request('limit_start') ? (int)request('limit_start') : 1) + $loop->index }} @if($item->user) {{-- Member path: data dari users --}} @@ -59,22 +123,68 @@ diff --git a/resources/views/laporan/peminjaman.blade.php b/resources/views/laporan/peminjaman.blade.php index 5817c7a..25aa8be 100644 --- a/resources/views/laporan/peminjaman.blade.php +++ b/resources/views/laporan/peminjaman.blade.php @@ -5,14 +5,91 @@ @section('content') -
-

Berikut adalah seluruh rekap data peminjaman buku perpustakaan.

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + @if(request()->anyFilled(['bulan', 'tahun', 'id_kategori', 'limit_start', 'limit_end'])) + + + + @endif +
+
+ +
+

Berikut adalah rekap data peminjaman buku perpustakaan yang disaring.

+ + + No @@ -27,7 +104,7 @@ @forelse($peminjaman as $index => $item) - {{ $index + 1 }} + {{ (request('limit_start') ? (int)request('limit_start') : 1) + $loop->index }} @if($item->anggota)
{{ $item->anggota->nama }}
@@ -80,22 +157,68 @@ diff --git a/resources/views/layouts/admin.blade.php b/resources/views/layouts/admin.blade.php index 366a129..45e263c 100644 --- a/resources/views/layouts/admin.blade.php +++ b/resources/views/layouts/admin.blade.php @@ -1,11 +1,13 @@ + @yield('title', 'Admin Dashboard') - SARAKATA - + @@ -16,15 +18,20 @@ extend: { fontFamily: { sans: ['Inter', 'sans-serif'] }, colors: { - primary: { 50:'#eef2ff',100:'#e0e7ff',200:'#c7d2fe',300:'#a5b4fc',400:'#818cf8',500:'#6366f1',600:'#4f46e5',700:'#4338ca',800:'#3730a3',900:'#312e81' }, + primary: { 50: '#eef2ff', 100: '#e0e7ff', 200: '#c7d2fe', 300: '#a5b4fc', 400: '#818cf8', 500: '#6366f1', 600: '#4f46e5', 700: '#4338ca', 800: '#3730a3', 900: '#312e81' }, } } } } + {{-- TOP HEADER --}} -
+
- - + -

SARAKATA

+

SARAKATA

@csrf -
- +
- + {{-- SIDEBAR --}} -