Compare commits
10 Commits
18529760d7
...
17c93a4252
| Author | SHA1 | Date |
|---|---|---|
|
|
17c93a4252 | |
|
|
5f30be4811 | |
|
|
1071e4e428 | |
|
|
bf0996430a | |
|
|
a656f599a4 | |
|
|
077e2746f6 | |
|
|
570b134f7c | |
|
|
70a420ae47 | |
|
|
ca98d0a665 | |
|
|
c2a61ea5a0 |
|
|
@ -0,0 +1,26 @@
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
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]
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
Options -Indexes
|
||||||
|
|
||||||
|
<FilesMatch "^\.env">
|
||||||
|
Order allow,deny
|
||||||
|
Deny from all
|
||||||
|
</FilesMatch>
|
||||||
|
|
@ -20,20 +20,24 @@ public function store(Request $request)
|
||||||
'bibid' => 'required|string|max:30|unique:buku,bibid',
|
'bibid' => 'required|string|max:30|unique:buku,bibid',
|
||||||
'judul' => 'required|string',
|
'judul' => 'required|string',
|
||||||
'pengarang' => 'required|string|max:100',
|
'pengarang' => 'required|string|max:100',
|
||||||
'penerbit' => 'nullable|string',
|
'penerbit' => 'required|string',
|
||||||
'tahun_terbit' => 'nullable|digits:4',
|
'tahun_terbit' => 'required|digits:4',
|
||||||
'edisi' => 'nullable|string|max:50',
|
'edisi' => 'required|digits:4',
|
||||||
'deskripsi_fisik' => 'nullable|string|max:100',
|
'deskripsi_fisik' => 'required|string|max:100',
|
||||||
'nomor_panggil' => 'required|string|max:50',
|
'nomor_panggil' => 'required|string|max:50|unique:buku,nomor_panggil',
|
||||||
'eksemplar' => 'required|integer|min:1',
|
'eksemplar' => 'required|integer|min:1',
|
||||||
'id_kategori' => 'required|exists:kategori,id_kategori',
|
'id_kategori' => 'required|exists:kategori,id_kategori',
|
||||||
'cover' => 'nullable|image|mimes:jpeg,png,jpg|max:2048'
|
'cover' => 'image|mimes:jpeg,png,jpg,webp|max:2048',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($request->hasFile('cover')) {
|
if ($request->hasFile('cover')) {
|
||||||
$validated['cover'] = $request->file('cover')->store('covers', 'public');
|
$validated['cover'] = $request->file('cover')->store('covers', 'public');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$koordinat = $this->tentukanKoordinatRak($validated['nomor_panggil']);
|
||||||
|
$validated['lokasi_x'] = $koordinat['x'];
|
||||||
|
$validated['lokasi_y'] = $koordinat['y'];
|
||||||
|
|
||||||
$validated['konten_digital'] = 0;
|
$validated['konten_digital'] = 0;
|
||||||
Buku::create($validated);
|
Buku::create($validated);
|
||||||
|
|
||||||
|
|
@ -71,14 +75,14 @@ public function update(Request $request, $id)
|
||||||
'bibid' => 'required|string|max:30|unique:buku,bibid,' . $id . ',id_buku',
|
'bibid' => 'required|string|max:30|unique:buku,bibid,' . $id . ',id_buku',
|
||||||
'judul' => 'required|string',
|
'judul' => 'required|string',
|
||||||
'pengarang' => 'required|string|max:100',
|
'pengarang' => 'required|string|max:100',
|
||||||
'penerbit' => 'nullable|string',
|
'penerbit' => 'required|string',
|
||||||
'tahun_terbit' => 'nullable|digits:4',
|
'tahun_terbit' => 'required|digits:4',
|
||||||
'edisi' => 'nullable|string|max:50',
|
'edisi' => 'required|digits:4',
|
||||||
'deskripsi_fisik' => 'nullable|string|max:100',
|
'deskripsi_fisik' => 'required|string|max:100',
|
||||||
'nomor_panggil' => 'required|string|max:50',
|
'nomor_panggil' => 'required|string|max:50|unique:buku,nomor_panggil,' . $id . ',id_buku',
|
||||||
'eksemplar' => 'required|integer|min:1',
|
'eksemplar' => 'required|integer|min:1',
|
||||||
'id_kategori' => 'required|exists:kategori,id_kategori',
|
'id_kategori' => 'required|exists:kategori,id_kategori',
|
||||||
'cover' => 'nullable|image|mimes:jpeg,png,jpg|max:2048'
|
'cover' => 'image|mimes:jpeg,png,jpg,webp|max:2048'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($request->hasFile('cover')) {
|
if ($request->hasFile('cover')) {
|
||||||
|
|
@ -88,6 +92,10 @@ public function update(Request $request, $id)
|
||||||
$validated['cover'] = $request->file('cover')->store('covers', 'public');
|
$validated['cover'] = $request->file('cover')->store('covers', 'public');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$koordinat = $this->tentukanKoordinatRak($validated['nomor_panggil']);
|
||||||
|
$validated['lokasi_x'] = $koordinat['x'];
|
||||||
|
$validated['lokasi_y'] = $koordinat['y'];
|
||||||
|
|
||||||
$buku->update($validated);
|
$buku->update($validated);
|
||||||
|
|
||||||
return redirect()->route('admin.buku.index')->with('success', 'Aset buku berhasil diperbarui.');
|
return redirect()->route('admin.buku.index')->with('success', 'Aset buku berhasil diperbarui.');
|
||||||
|
|
@ -100,4 +108,52 @@ public function destroy($id)
|
||||||
|
|
||||||
return redirect()->route('admin.buku.index')->with('success', 'Aset buku berhasil dihapus dari sistem.');
|
return redirect()->route('admin.buku.index')->with('success', 'Aset buku berhasil dihapus dari sistem.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function tentukanKoordinatRak($nomor_panggil)
|
||||||
|
{
|
||||||
|
if (empty($nomor_panggil)) return ['x' => null, 'y' => null];
|
||||||
|
|
||||||
|
$kode_utama = (int) substr(trim(preg_replace('/[^0-9]/', '', $nomor_panggil)), 0, 3);
|
||||||
|
|
||||||
|
return match (true) {
|
||||||
|
$kode_utama >= 0 && $kode_utama <= 99 => match (true) {
|
||||||
|
$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' => 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' => 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' => 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' => 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' => 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' => 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' => 25.00], // Rak 77-79
|
||||||
|
$kode_utama >= 900 && $kode_utama <= 999 => match (true) {
|
||||||
|
$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],
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,8 @@
|
||||||
use App\Models\Buku;
|
use App\Models\Buku;
|
||||||
use App\Models\Anggota;
|
use App\Models\Anggota;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Barryvdh\DomPDF\Facade\Pdf;
|
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
|
|
||||||
class AdminPeminjamanController extends Controller
|
class AdminPeminjamanController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -35,14 +33,14 @@ public function index(Request $request)
|
||||||
$peminjaman = $query->paginate(15)->appends(['search' => $search]);
|
$peminjaman = $query->paginate(15)->appends(['search' => $search]);
|
||||||
|
|
||||||
$buku = Buku::orderBy('judul', 'asc')->get();
|
$buku = Buku::orderBy('judul', 'asc')->get();
|
||||||
$anggota = Anggota::orderBy('nama', 'asc')->get();
|
$anggota = Anggota::latest()->get();
|
||||||
return view('admin.peminjaman.index', compact('peminjaman', 'buku', 'anggota', 'search'));
|
return view('admin.peminjaman.index', compact('peminjaman', 'buku', 'anggota', 'search'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
$buku = Buku::where('eksemplar', '>', 0)->orderBy('judul', 'asc')->get();
|
$buku = Buku::where('eksemplar', '>', 0)->orderBy('judul', 'asc')->get();
|
||||||
$anggota = Anggota::orderBy('nama', 'asc')->get();
|
$anggota = Anggota::latest()->get();
|
||||||
return view('admin.peminjaman.create', compact('buku', 'anggota'));
|
return view('admin.peminjaman.create', compact('buku', 'anggota'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +48,7 @@ public function edit($id)
|
||||||
{
|
{
|
||||||
$peminjaman = Peminjaman::findOrFail($id);
|
$peminjaman = Peminjaman::findOrFail($id);
|
||||||
$buku = Buku::all();
|
$buku = Buku::all();
|
||||||
$anggota = Anggota::orderBy('nama', 'asc')->get();
|
$anggota = Anggota::latest()->get();
|
||||||
return view('admin.peminjaman.edit', compact('peminjaman', 'buku', 'anggota'));
|
return view('admin.peminjaman.edit', compact('peminjaman', 'buku', 'anggota'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,84 +58,49 @@ public function store(Request $request)
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'id_anggota' => 'required|exists:anggotas,id',
|
'id_anggota' => 'required|exists:anggotas,id',
|
||||||
'id_buku' => 'required|exists:buku,id_buku',
|
'id_buku' => 'required|exists:buku,id_buku',
|
||||||
'tanggal_pinjam' => 'required|date',
|
'id_buku_2' => 'nullable|exists:buku,id_buku|different:id_buku',
|
||||||
'tanggal_kembali' => 'required|date|after_or_equal:tanggal_pinjam',
|
'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
|
// 2. Kurangi Stok Buku Pertama & Simpan
|
||||||
$buku = Buku::findOrFail($validated['id_buku']);
|
$buku1 = Buku::findOrFail($validated['id_buku']);
|
||||||
$buku->decrement('eksemplar');
|
$buku1->decrement('eksemplar');
|
||||||
|
|
||||||
// 3. Simpan ke Database
|
$peminjaman1 = Peminjaman::create([
|
||||||
$peminjaman = Peminjaman::create($validated);
|
'id_anggota' => $validated['id_anggota'],
|
||||||
$peminjaman->load(['buku', '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)
|
// 3. Proses Buku Kedua (Opsional)
|
||||||
try {
|
if (!empty($validated['id_buku_2'])) {
|
||||||
$targetNum = $peminjaman->anggota->no_hp ?? '';
|
$buku2 = Buku::findOrFail($validated['id_buku_2']);
|
||||||
$fonnteToken = 'vpzqxF2ZGgTGz9F5UbUS'; // Token Fonnte
|
$buku2->decrement('eksemplar');
|
||||||
|
|
||||||
// Standarisasi Format Nomor ke awalan 62
|
$peminjaman2 = Peminjaman::create([
|
||||||
if (!empty($targetNum)) {
|
'id_anggota' => $validated['id_anggota'],
|
||||||
$targetNum = preg_replace('/^0/', '62', trim($targetNum));
|
'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.';
|
$msg = 'Transaksi peminjaman berhasil dicatat.';
|
||||||
if ($waSuccess) {
|
if ($waSuccess) {
|
||||||
$msg .= ' Struk WA berhasil terkirim kepada Anggota.';
|
$msg .= ' Struk WA berhasil terkirim kepada Anggota.';
|
||||||
|
|
@ -197,42 +160,42 @@ private function prediksiLokasiRakUntukAdmin($nomor_panggil)
|
||||||
|
|
||||||
return match (true) {
|
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'],
|
$kode_utama <= 19 => ['rak' => 'Rak 01', 'area' => 'Karya Umum'],
|
||||||
$kode_utama <= 50 => ['rak' => 'Rak 02', 'area' => 'Karya Umum'],
|
$kode_utama <= 50 => ['rak' => 'Rak 02', 'area' => 'Karya Umum'],
|
||||||
default => ['rak' => 'Rak 03-05', 'area' => 'Karya Umum Lainnya'],
|
default => ['rak' => 'Rak 03-05', 'area' => 'Karya Umum Lainnya'],
|
||||||
},
|
},
|
||||||
$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_utama <= 150 => ['rak' => 'Rak 06-10', 'area' => 'Filsafat'],
|
||||||
default => ['rak' => 'Rak 11-14', 'area' => 'Psikologi'],
|
default => ['rak' => 'Rak 11-14', 'area' => 'Psikologi'],
|
||||||
},
|
},
|
||||||
$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_utama == 297 => ['rak' => 'Rak 25-32', 'area' => 'Agama Islam'],
|
||||||
default => ['rak' => 'Rak 15-24', 'area' => 'Agama Umum'],
|
default => ['rak' => 'Rak 15-24', 'area' => 'Agama Umum'],
|
||||||
},
|
},
|
||||||
$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 & Politik'],
|
$kode_utama <= 330 => ['rak' => 'Rak 33-36', 'area' => 'Sosiologi & Politik'],
|
||||||
$kode_utama <= 360 => ['rak' => 'Rak 37-40', 'area' => 'Ekonomi & Hukum'],
|
$kode_utama <= 360 => ['rak' => 'Rak 37-40', 'area' => 'Ekonomi & Hukum'],
|
||||||
default => ['rak' => 'Rak 41-44', 'area' => 'Pendidikan & Adat'],
|
default => ['rak' => 'Rak 41-44', 'area' => 'Pendidikan & Adat'],
|
||||||
},
|
},
|
||||||
$kode_utama >= 400 && $kode_utama <= 499 => ['rak' => 'Rak 45', 'area' => 'Bahasa'],
|
$kode_utama >= 400 && $kode_utama <= 499 => ['rak' => 'Rak 45', 'area' => 'Bahasa'],
|
||||||
$kode_utama >= 500 && $kode_utama <= 599 => ['rak' => 'Rak 46-48', 'area' => 'Ilmu Murni'],
|
$kode_utama >= 500 && $kode_utama <= 599 => ['rak' => 'Rak 46-48', 'area' => 'Ilmu Murni'],
|
||||||
$kode_utama >= 600 && $kode_utama <= 699 => match (true) {
|
$kode_utama >= 600 && $kode_utama <= 699 => match (true) {
|
||||||
$kode_utama <= 610 => ['rak' => 'Rak 49-53', 'area' => 'Kedokteran'],
|
$kode_utama <= 610 => ['rak' => 'Rak 49-53', 'area' => 'Kedokteran'],
|
||||||
$kode_utama <= 630 => ['rak' => 'Rak 54-58', 'area' => 'Teknik'],
|
$kode_utama <= 630 => ['rak' => 'Rak 54-58', 'area' => 'Teknik'],
|
||||||
$kode_utama <= 650 => ['rak' => 'Rak 59-63', 'area' => 'Pertanian'],
|
$kode_utama <= 650 => ['rak' => 'Rak 59-63', 'area' => 'Pertanian'],
|
||||||
default => ['rak' => 'Rak 64-68', 'area' => 'Manajemen Bisnis'],
|
default => ['rak' => 'Rak 64-68', 'area' => 'Manajemen Bisnis'],
|
||||||
},
|
},
|
||||||
$kode_utama >= 700 && $kode_utama <= 799 => match (true) {
|
$kode_utama >= 700 && $kode_utama <= 799 => match (true) {
|
||||||
$kode_utama <= 739 => ['rak' => 'Rak 71', 'area' => 'Kesenian'],
|
$kode_utama <= 739 => ['rak' => 'Rak 71', 'area' => 'Kesenian'],
|
||||||
$kode_utama <= 769 => ['rak' => 'Rak 72', 'area' => 'Seni Rupa'],
|
$kode_utama <= 769 => ['rak' => 'Rak 72', 'area' => 'Seni Rupa'],
|
||||||
$kode_utama <= 789 => ['rak' => 'Rak 73', 'area' => 'Fotografi/Musik'],
|
$kode_utama <= 789 => ['rak' => 'Rak 73', 'area' => 'Fotografi/Musik'],
|
||||||
default => ['rak' => 'Rak 74', 'area' => 'Olahraga'],
|
default => ['rak' => 'Rak 74', 'area' => 'Olahraga'],
|
||||||
},
|
},
|
||||||
$kode_utama >= 800 && $kode_utama <= 899 => ['rak' => 'Rak 77-79', 'area' => 'Sastra'],
|
$kode_utama >= 800 && $kode_utama <= 899 => ['rak' => 'Rak 77-79', 'area' => 'Sastra'],
|
||||||
$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'],
|
$kode_utama <= 919 => ['rak' => 'Rak 69, 70', 'area' => 'Geografi'],
|
||||||
default => ['rak' => 'Rak 80-84', 'area' => 'Sejarah Umum'],
|
default => ['rak' => 'Rak 80-84', 'area' => 'Sejarah Umum'],
|
||||||
},
|
},
|
||||||
default => ['rak' => 'Rak 75-76', 'area' => 'Koleksi Terbaru'],
|
default => ['rak' => 'Rak 75-76', 'area' => 'Koleksi Terbaru'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -242,12 +205,12 @@ public function update(Request $request, $id)
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'id_anggota' => 'required|exists:anggotas,id',
|
'id_anggota' => 'required|exists:anggotas,id',
|
||||||
'id_buku' => 'required|exists:buku,id_buku',
|
'id_buku' => 'required|exists:buku,id_buku',
|
||||||
'tanggal_pinjam' => 'required|date',
|
'tanggal_pinjam' => 'required|date_format:Y-m-d|after_or_equal:2000-01-01|before_or_equal:2100-12-31',
|
||||||
'tanggal_kembali' => 'required|date|after_or_equal:tanggal_pinjam',
|
'tanggal_kembali' => 'required|date_format:Y-m-d|after_or_equal:tanggal_pinjam|before_or_equal:2100-12-31',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$peminjaman = Peminjaman::findOrFail($id);
|
$peminjaman = Peminjaman::findOrFail($id);
|
||||||
|
|
||||||
// If the book changed, adjust stock
|
// If the book changed, adjust stock
|
||||||
if ($peminjaman->id_buku != $validated['id_buku']) {
|
if ($peminjaman->id_buku != $validated['id_buku']) {
|
||||||
if ($peminjaman->status_peminjaman == 'Dipinjam') {
|
if ($peminjaman->status_peminjaman == 'Dipinjam') {
|
||||||
|
|
@ -267,7 +230,7 @@ public function update(Request $request, $id)
|
||||||
public function destroy($id)
|
public function destroy($id)
|
||||||
{
|
{
|
||||||
$peminjaman = Peminjaman::findOrFail($id);
|
$peminjaman = Peminjaman::findOrFail($id);
|
||||||
|
|
||||||
if ($peminjaman->status_peminjaman == 'Dipinjam' && $peminjaman->buku) {
|
if ($peminjaman->status_peminjaman == 'Dipinjam' && $peminjaman->buku) {
|
||||||
$peminjaman->buku->increment('eksemplar');
|
$peminjaman->buku->increment('eksemplar');
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +243,7 @@ public function destroy($id)
|
||||||
public function kembalikan($id)
|
public function kembalikan($id)
|
||||||
{
|
{
|
||||||
$peminjaman = Peminjaman::findOrFail($id);
|
$peminjaman = Peminjaman::findOrFail($id);
|
||||||
|
|
||||||
$tglTenggat = \Carbon\Carbon::parse($peminjaman->tanggal_kembali)->startOfDay();
|
$tglTenggat = \Carbon\Carbon::parse($peminjaman->tanggal_kembali)->startOfDay();
|
||||||
$tglSekarang = \Carbon\Carbon::now()->startOfDay();
|
$tglSekarang = \Carbon\Carbon::now()->startOfDay();
|
||||||
|
|
||||||
|
|
@ -307,4 +270,74 @@ public function kembalikan($id)
|
||||||
|
|
||||||
return redirect()->back()->with('success', $pesan);
|
return redirect()->back()->with('success', $pesan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
|
||||||
|
if (!empty($targetNum)) {
|
||||||
|
$targetNum = preg_replace('/^0/', '62', trim($targetNum));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 .= "📄 *{$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";
|
||||||
|
$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_";
|
||||||
|
|
||||||
|
$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)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
\Illuminate\Support\Facades\Log::warning("Fonnte Log: Gagal Terkirim", [
|
||||||
|
'target' => $targetNum,
|
||||||
|
'http_status' => $response->status(),
|
||||||
|
'body' => $response->body()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\Illuminate\Support\Facades\Log::error("Error WA Pengiriman: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,12 @@ public function store(Request $request)
|
||||||
'nama' => 'required|string|max:255',
|
'nama' => 'required|string|max:255',
|
||||||
'jenis_anggota' => 'required|in:Mahasiswa,Siswa,Dosen,Umum',
|
'jenis_anggota' => 'required|in:Mahasiswa,Siswa,Dosen,Umum',
|
||||||
'no_identitas' => 'required|unique:anggotas,no_identitas',
|
'no_identitas' => 'required|unique:anggotas,no_identitas',
|
||||||
'no_ktp' => 'required|numeric|digits_between:10,16',
|
'no_ktp' => 'required|numeric|digits_between:1,16|unique:anggotas,no_ktp',
|
||||||
'prodi' => 'nullable|string|max:255',
|
'prodi' => 'required_unless:jenis_anggota,Umum|nullable|string|max:255',
|
||||||
'no_hp' => 'required|string|max:20',
|
'no_hp' => 'required|numeric|digits_between:10,13',
|
||||||
'alamat' => 'required|string',
|
'alamat' => 'required|string',
|
||||||
'nama_wali' => 'required|string|max:255',
|
'nama_wali' => 'required|string|max:255',
|
||||||
'no_hp_wali' => 'required|string|max:20',
|
'no_hp_wali' => 'required|numeric|digits_between:10,13',
|
||||||
'hubungan_wali' => 'required|in:Orang Tua,Saudara,Dosen Wali,Lainnya',
|
'hubungan_wali' => 'required|in:Orang Tua,Saudara,Dosen Wali,Lainnya',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -60,12 +60,12 @@ public function update(Request $request, Anggota $member)
|
||||||
'nama' => 'required|string|max:255',
|
'nama' => 'required|string|max:255',
|
||||||
'jenis_anggota' => 'required|in:Mahasiswa,Siswa,Dosen,Umum',
|
'jenis_anggota' => 'required|in:Mahasiswa,Siswa,Dosen,Umum',
|
||||||
'no_identitas' => 'required|unique:anggotas,no_identitas,' . $member->id,
|
'no_identitas' => 'required|unique:anggotas,no_identitas,' . $member->id,
|
||||||
'no_ktp' => 'required|numeric|digits_between:10,16',
|
'no_ktp' => 'required|numeric|digits_between:1,16|unique:anggotas,no_ktp,' . $member->id,
|
||||||
'prodi' => 'nullable|string|max:255',
|
'prodi' => 'required_unless:jenis_anggota,Umum|nullable|string|max:255',
|
||||||
'no_hp' => 'required|string|max:20',
|
'no_hp' => 'required|numeric|digits_between:10,13',
|
||||||
'alamat' => 'required|string',
|
'alamat' => 'required|string',
|
||||||
'nama_wali' => 'required|string|max:255',
|
'nama_wali' => 'required|string|max:255',
|
||||||
'no_hp_wali' => 'required|string|max:20',
|
'no_hp_wali' => 'required|numeric|digits_between:10,13',
|
||||||
'hubungan_wali' => 'required|in:Orang Tua,Saudara,Dosen Wali,Lainnya',
|
'hubungan_wali' => 'required|in:Orang Tua,Saudara,Dosen Wali,Lainnya',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ public function index()
|
||||||
*/
|
*/
|
||||||
public function showAdminLoginForm()
|
public function showAdminLoginForm()
|
||||||
{
|
{
|
||||||
return view('auth.login'); // login admin
|
return view('Auth.login'); // login admin
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -53,7 +53,7 @@ public function adminLogin(Request $request)
|
||||||
*/
|
*/
|
||||||
public function showUserLoginForm()
|
public function showUserLoginForm()
|
||||||
{
|
{
|
||||||
return view('auth.login-user'); // login pengunjung
|
return view('Auth.login-user'); // login pengunjung
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,10 @@ public function store(Request $request)
|
||||||
'keperluan' => 'required',
|
'keperluan' => 'required',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$anggota = Anggota::where('no_identitas', $request->no_anggota)->first();
|
$anggota = Anggota::where('no_ktp', $request->no_anggota)->orWhere('no_identitas', $request->no_anggota)->first();
|
||||||
|
|
||||||
if (!$anggota) {
|
if (!$anggota) {
|
||||||
return back()->withErrors(['no_anggota' => 'Nomor Anggota tidak ditemukan dalam sistem kami.'])->withInput();
|
return back()->withErrors(['no_anggota' => 'Nomor KTP / NIK tidak ditemukan dalam sistem kami.'])->withInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cari user terkait untuk id_user
|
// Cari user terkait untuk id_user
|
||||||
|
|
@ -47,10 +47,10 @@ public function store(Request $request)
|
||||||
// Jalur tamu: isi manual
|
// Jalur tamu: isi manual
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'nama_tamu' => 'required|string|max:255',
|
'nama_tamu' => 'required|string|max:255',
|
||||||
'email' => 'nullable|email|max:255',
|
'email' => 'required|email|max:255',
|
||||||
'no_hp' => 'nullable|string|max:20',
|
'no_hp' => 'required|digits_between:10,13',
|
||||||
'asal_instansi' => 'required|string|max:255',
|
'asal_instansi' => 'required|string|max:255',
|
||||||
'status' => 'nullable|string|max:255',
|
'status' => 'required|string|max:255',
|
||||||
'keperluan' => 'required',
|
'keperluan' => 'required',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,23 +8,85 @@
|
||||||
|
|
||||||
class LaporanController extends Controller
|
class LaporanController extends Controller
|
||||||
{
|
{
|
||||||
public function kehadiran()
|
public function kehadiran(Request $request)
|
||||||
{
|
{
|
||||||
$bukuTamu = BukuTamu::with('user')
|
$query = BukuTamu::with('user')->orderBy('tanggal_kunjungan', 'desc');
|
||||||
->orderBy('tanggal_kunjungan', 'desc')
|
|
||||||
->get();
|
// 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'));
|
return view('laporan.kehadiran', compact('bukuTamu'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function peminjaman(Request $request)
|
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();
|
$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'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
use Barryvdh\DomPDF\Facade\Pdf;
|
use Barryvdh\DomPDF\Facade\Pdf;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
|
|
||||||
class PeminjamanController extends Controller
|
class PeminjamanController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -32,13 +31,13 @@ public function index(\Illuminate\Http\Request $request)
|
||||||
|
|
||||||
$peminjaman = $query->paginate(15)->appends(['search' => $search]);
|
$peminjaman = $query->paginate(15)->appends(['search' => $search]);
|
||||||
$buku = Buku::where('eksemplar', '>', 0)->get();
|
$buku = Buku::where('eksemplar', '>', 0)->get();
|
||||||
$anggota = Anggota::all();
|
$anggota = Anggota::latest()->get();
|
||||||
return view('admin.peminjaman.index', compact('peminjaman', 'buku', 'anggota', 'search'));
|
return view('admin.peminjaman.index', compact('peminjaman', 'buku', 'anggota', 'search'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
$anggota = Anggota::all();
|
$anggota = Anggota::latest()->get();
|
||||||
$bukus = Buku::all();
|
$bukus = Buku::all();
|
||||||
return view('admin.peminjaman.create', compact('anggota', 'bukus'));
|
return view('admin.peminjaman.create', compact('anggota', 'bukus'));
|
||||||
}
|
}
|
||||||
|
|
@ -110,9 +109,9 @@ public function store(Request $request)
|
||||||
$response = Http::withoutVerifying()->timeout(15)->withHeaders([
|
$response = Http::withoutVerifying()->timeout(15)->withHeaders([
|
||||||
'Authorization' => $fonnteToken,
|
'Authorization' => $fonnteToken,
|
||||||
])->attach('file', file_get_contents($filePath), $fileName)->post('https://api.fonnte.com/send', [
|
])->attach('file', file_get_contents($filePath), $fileName)->post('https://api.fonnte.com/send', [
|
||||||
'target' => $targetNum,
|
'target' => $targetNum,
|
||||||
'message' => "Halo Kak *{$peminjaman->anggota->nama}*! 📚✨\n\nTerima kasih telah meminjam buku di *Perpustakaan Daerah Jember*.\n\nBersama pesan ini, kami lampirkan file e-Struk (PDF) untuk peminjaman buku:\n📖 Judul: _{$peminjaman->buku->judul}_\n📅 Batas Kembali: *{$peminjaman->tanggal_kembali}*\n\nMohon simpan dokumen PDF ini sebagai bukti transaksi yang sah. Harap kembalikan buku tepat waktu untuk menghindari denda keterlambatan.\n\nSelamat menikmati waktu membaca Anda!\n\nSalam Literasi,\n*Sistem Sarakata Jember*"
|
'message' => "Halo Kak *{$peminjaman->anggota->nama}*! 📚✨\n\nTerima kasih telah meminjam buku di *Perpustakaan Daerah Jember*.\n\nBersama pesan ini, kami lampirkan file e-Struk (PDF) untuk peminjaman buku:\n📖 Judul: _{$peminjaman->buku->judul}_\n📅 Batas Kembali: *{$peminjaman->tanggal_kembali}*\n\nMohon simpan dokumen PDF ini sebagai bukti transaksi yang sah. Harap kembalikan buku tepat waktu untuk menghindari denda keterlambatan.\n\nSelamat menikmati waktu membaca Anda!\n\nSalam Literasi,\n*Sistem Sarakata Jember*"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Cek respon resmi fonnte API
|
// Cek respon resmi fonnte API
|
||||||
if ($response->successful() && ($response->json('status') == true)) {
|
if ($response->successful() && ($response->json('status') == true)) {
|
||||||
|
|
@ -128,7 +127,6 @@ public function store(Request $request)
|
||||||
if (file_exists($filePath)) {
|
if (file_exists($filePath)) {
|
||||||
unlink($filePath);
|
unlink($filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// Bila error (misal: memori PDF kurang, library dompdf crash, timeout dari Fonnte)
|
// Bila error (misal: memori PDF kurang, library dompdf crash, timeout dari Fonnte)
|
||||||
// Error ditangkap agar sistem tidak langsung menampilkan layar error 500 ke User.
|
// Error ditangkap agar sistem tidak langsung menampilkan layar error 500 ke User.
|
||||||
|
|
@ -184,4 +182,4 @@ public function indexPengembalian()
|
||||||
|
|
||||||
return view('admin.peminjaman.pengembalian', compact('pengembalian'));
|
return view('admin.peminjaman.pengembalian', compact('pengembalian'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,21 +51,12 @@ public function index(Request $request)
|
||||||
$hasil = [];
|
$hasil = [];
|
||||||
|
|
||||||
foreach ($semuaBuku as $b) {
|
foreach ($semuaBuku as $b) {
|
||||||
// Hitung nilai Cosine Similarity untuk masing-masing atribut berdasarkan kata kunci yang sudah dibersihkan
|
// Hitung nilai kemiripan dengan algoritma Dynamic Token-wise WTS
|
||||||
$judulSimilarity = $this->calculateSimilarity($processedSearch, $b->judul);
|
$totalScore = $this->calculateDynamicWTS($processedSearch, $b);
|
||||||
$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);
|
|
||||||
|
|
||||||
// Penyesuaian Bobot WTS (Weighting)
|
|
||||||
// Judul (70%), Pengarang (20%), Penerbit/Deskripsi/Kategori (10%)
|
|
||||||
$totalScore = ($judulSimilarity * 0.7) + ($pengarangSimilarity * 0.2) + ($tambahanSimilarity * 0.1);
|
|
||||||
|
|
||||||
// 3. Penetapan Batas Relevansi (Threshold)
|
// 3. Penetapan Batas Relevansi (Threshold)
|
||||||
// Hanya tampilkan yang memiliki Total Similarity Score >= 0.3 (30%)
|
// Hanya tampilkan yang memiliki Total Similarity Score >= 0.2 (20%)
|
||||||
if ($totalScore >= 0.3) {
|
if ($totalScore >= 0.2) {
|
||||||
$b->similarity_score = $totalScore;
|
$b->similarity_score = $totalScore;
|
||||||
$hasil[] = $b;
|
$hasil[] = $b;
|
||||||
}
|
}
|
||||||
|
|
@ -90,44 +81,77 @@ public function index(Request $request)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper untuk menghitung nilai perbandingan kemiripan teks.
|
* [ALGORITMA DYNAMIC TOKEN-WISE WTS]
|
||||||
* Menggunakan pendekatan Cosine Similarity yang dimodifikasi (Query Coverage)
|
* Dibuat khusus untuk memecahkan masalah "Length Penalty" pada Global Search Bar.
|
||||||
* agar panjang judul buku tidak menurunkan skor secara drastis
|
*
|
||||||
* pada pencarian kata kunci pendek.
|
* KONTEKS MASALAH (Untuk Sidang TA):
|
||||||
|
* Algoritma Cosine Similarity standar membandingkan string secara utuh.
|
||||||
|
* Saat user menggabungkan "Judul + Pengarang" (contoh: "Aku Anak Mandiri Watiek"),
|
||||||
|
* panjang vektor query membesar. Saat dicocokkan HANYA ke kolom Judul ("Aku Anak Mandiri"),
|
||||||
|
* kata "Watiek" dianggap sebagai noise/sampah, sehingga skor kemiripan Judul anjlok.
|
||||||
|
* Akibatnya total WTS turun dan buku yang relevan malah tenggelam.
|
||||||
|
*
|
||||||
|
* SOLUSI (Tokenisasi Dinamis Lintas Atribut):
|
||||||
|
* 1. Query dipecah menjadi token (kata per kata).
|
||||||
|
* 2. Setiap token dievaluasi secara independen ke semua atribut buku (Judul, Pengarang, Kategori).
|
||||||
|
* 3. Token berhak memilih skor kemiripan TERTINGGI dari atribut manapun (Local Max Pooling).
|
||||||
|
* -> Contoh: Kata "Watiek" tidak akan menghukum skor Judul, melainkan diidentifikasi
|
||||||
|
* sebagai milik "Pengarang" dan menyumbang skor 0.3.
|
||||||
|
* 4. Agregasi: Skor semua token dijumlahkan, lalu dibagi jumlah token query.
|
||||||
|
*
|
||||||
|
* Hasilnya: "Aku Anak Mandiri Watiek" akan secara dinamis menyatukan bobot Judul (0.6)
|
||||||
|
* dan Pengarang (0.3), BUKAN saling memberikan penalti!
|
||||||
|
*
|
||||||
|
* @param string $query String pencarian dari user
|
||||||
|
* @param \App\Models\Buku $buku Objek buku dari database
|
||||||
|
* @return float Skor relevansi akhir
|
||||||
*/
|
*/
|
||||||
private function calculateSimilarity($query, $text)
|
private function calculateDynamicWTS($query, $buku)
|
||||||
{
|
{
|
||||||
if (empty(trim($text))) return 0;
|
// 1. Ekstraksi dan Pembersihan Token Query
|
||||||
|
|
||||||
$queryWords = explode(' ', strtolower(preg_replace('/[^\p{L}\p{N}\s]/u', '', $query)));
|
$queryWords = explode(' ', strtolower(preg_replace('/[^\p{L}\p{N}\s]/u', '', $query)));
|
||||||
$textWords = explode(' ', strtolower(preg_replace('/[^\p{L}\p{N}\s]/u', '', $text)));
|
$queryWords = array_filter($queryWords);
|
||||||
|
|
||||||
|
if (empty($queryWords)) return 0;
|
||||||
|
|
||||||
// Frekuensi kata
|
// 2. Ekstraksi Token Atribut Buku
|
||||||
$vecA = array_count_values(array_filter($queryWords));
|
$judulWords = explode(' ', strtolower(preg_replace('/[^\p{L}\p{N}\s]/u', '', $buku->judul)));
|
||||||
$vecB = array_count_values(array_filter($textWords));
|
$pengarangWords = explode(' ', strtolower(preg_replace('/[^\p{L}\p{N}\s]/u', '', $buku->pengarang)));
|
||||||
|
|
||||||
|
$teksTambahan = trim(($buku->penerbit ?? '') . ' ' . ($buku->deskripsi ?? '') . ' ' . ($buku->kategori->nama_kategori ?? ''));
|
||||||
|
$tambahanWords = explode(' ', strtolower(preg_replace('/[^\p{L}\p{N}\s]/u', '', $teksTambahan)));
|
||||||
|
|
||||||
$terms = array_unique(array_merge(array_keys($vecA), array_keys($vecB)));
|
// Konversi ke array asosiatif (Hash Map) untuk efisiensi pencarian O(1)
|
||||||
|
$judulMap = array_flip(array_filter($judulWords));
|
||||||
|
$pengarangMap = array_flip(array_filter($pengarangWords));
|
||||||
|
$tambahanMap = array_flip(array_filter($tambahanWords));
|
||||||
|
|
||||||
$dotProduct = 0;
|
// 3. Konfigurasi Bobot WTS
|
||||||
$normA = 0;
|
$weights = [
|
||||||
$normB = 0;
|
'judul' => 0.6,
|
||||||
|
'pengarang' => 0.3,
|
||||||
|
'tambahan' => 0.1
|
||||||
|
];
|
||||||
|
|
||||||
foreach ($terms as $term) {
|
$totalScore = 0;
|
||||||
$valA = $vecA[$term] ?? 0;
|
|
||||||
$valB = $vecB[$term] ?? 0;
|
|
||||||
|
|
||||||
$dotProduct += ($valA * $valB);
|
// 4. Evaluasi Token-wise (Local Max Pooling)
|
||||||
$normA += pow($valA, 2);
|
foreach ($queryWords as $qWord) {
|
||||||
$normB += pow($valB, 2);
|
// Berikan bobot penuh jika kata ditemukan di atribut bersangkutan
|
||||||
|
$scoreJudul = isset($judulMap[$qWord]) ? $weights['judul'] : 0;
|
||||||
|
$scorePengarang = isset($pengarangMap[$qWord]) ? $weights['pengarang'] : 0;
|
||||||
|
$scoreTambahan = isset($tambahanMap[$qWord]) ? $weights['tambahan'] : 0;
|
||||||
|
|
||||||
|
// KUNCI SOLUSI: Kata ini akan menyumbang skor dari atribut yang paling relevan untuknya
|
||||||
|
$bestScoreForToken = max($scoreJudul, $scorePengarang, $scoreTambahan);
|
||||||
|
|
||||||
|
$totalScore += $bestScoreForToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($normA == 0 || $normB == 0) return 0;
|
// 5. Normalisasi
|
||||||
|
// Membagi total skor dengan jumlah token query agar term-frequency ternormalisasi,
|
||||||
// Standard Cosine Similarity: $dotProduct / (sqrt($normA) * sqrt($normB))
|
// namun tetap secara matematis mempertahankan keunggulan bobot absolut antar atribut.
|
||||||
// Kelemahannya: Jika judul buku sangat panjang (normB besar), skor akan anjlok (misal < 0.2)
|
return $totalScore / count($queryWords);
|
||||||
// padahal kata pencariannya cocok 100%.
|
|
||||||
// Modifikasi menjadi Query Coverage (Pembagi dominan adalah panjang query).
|
|
||||||
return $dotProduct / $normA;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show($id)
|
public function show($id)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ class BukuTamu extends Model
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $table = 'buku_tamu';
|
protected $table = 'buku_tamu';
|
||||||
|
protected $primaryKey = 'id_tamu';
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,24 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Pagination\Paginator;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Register any application services.
|
|
||||||
*/
|
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Bootstrap any application services.
|
|
||||||
*/
|
|
||||||
public function boot(): void
|
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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Script untuk membuat Symlink (Storage Link) di Shared Hosting
|
||||||
|
* Jalankan file ini sekali saja melalui browser: https://ta.myhost.id/E31230887/buat_link.php
|
||||||
|
*/
|
||||||
|
|
||||||
|
$target = __DIR__ . '/storage/app/public';
|
||||||
|
$link = __DIR__ . '/public/storage';
|
||||||
|
|
||||||
|
echo "<h2>🔧 Memperbaiki Jembatan Storage (Symlink)</h2>";
|
||||||
|
echo "<p>Target: <code>$target</code></p>";
|
||||||
|
echo "<p>Link: <code>$link</code></p>";
|
||||||
|
|
||||||
|
if (file_exists($link)) {
|
||||||
|
echo "<p>⚠️ Menghapus link/folder storage lama...</p>";
|
||||||
|
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 "<h3 style='color: green;'>✅ Jembatan Storage Berhasil Dibuat!</h3>";
|
||||||
|
echo "<p>Sekarang gambar buku Anda seharusnya sudah muncul di website.</p>";
|
||||||
|
echo "<a href='./'>Kembali ke Website</a>";
|
||||||
|
} else {
|
||||||
|
echo "<h3 style='color: red;'>❌ Gagal membuat jembatan symlink.</h3>";
|
||||||
|
echo "<p>Beberapa hosting membatasi fungsi symlink(). Jika ini terjadi, beri tahu asisten AI Anda untuk menggunakan metode .htaccess redirect.</p>";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
$target = __DIR__ . '/public/storage';
|
||||||
|
echo "Path: $target\n";
|
||||||
|
echo "Is Link: " . (is_link($target) ? 'YES' : 'NO') . "\n";
|
||||||
|
echo "Is Dir: " . (is_dir($target) ? 'YES' : 'NO') . "\n";
|
||||||
|
if (is_link($target)) {
|
||||||
|
echo "Readlink: " . readlink($target) . "\n";
|
||||||
|
}
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
|
|
||||||
return new class extends Migration
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the migrations.
|
|
||||||
*/
|
|
||||||
public function up()
|
|
||||||
{
|
|
||||||
Schema::create('bukus', function (Blueprint $table) {
|
|
||||||
$table->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');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
DB::statement('ALTER TABLE peminjaman DROP FOREIGN KEY peminjaman_ibfk_2');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::statement('ALTER TABLE peminjaman DROP FOREIGN KEY fk_peminjaman_buku');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Schema::table('peminjaman', function (Blueprint $table) {
|
||||||
|
$table->dropForeign(['id_buku']);
|
||||||
|
});
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::statement('ALTER TABLE peminjaman MODIFY id_buku INT UNSIGNED NOT NULL');
|
||||||
|
|
||||||
|
Schema::table('peminjaman', function (Blueprint $table) {
|
||||||
|
$table->foreign('id_buku')
|
||||||
|
->references('id_buku')
|
||||||
|
->on('buku')
|
||||||
|
->cascadeOnDelete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Schema::table('peminjaman', function (Blueprint $table) {
|
||||||
|
$table->dropForeign(['id_buku']);
|
||||||
|
});
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::statement('ALTER TABLE peminjaman MODIFY id_buku BIGINT UNSIGNED NOT NULL');
|
||||||
|
|
||||||
|
Schema::table('peminjaman', function (Blueprint $table) {
|
||||||
|
$table->foreign('id_buku')
|
||||||
|
->references('id')
|
||||||
|
->on('bukus')
|
||||||
|
->cascadeOnDelete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
\Illuminate\Support\Facades\DB::statement('ALTER TABLE buku_tamu MODIFY id_tamu INT UNSIGNED NOT NULL AUTO_INCREMENT');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
\Illuminate\Support\Facades\DB::statement('ALTER TABLE buku_tamu MODIFY id_tamu INT UNSIGNED NOT NULL');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
// File debug sementara — HAPUS setelah selesai!
|
||||||
|
echo "<pre>";
|
||||||
|
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 "</pre>";
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
require __DIR__.'/vendor/autoload.php';
|
||||||
|
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||||
|
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||||
|
$kernel->bootstrap();
|
||||||
|
|
||||||
|
$buku = App\Models\Buku::find(52);
|
||||||
|
echo json_encode($buku->toArray());
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
require __DIR__.'/vendor/autoload.php';
|
||||||
|
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||||
|
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||||
|
$kernel->bootstrap();
|
||||||
|
|
||||||
|
$res = [];
|
||||||
|
foreach(['0','1','2','3','4','5','6','7','8','9'] as $d) {
|
||||||
|
$b = App\Models\Buku::where('nomor_panggil', 'like', $d.'%')->whereNotNull('lokasi_x')->first();
|
||||||
|
$res[$d.'00'] = $b ? ['x' => $b->lokasi_x, 'y' => $b->lokasi_y] : null;
|
||||||
|
}
|
||||||
|
echo json_encode($res);
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Bootstrap Laravel dari folder public/
|
||||||
|
require __DIR__ . '/public/index.php';
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 240 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.9 MiB After Width: | Height: | Size: 6.8 MiB |
|
|
@ -3,7 +3,7 @@
|
||||||
@section('title', 'Manajemen Admin')
|
@section('title', 'Manajemen Admin')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div x-data="{ isModalOpen: false }" x-cloak>
|
<div x-data="{ isModalOpen: {{ $errors->any() ? 'true' : 'false' }} }" x-cloak>
|
||||||
<x-page-header title="Manajemen Akun Admin">
|
<x-page-header title="Manajemen Akun Admin">
|
||||||
<x-slot name="actions">
|
<x-slot name="actions">
|
||||||
<button @click="isModalOpen = true" class="bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white px-5 py-2.5 rounded-lg border border-transparent shadow-[0_4px_10px_rgba(37,99,235,0.2)] hover:shadow-[0_6px_15px_rgba(37,99,235,0.3)] transition-all font-semibold flex items-center gap-2 transform hover:translate-y-[-1px]">
|
<button @click="isModalOpen = true" class="bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white px-5 py-2.5 rounded-lg border border-transparent shadow-[0_4px_10px_rgba(37,99,235,0.2)] hover:shadow-[0_6px_15px_rgba(37,99,235,0.3)] transition-all font-semibold flex items-center gap-2 transform hover:translate-y-[-1px]">
|
||||||
|
|
@ -15,16 +15,6 @@
|
||||||
<x-alert type="success" :message="session('success')" />
|
<x-alert type="success" :message="session('success')" />
|
||||||
<x-alert type="error" :message="session('error')" />
|
<x-alert type="error" :message="session('error')" />
|
||||||
|
|
||||||
@if ($errors->any())
|
|
||||||
<div class="bg-red-50 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded-r-xl text-sm shadow-sm">
|
|
||||||
<p class="font-bold mb-1"><i class="fas fa-exclamation-circle mr-1"></i> Validasi Gagal</p>
|
|
||||||
<ul class="list-disc ml-5 space-y-1">
|
|
||||||
@foreach ($errors->all() as $error)
|
|
||||||
<li>{{ $error }}</li>
|
|
||||||
@endforeach
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<x-card>
|
<x-card>
|
||||||
<x-table>
|
<x-table>
|
||||||
|
|
@ -95,27 +85,38 @@ class="relative z-[60] inline-block px-4 pt-5 pb-4 overflow-hidden text-left ali
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="bg-red-50 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded-r-xl text-sm shadow-sm">
|
||||||
|
<p class="font-bold mb-1"><i class="fas fa-exclamation-circle mr-1"></i> Validasi Gagal</p>
|
||||||
|
<ul class="list-disc ml-5 space-y-1">
|
||||||
|
@foreach ($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<form action="{{ route('admin.akun.store') }}" method="POST" class="space-y-5">
|
<form action="{{ route('admin.akun.store') }}" method="POST" class="space-y-5">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Nama Lengkap</label>
|
<x-input-label for="name" value="Nama Lengkap" class="mb-1" />
|
||||||
<input type="text" name="name" value="{{ old('name') }}" placeholder="Masukkan nama..." class="w-full rounded-xl border-gray-300 focus:border-blue-500 focus:ring-blue-500 shadow-sm bg-gray-50/50" required>
|
<x-text-input id="name" type="text" name="name" :value="old('name')" placeholder="Masukkan nama..." required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Email</label>
|
<x-input-label for="email" value="Email" class="mb-1" />
|
||||||
<input type="email" name="email" value="{{ old('email') }}" placeholder="admin@domain.com" class="w-full rounded-xl border-gray-300 focus:border-blue-500 focus:ring-blue-500 shadow-sm bg-gray-50/50" required>
|
<x-text-input id="email" type="email" name="email" :value="old('email')" placeholder="admin@domain.com" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Password</label>
|
<x-input-label for="password" value="Password" class="mb-1" />
|
||||||
<input type="password" name="password" placeholder="Minimal 8 karakter" class="w-full rounded-xl border-gray-300 focus:border-blue-500 focus:ring-blue-500 shadow-sm bg-gray-50/50" required>
|
<x-text-input id="password" type="password" name="password" placeholder="Minimal 8 karakter" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Konfirmasi Password</label>
|
<x-input-label for="password_confirmation" value="Konfirmasi Password" class="mb-1" />
|
||||||
<input type="password" name="password_confirmation" placeholder="Ulangi password" class="w-full rounded-xl border-gray-300 focus:border-blue-500 focus:ring-blue-500 shadow-sm bg-gray-50/50" required>
|
<x-text-input id="password_confirmation" type="password" name="password_confirmation" placeholder="Ulangi password" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-end gap-3 pt-4 border-t border-gray-100 mt-6">
|
<div class="flex items-center justify-end gap-3 pt-4 border-t border-gray-100 mt-6">
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="id_kategori" value="Kategori" />
|
<x-input-label for="id_kategori" value="Kategori" />
|
||||||
<select id="id_kategori" name="id_kategori" class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm">
|
<select id="id_kategori" name="id_kategori" class="border border-gray-200 bg-gray-50 text-gray-900 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block w-full p-3 shadow-sm hover:bg-white transition-all duration-200 outline-none">
|
||||||
<option value="">Pilih Kategori</option>
|
<option value="">Pilih Kategori</option>
|
||||||
@foreach($kategori as $kat)
|
@foreach($kategori as $kat)
|
||||||
<option value="{{ $kat->id_kategori }}" {{ old('id_kategori') == $kat->id_kategori ? 'selected' : '' }}>
|
<option value="{{ $kat->id_kategori }}" {{ old('id_kategori') == $kat->id_kategori ? 'selected' : '' }}>
|
||||||
|
|
@ -65,26 +65,26 @@
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="penerbit" value="Penerbit" />
|
<x-input-label for="penerbit" value="Penerbit" />
|
||||||
<x-text-input id="penerbit" name="penerbit" type="text" class="mt-1 block w-full" :value="old('penerbit')" />
|
<x-text-input id="penerbit" name="penerbit" type="text" class="mt-1 block w-full" :value="old('penerbit')" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="tahun_terbit" value="Tahun Terbit" />
|
<x-input-label for="tahun_terbit" value="Tahun Terbit" />
|
||||||
<x-text-input id="tahun_terbit" name="tahun_terbit" type="number" class="mt-1 block w-full" :value="old('tahun_terbit')" />
|
<x-text-input id="tahun_terbit" name="tahun_terbit" type="number" class="mt-1 block w-full" :value="old('tahun_terbit')" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="edisi" value="Edisi" />
|
<x-input-label for="edisi" value="Edisi" />
|
||||||
<x-text-input id="edisi" name="edisi" type="text" class="mt-1 block w-full" :value="old('edisi')" />
|
<x-text-input id="edisi" name="edisi" type="number" class="mt-1 block w-full" :value="old('edisi')" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="deskripsi_fisik" value="Deskripsi Fisik" />
|
<x-input-label for="deskripsi_fisik" value="Deskripsi Fisik" />
|
||||||
<x-text-input id="deskripsi_fisik" name="deskripsi_fisik" type="text" class="mt-1 block w-full" :value="old('deskripsi_fisik')" />
|
<x-text-input id="deskripsi_fisik" name="deskripsi_fisik" type="text" class="mt-1 block w-full" :value="old('deskripsi_fisik')" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="eksemplar" value="Jumlah Eksemplar" />
|
<x-input-label for="eksemplar" value="Jumlah Eksemplar" />
|
||||||
<x-text-input id="eksemplar" name="eksemplar" type="number" class="mt-1 block w-full" :value="old('eksemplar')" />
|
<x-text-input id="eksemplar" name="eksemplar" type="number" class="mt-1 block w-full" :value="old('eksemplar')" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class="p-6 space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="id_kategori" value="Kategori" />
|
<x-input-label for="id_kategori" value="Kategori" />
|
||||||
<select id="id_kategori" name="id_kategori"
|
<select id="id_kategori" name="id_kategori"
|
||||||
class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm">
|
class="border border-gray-200 bg-gray-50 text-gray-900 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block w-full p-3 shadow-sm hover:bg-white transition-all duration-200 outline-none">
|
||||||
<option value="">Pilih Kategori</option>
|
<option value="">Pilih Kategori</option>
|
||||||
@foreach ($kategori as $kat)
|
@foreach ($kategori as $kat)
|
||||||
<option value="{{ $kat->id_kategori }}"
|
<option value="{{ $kat->id_kategori }}"
|
||||||
|
|
@ -66,17 +66,17 @@ class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indi
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="penerbit" value="Penerbit" />
|
<x-input-label for="penerbit" value="Penerbit" />
|
||||||
<x-text-input id="penerbit" name="penerbit" type="text" class="mt-1 block w-full"
|
<x-text-input id="penerbit" name="penerbit" type="text" class="mt-1 block w-full"
|
||||||
:value="old('penerbit', $buku->penerbit)" />
|
:value="old('penerbit', $buku->penerbit)" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="tahun_terbit" value="Tahun Terbit" />
|
<x-input-label for="tahun_terbit" value="Tahun Terbit" />
|
||||||
<x-text-input id="tahun_terbit" name="tahun_terbit" type="number" class="mt-1 block w-full"
|
<x-text-input id="tahun_terbit" name="tahun_terbit" type="number" class="mt-1 block w-full"
|
||||||
:value="old('tahun_terbit', $buku->tahun_terbit)" />
|
:value="old('tahun_terbit', $buku->tahun_terbit)" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="edisi" value="Edisi" />
|
<x-input-label for="edisi" value="Edisi" />
|
||||||
<x-text-input id="edisi" name="edisi" type="text" class="mt-1 block w-full"
|
<x-text-input id="edisi" name="edisi" type="number" class="mt-1 block w-full"
|
||||||
:value="old('edisi', $buku->edisi)" />
|
:value="old('edisi', $buku->edisi)" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -84,12 +84,12 @@ class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indi
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="deskripsi_fisik" value="Deskripsi Fisik" />
|
<x-input-label for="deskripsi_fisik" value="Deskripsi Fisik" />
|
||||||
<x-text-input id="deskripsi_fisik" name="deskripsi_fisik" type="text" class="mt-1 block w-full"
|
<x-text-input id="deskripsi_fisik" name="deskripsi_fisik" type="text" class="mt-1 block w-full"
|
||||||
:value="old('deskripsi_fisik', $buku->deskripsi_fisik)" />
|
:value="old('deskripsi_fisik', $buku->deskripsi_fisik)" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="eksemplar" value="Jumlah Eksemplar" />
|
<x-input-label for="eksemplar" value="Jumlah Eksemplar" />
|
||||||
<x-text-input id="eksemplar" name="eksemplar" type="number" class="mt-1 block w-full"
|
<x-text-input id="eksemplar" name="eksemplar" type="number" class="mt-1 block w-full"
|
||||||
:value="old('eksemplar', $buku->eksemplar)" />
|
:value="old('eksemplar', $buku->eksemplar)" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ class="mt-1 block w-full bg-gray-50/50" :value="old('bibid')" required />
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="id_kategori" value="Kategori" />
|
<x-input-label for="id_kategori" value="Kategori" />
|
||||||
<select id="id_kategori" name="id_kategori"
|
<select id="id_kategori" name="id_kategori"
|
||||||
class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50"
|
class="border border-gray-200 bg-gray-50 text-gray-900 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block w-full p-3 shadow-sm hover:bg-white transition-all duration-200 outline-none"
|
||||||
required>
|
required>
|
||||||
<option value="">Pilih Kategori</option>
|
<option value="">Pilih Kategori</option>
|
||||||
@foreach ($kategori as $kat)
|
@foreach ($kategori as $kat)
|
||||||
|
|
@ -169,17 +169,17 @@ class="mt-1 block w-full bg-gray-50/50" :value="old('nomor_panggil')" required /
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="penerbit" value="Penerbit" />
|
<x-input-label for="penerbit" value="Penerbit" />
|
||||||
<x-text-input id="penerbit" name="penerbit" type="text"
|
<x-text-input id="penerbit" name="penerbit" type="text"
|
||||||
class="mt-1 block w-full bg-gray-50/50" :value="old('penerbit')" />
|
class="mt-1 block w-full bg-gray-50/50" :value="old('penerbit')" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="tahun_terbit" value="Tahun Terbit" />
|
<x-input-label for="tahun_terbit" value="Tahun Terbit" />
|
||||||
<x-text-input id="tahun_terbit" name="tahun_terbit" type="number"
|
<x-text-input id="tahun_terbit" name="tahun_terbit" type="number"
|
||||||
class="mt-1 block w-full bg-gray-50/50" :value="old('tahun_terbit')" />
|
class="mt-1 block w-full bg-gray-50/50" :value="old('tahun_terbit')" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="edisi" value="Edisi" />
|
<x-input-label for="edisi" value="Edisi" />
|
||||||
<x-text-input id="edisi" name="edisi" type="text"
|
<x-text-input id="edisi" name="edisi" type="number"
|
||||||
class="mt-1 block w-full bg-gray-50/50" :value="old('edisi')" />
|
class="mt-1 block w-full bg-gray-50/50" :value="old('edisi')" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -187,7 +187,7 @@ class="mt-1 block w-full bg-gray-50/50" :value="old('edisi')" />
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="deskripsi_fisik" value="Deskripsi Fisik" />
|
<x-input-label for="deskripsi_fisik" value="Deskripsi Fisik" />
|
||||||
<x-text-input id="deskripsi_fisik" name="deskripsi_fisik" type="text"
|
<x-text-input id="deskripsi_fisik" name="deskripsi_fisik" type="text"
|
||||||
class="mt-1 block w-full bg-gray-50/50" :value="old('deskripsi_fisik')" />
|
class="mt-1 block w-full bg-gray-50/50" :value="old('deskripsi_fisik')" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="eksemplar" value="Jumlah Eksemplar" />
|
<x-input-label for="eksemplar" value="Jumlah Eksemplar" />
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,31 @@
|
||||||
@extends('layouts.app')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
<!-- CDNs for icons, TomSelect and sweetalert -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/css/tom-select.css" rel="stylesheet">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/js/tom-select.complete.min.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* TomSelect Custom Styling matching dashboard */
|
||||||
|
.ts-control {
|
||||||
|
border: 1px solid #d1d5db !important;
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
border-radius: 0.5rem !important;
|
||||||
|
padding: 0.75rem 1rem !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) !important;
|
||||||
|
}
|
||||||
|
.ts-control.focus {
|
||||||
|
border-color: #3b82f6 !important;
|
||||||
|
box-shadow: 0 0 0 1px #3b82f6 !important;
|
||||||
|
}
|
||||||
|
.ts-control input {
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<div class="container mx-auto px-4 py-8 max-w-2xl">
|
<div class="container mx-auto px-4 py-8 max-w-2xl">
|
||||||
<div class="bg-white shadow-lg rounded-xl overflow-hidden border border-gray-100">
|
<div class="bg-white shadow-lg rounded-xl overflow-hidden border border-gray-100">
|
||||||
<div class="p-6 bg-blue-50 border-b border-blue-100">
|
<div class="p-6 bg-blue-50 border-b border-blue-100">
|
||||||
|
|
@ -34,30 +59,43 @@ class="w-full border-gray-300 rounded-lg shadow-sm focus:border-blue-500 focus:r
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2">Pilih Aset Buku (Hanya yang
|
<div>
|
||||||
tersedia)</label>
|
<label class="block text-sm font-medium text-gray-700 mb-2">Pilih Buku Utama (Pindai Barcode / Cari Judul)</label>
|
||||||
<select name="id_buku"
|
<select name="id_buku" id="select-buku-peminjaman" required>
|
||||||
class="w-full border-gray-300 rounded-lg shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
<option value="">-- Pilih Buku Pertama --</option>
|
||||||
<option value="">-- Pilih Buku --</option>
|
@foreach ($buku as $b)
|
||||||
@foreach ($buku as $b)
|
<option value="{{ $b->id_buku }}" data-bibid="{{ $b->bibid }}" {{ old('id_buku') == $b->id_buku ? 'selected' : '' }}>
|
||||||
<option value="{{ $b->id }}" {{ old('id_buku') == $b->id ? 'selected' : '' }}>
|
{{ $b->bibid }} - {{ $b->judul }} (Stok: {{ $b->eksemplar }})
|
||||||
{{ $b->bibid }} - {{ $b->judul }} (Stok: {{ $b->eksemplar }})
|
</option>
|
||||||
</option>
|
@endforeach
|
||||||
@endforeach
|
</select>
|
||||||
</select>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-2">Pilih Buku Kedua (Opsional)</label>
|
||||||
|
<select name="id_buku_2" id="select-buku-peminjaman-2">
|
||||||
|
<option value="">-- Pilih Buku Kedua (Kosongkan jika hanya 1) --</option>
|
||||||
|
@foreach ($buku as $b)
|
||||||
|
<option value="{{ $b->id_buku }}" data-bibid="{{ $b->bibid }}" {{ old('id_buku_2') == $b->id_buku ? 'selected' : '' }}>
|
||||||
|
{{ $b->bibid }} - {{ $b->judul }} (Stok: {{ $b->eksemplar }})
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2">Tanggal Pinjam</label>
|
<label class="block text-sm font-medium text-gray-700 mb-2">Tanggal Pinjam</label>
|
||||||
<input type="date" name="tanggal_pinjam" value="{{ old('tanggal_pinjam', date('Y-m-d')) }}"
|
<input type="date" name="tanggal_pinjam" value="{{ old('tanggal_pinjam', date('Y-m-d')) }}"
|
||||||
|
min="2000-01-01" max="2100-12-31"
|
||||||
class="w-full border-gray-300 rounded-lg shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
class="w-full border-gray-300 rounded-lg shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-2">Tanggal Kembali (Tenggat)</label>
|
<label class="block text-sm font-medium text-gray-700 mb-2">Tanggal Kembali (Tenggat)</label>
|
||||||
<input type="date" name="tanggal_kembali"
|
<input type="date" name="tanggal_kembali"
|
||||||
value="{{ old('tanggal_kembali', date('Y-m-d', strtotime('+7 days'))) }}"
|
value="{{ old('tanggal_kembali', date('Y-m-d', strtotime('+7 days'))) }}"
|
||||||
|
min="2000-01-01" max="2100-12-31"
|
||||||
class="w-full border-gray-300 rounded-lg shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
class="w-full border-gray-300 rounded-lg shadow-sm focus:border-blue-500 focus:ring-blue-500">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -81,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.innerHTML = 'Memproses...';
|
||||||
btn.classList.add('opacity-50', 'cursor-not-allowed');
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,16 @@
|
||||||
tanggal_pinjam: '{{ old('tanggal_pinjam') ?? '' }}',
|
tanggal_pinjam: '{{ old('tanggal_pinjam') ?? '' }}',
|
||||||
tanggal_kembali: '{{ old('tanggal_kembali') ?? '' }}'
|
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) {
|
openEditModal(id, anggotaId, buku, pinjam, kembali) {
|
||||||
this.editData = { id: id, id_anggota: anggotaId, id_buku: buku, tanggal_pinjam: pinjam, tanggal_kembali: kembali };
|
this.editData = { id: id, id_anggota: anggotaId, id_buku: buku, tanggal_pinjam: pinjam, tanggal_kembali: kembali };
|
||||||
this.isModalEditOpen = true;
|
this.isModalEditOpen = true;
|
||||||
|
|
@ -153,6 +163,14 @@ class="text-green-600 font-bold px-3 py-1.5 text-xs bg-green-50 rounded border b
|
||||||
<a href="{{ route('admin.peminjaman.struk', $item->id_peminjaman) }}" target="_blank"
|
<a href="{{ route('admin.peminjaman.struk', $item->id_peminjaman) }}" target="_blank"
|
||||||
class="bg-gray-800 text-white hover:bg-gray-900 px-3 py-1.5 rounded text-xs font-bold transition flex items-center">Cetak
|
class="bg-gray-800 text-white hover:bg-gray-900 px-3 py-1.5 rounded text-xs font-bold transition flex items-center">Cetak
|
||||||
Struk</a>
|
Struk</a>
|
||||||
|
<form action="{{ route('admin.peminjaman.resend_wa', $item->id_peminjaman) }}" method="POST" class="m-0 p-0 flex items-center">
|
||||||
|
@csrf
|
||||||
|
<button type="submit"
|
||||||
|
class="bg-green-500 text-white hover:bg-green-600 px-2 py-1.5 rounded text-xs font-bold transition flex items-center"
|
||||||
|
title="Kirim Struk ke WhatsApp">
|
||||||
|
<i class="fab fa-whatsapp"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
@if ($item->status_peminjaman == 'Dipinjam')
|
@if ($item->status_peminjaman == 'Dipinjam')
|
||||||
<button type="button"
|
<button type="button"
|
||||||
@click="openEditModal('{{ $item->id_peminjaman }}', '{{ $item->id_anggota ?? $item->id_user }}', '{{ $item->id_buku }}', '{{ \Carbon\Carbon::parse($item->tanggal_pinjam)->format('Y-m-d') }}', '{{ \Carbon\Carbon::parse($item->tanggal_kembali)->format('Y-m-d') }}')"
|
@click="openEditModal('{{ $item->id_peminjaman }}', '{{ $item->id_anggota ?? $item->id_user }}', '{{ $item->id_buku }}', '{{ \Carbon\Carbon::parse($item->tanggal_pinjam)->format('Y-m-d') }}', '{{ \Carbon\Carbon::parse($item->tanggal_kembali)->format('Y-m-d') }}')"
|
||||||
|
|
@ -232,7 +250,7 @@ class="w-10 h-10 rounded-full bg-gray-50 hover:bg-red-50 text-gray-400 hover:tex
|
||||||
<div>
|
<div>
|
||||||
<x-input-label value="Pilih Anggota / Member" />
|
<x-input-label value="Pilih Anggota / Member" />
|
||||||
<select name="id_anggota" x-init="new TomSelect($el, { maxOptions: null })"
|
<select name="id_anggota" x-init="new TomSelect($el, { maxOptions: null })"
|
||||||
class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50"
|
class="mt-1 block w-full border-gray-200 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50 p-3"
|
||||||
required>
|
required>
|
||||||
<option value="">-- Pilih Member --</option>
|
<option value="">-- Pilih Member --</option>
|
||||||
@foreach ($anggota as $a)
|
@foreach ($anggota as $a)
|
||||||
|
|
@ -244,18 +262,34 @@ class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indi
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<x-input-label value="Pilih Buku (Hanya yang tersedia)" />
|
<div>
|
||||||
<select name="id_buku" x-init="new TomSelect($el, { maxOptions: null })"
|
<x-input-label value="Pilih Buku Utama (Pindai Barcode / Cari Judul)" />
|
||||||
class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50"
|
<select name="id_buku" id="select-buku-peminjaman" x-init="new TomSelect($el, { maxOptions: null })"
|
||||||
required>
|
class="mt-1 block w-full border-gray-200 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50 p-3"
|
||||||
<option value="">-- Pilih Buku --</option>
|
required>
|
||||||
@foreach ($buku as $b)
|
<option value="">-- Pilih Buku Pertama --</option>
|
||||||
<option value="{{ $b->id }}" {{ old('id_buku') == $b->id ? 'selected' : '' }}>
|
@foreach ($buku as $b)
|
||||||
{{ $b->bibid }} - {{ $b->judul }} (Stok: {{ $b->eksemplar }})
|
<option value="{{ $b->id_buku }}" data-bibid="{{ $b->bibid }}"
|
||||||
</option>
|
{{ old('id_buku') == $b->id_buku ? 'selected' : '' }}>
|
||||||
@endforeach
|
{{ $b->bibid }} - {{ $b->judul }} (Stok: {{ $b->eksemplar }})
|
||||||
</select>
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<x-input-label value="Pilih Buku Kedua (Opsional)" />
|
||||||
|
<select name="id_buku_2" id="select-buku-peminjaman-2" x-init="new TomSelect($el, { maxOptions: null })"
|
||||||
|
class="mt-1 block w-full border-gray-200 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50 p-3">
|
||||||
|
<option value="">-- Pilih Buku Kedua (Kosongkan jika hanya 1) --</option>
|
||||||
|
@foreach ($buku as $b)
|
||||||
|
<option value="{{ $b->id_buku }}" data-bibid="{{ $b->bibid }}"
|
||||||
|
{{ old('id_buku_2') == $b->id_buku ? 'selected' : '' }}>
|
||||||
|
{{ $b->bibid }} - {{ $b->judul }} (Stok: {{ $b->eksemplar }})
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
|
@ -263,12 +297,14 @@ class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indi
|
||||||
<x-input-label value="Tanggal Pinjam" />
|
<x-input-label value="Tanggal Pinjam" />
|
||||||
<x-text-input type="date" name="tanggal_pinjam"
|
<x-text-input type="date" name="tanggal_pinjam"
|
||||||
value="{{ old('tanggal_pinjam', date('Y-m-d')) }}"
|
value="{{ old('tanggal_pinjam', date('Y-m-d')) }}"
|
||||||
|
min="2000-01-01" max="2100-12-31"
|
||||||
class="mt-1 block w-full bg-gray-50/50" required />
|
class="mt-1 block w-full bg-gray-50/50" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label value="Tanggal Kembali (Tenggat)" />
|
<x-input-label value="Tanggal Kembali (Tenggat)" />
|
||||||
<x-text-input type="date" name="tanggal_kembali"
|
<x-text-input type="date" name="tanggal_kembali"
|
||||||
value="{{ old('tanggal_kembali', date('Y-m-d', strtotime('+7 days'))) }}"
|
value="{{ old('tanggal_kembali', date('Y-m-d', strtotime('+7 days'))) }}"
|
||||||
|
min="2000-01-01" max="2100-12-31"
|
||||||
class="mt-1 block w-full bg-gray-50/50" required />
|
class="mt-1 block w-full bg-gray-50/50" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -365,7 +401,7 @@ class="bg-green-50 text-green-700 p-3 rounded-lg text-center font-bold text-xs m
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<form :action="'/admin/peminjaman/' + kembaliData.id + '/kembali'" method="POST">
|
<form :action="'{{ url('admin/peminjaman') }}/' + kembaliData.id + '/kembali'" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
@method('PUT')
|
@method('PUT')
|
||||||
<div class="flex items-center justify-end gap-2 pt-3 border-t border-gray-100">
|
<div class="flex items-center justify-end gap-2 pt-3 border-t border-gray-100">
|
||||||
|
|
@ -413,7 +449,7 @@ class="w-10 h-10 rounded-full bg-gray-50 hover:bg-red-50 text-gray-400 hover:tex
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form :action="'/admin/peminjaman/' + editData.id" method="POST" class="space-y-6">
|
<form :action="'{{ url('admin/peminjaman') }}/' + editData.id" method="POST" class="space-y-6">
|
||||||
@csrf
|
@csrf
|
||||||
@method('PUT')
|
@method('PUT')
|
||||||
<input type="hidden" name="peminjaman_id_edit" :value="editData.id">
|
<input type="hidden" name="peminjaman_id_edit" :value="editData.id">
|
||||||
|
|
@ -422,7 +458,7 @@ class="w-10 h-10 rounded-full bg-gray-50 hover:bg-red-50 text-gray-400 hover:tex
|
||||||
<x-input-label value="Pilih Anggota / Member" />
|
<x-input-label value="Pilih Anggota / Member" />
|
||||||
<select name="id_anggota" x-model="editData.id_anggota" x-init="let tsA = null;
|
<select name="id_anggota" x-model="editData.id_anggota" x-init="let tsA = null;
|
||||||
$watch('isModalEditOpen', value => { if (value && !tsA && $el.offsetHeight) setTimeout(() => { try { tsA = new TomSelect($el, { maxOptions: null }); } catch (e) {} }, 100) })"
|
$watch('isModalEditOpen', value => { if (value && !tsA && $el.offsetHeight) setTimeout(() => { try { tsA = new TomSelect($el, { maxOptions: null }); } catch (e) {} }, 100) })"
|
||||||
class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50"
|
class="mt-1 block w-full border-gray-200 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50 p-3"
|
||||||
required>
|
required>
|
||||||
<option value="">-- Pilih Member --</option>
|
<option value="">-- Pilih Member --</option>
|
||||||
@foreach ($anggota as $a)
|
@foreach ($anggota as $a)
|
||||||
|
|
@ -436,11 +472,11 @@ class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indi
|
||||||
<x-input-label value="Pilih Buku" />
|
<x-input-label value="Pilih Buku" />
|
||||||
<select name="id_buku" x-model="editData.id_buku" x-init="let tsB = null;
|
<select name="id_buku" x-model="editData.id_buku" x-init="let tsB = null;
|
||||||
$watch('isModalEditOpen', value => { if (value && !tsB && $el.offsetHeight) setTimeout(() => { try { tsB = new TomSelect($el, { maxOptions: null }); } catch (e) {} }, 100) })"
|
$watch('isModalEditOpen', value => { if (value && !tsB && $el.offsetHeight) setTimeout(() => { try { tsB = new TomSelect($el, { maxOptions: null }); } catch (e) {} }, 100) })"
|
||||||
class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50"
|
class="mt-1 block w-full border-gray-200 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50 p-3"
|
||||||
required>
|
required>
|
||||||
<option value="">-- Pilih Buku --</option>
|
<option value="">-- Pilih Buku --</option>
|
||||||
@foreach ($buku as $b)
|
@foreach ($buku as $b)
|
||||||
<option value="{{ $b->id }}">{{ $b->bibid }} - {{ $b->judul }}
|
<option value="{{ $b->id_buku }}">{{ $b->bibid }} - {{ $b->judul }}
|
||||||
</option>
|
</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -450,11 +486,13 @@ class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indi
|
||||||
<div>
|
<div>
|
||||||
<x-input-label value="Tanggal Pinjam" />
|
<x-input-label value="Tanggal Pinjam" />
|
||||||
<x-text-input type="date" name="tanggal_pinjam" x-model="editData.tanggal_pinjam"
|
<x-text-input type="date" name="tanggal_pinjam" x-model="editData.tanggal_pinjam"
|
||||||
|
min="2000-01-01" max="2100-12-31"
|
||||||
class="mt-1 block w-full bg-gray-50/50" required />
|
class="mt-1 block w-full bg-gray-50/50" required />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<x-input-label value="Tanggal Kembali (Tenggat)" />
|
<x-input-label value="Tanggal Kembali (Tenggat)" />
|
||||||
<x-text-input type="date" name="tanggal_kembali" x-model="editData.tanggal_kembali"
|
<x-text-input type="date" name="tanggal_kembali" x-model="editData.tanggal_kembali"
|
||||||
|
min="2000-01-01" max="2100-12-31"
|
||||||
class="mt-1 block w-full bg-gray-50/50" required />
|
class="mt-1 block w-full bg-gray-50/50" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -504,7 +542,7 @@ class="text-gray-400 hover:text-gray-600 transition-colors">
|
||||||
buku akan otomatis dikembalikan jika status masih dipinjam.</p>
|
buku akan otomatis dikembalikan jika status masih dipinjam.</p>
|
||||||
|
|
||||||
<div class="flex flex-col gap-3">
|
<div class="flex flex-col gap-3">
|
||||||
<form :action="'/admin/peminjaman/' + deleteId" method="POST" class="w-full m-0">
|
<form :action="'{{ url('admin/peminjaman') }}/' + deleteId" method="POST" class="w-full m-0">
|
||||||
@csrf
|
@csrf
|
||||||
@method('DELETE')
|
@method('DELETE')
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
|
|
@ -523,3 +561,123 @@ class="w-full py-4 bg-gray-50 hover:bg-gray-100 text-gray-600 rounded-2xl font-b
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
|
@push('scripts')
|
||||||
|
<script>
|
||||||
|
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() {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
|
|
|
||||||
|
|
@ -1,84 +1,101 @@
|
||||||
@extends('layouts.admin')
|
@extends('layouts.admin')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container mx-auto px-4 py-8 max-w-3xl">
|
<div class="container mx-auto px-4 py-8 max-w-3xl">
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<a href="{{ route('admin.peminjaman.index') }}" class="text-blue-600 hover:text-blue-800 font-medium">← Kembali ke Daftar Sirkulasi</a>
|
<a href="{{ route('admin.pengembalian.index') }}" class="text-blue-600 hover:text-blue-800 font-medium">←
|
||||||
</div>
|
Kembali ke Daftar Pengembalian</a>
|
||||||
|
|
||||||
<div class="bg-white shadow-lg rounded-xl overflow-hidden border border-gray-100 mb-8">
|
|
||||||
<div class="p-8 bg-gray-900 text-center">
|
|
||||||
<h1 class="text-2xl font-extrabold text-white mb-2">Pindai Barcode Buku</h1>
|
|
||||||
<p class="text-gray-400 text-sm mb-6">Arahkan scanner ke label BIBID pada buku yang dikembalikan.</p>
|
|
||||||
|
|
||||||
<form action="{{ route('admin.peminjaman.proses_scan') }}" method="POST" class="max-w-md mx-auto relative">
|
|
||||||
@csrf
|
|
||||||
<input type="text" name="bibid" autofocus autocomplete="off" placeholder="Scan Barcode di sini..." class="w-full pl-12 pr-4 py-4 text-center text-xl font-mono border-2 border-gray-700 bg-gray-800 text-white rounded-lg shadow-sm focus:border-blue-500 focus:ring-blue-500 focus:bg-gray-900 transition-colors">
|
|
||||||
<svg class="w-6 h-6 text-gray-500 absolute left-4 top-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm14 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z"></path></svg>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
@if(session('error'))
|
<div class="bg-white shadow-lg rounded-xl overflow-hidden border border-gray-100 mb-8">
|
||||||
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded shadow-sm" role="alert">
|
<div class="p-8 bg-gray-900 text-center">
|
||||||
<p class="font-bold">Pemindaian Gagal</p>
|
<h1 class="text-2xl font-extrabold text-white mb-2">Pindai Barcode Buku</h1>
|
||||||
<p>{{ session('error') }}</p>
|
<p class="text-gray-400 text-sm mb-6">Arahkan scanner ke label BIBID pada buku yang dikembalikan.</p>
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
@if(isset($peminjaman))
|
<form action="{{ route('admin.peminjaman.proses_scan') }}" method="POST" class="max-w-md mx-auto relative">
|
||||||
<div class="bg-white shadow-xl rounded-xl overflow-hidden border-2 border-green-500">
|
@csrf
|
||||||
<div class="p-6 bg-green-50 border-b border-green-100 flex justify-between items-center">
|
<input type="text" name="bibid" autofocus autocomplete="off" placeholder="Scan Barcode di sini..."
|
||||||
<h2 class="text-xl font-bold text-green-900">Data Transaksi Ditemukan</h2>
|
class="w-full pl-12 pr-4 py-4 text-center text-xl font-mono border-2 border-gray-700 bg-gray-800 text-white rounded-lg shadow-sm focus:border-blue-500 focus:ring-blue-500 focus:bg-gray-900 transition-colors">
|
||||||
<span class="bg-green-200 text-green-800 px-3 py-1 rounded-full text-xs font-bold uppercase">#{{ $peminjaman->id_peminjaman }}</span>
|
<svg class="w-6 h-6 text-gray-500 absolute left-4 top-4" fill="none" stroke="currentColor"
|
||||||
</div>
|
viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
<div class="p-6 grid grid-cols-1 md:grid-cols-2 gap-8">
|
d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm14 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z">
|
||||||
<div class="space-y-4">
|
</path>
|
||||||
<div>
|
</svg>
|
||||||
<span class="block text-xs font-bold text-gray-500 uppercase">Peminjam</span>
|
</form>
|
||||||
<span class="block text-lg font-bold text-gray-900">{{ $peminjaman->anggota?->nama ?? ($peminjaman->user?->name ?? 'Anonim') }}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="block text-xs font-bold text-gray-500 uppercase">Aset Buku</span>
|
|
||||||
<span class="block text-base font-bold text-gray-900">{{ $buku->judul }}</span>
|
|
||||||
<span class="block text-sm text-gray-600 font-mono">{{ $buku->bibid }}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="block text-xs font-bold text-gray-500 uppercase">Tenggat Waktu</span>
|
|
||||||
<span class="block text-sm font-medium {{ $denda > 0 ? 'text-red-600' : 'text-gray-900' }}">
|
|
||||||
{{ \Carbon\Carbon::parse($peminjaman->tanggal_kembali)->format('d F Y') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col space-y-4">
|
|
||||||
<div class="bg-blue-900 rounded-xl p-5 text-center text-white shadow-inner flex-1 flex flex-col justify-center border border-blue-800">
|
|
||||||
<span class="block text-xs font-bold text-blue-300 uppercase tracking-widest mb-2">Panduan Pengembalian Rak Fisik</span>
|
|
||||||
<span class="block text-4xl font-black mb-1 drop-shadow-md">{{ $lokasi['rak'] }}</span>
|
|
||||||
<span class="block text-sm text-blue-100 font-medium bg-blue-950/50 py-1 rounded-full w-max mx-auto px-4 mt-2 border border-blue-700/50">{{ $lokasi['area'] }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if($denda > 0)
|
|
||||||
<div class="bg-red-50 border-2 border-red-200 rounded-xl p-4 text-center">
|
|
||||||
<span class="block text-xs font-bold text-red-600 uppercase mb-1">Denda Keterlambatan</span>
|
|
||||||
<span class="block text-2xl font-black text-red-700">Rp {{ number_format($denda, 0, ',', '.') }}</span>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-6 border-t border-gray-100 bg-gray-50 flex justify-end">
|
@if(session('error'))
|
||||||
<form action="{{ route('admin.peminjaman.kembalikan', $peminjaman->id_peminjaman) }}" method="POST" onsubmit="return confirm('Selesaikan transaksi dan pulihkan stok buku?');">
|
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded shadow-sm" role="alert">
|
||||||
@csrf
|
<p class="font-bold">Pemindaian Gagal</p>
|
||||||
@method('PUT')
|
<p>{{ session('error') }}</p>
|
||||||
<button type="submit" class="bg-green-600 hover:bg-green-700 text-white font-bold py-4 px-8 rounded-lg shadow-md transition duration-300 w-full md:w-auto text-lg flex items-center gap-2">
|
</div>
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>
|
@endif
|
||||||
Konfirmasi Pengembalian
|
|
||||||
</button>
|
@if(isset($peminjaman))
|
||||||
</form>
|
<div class="bg-white shadow-xl rounded-xl overflow-hidden border-2 border-green-500">
|
||||||
</div>
|
<div class="p-6 bg-green-50 border-b border-green-100 flex justify-between items-center">
|
||||||
|
<h2 class="text-xl font-bold text-green-900">Data Transaksi Ditemukan</h2>
|
||||||
|
<span
|
||||||
|
class="bg-green-200 text-green-800 px-3 py-1 rounded-full text-xs font-bold uppercase">#{{ $peminjaman->id_peminjaman }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6 grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<span class="block text-xs font-bold text-gray-500 uppercase">Peminjam</span>
|
||||||
|
<span
|
||||||
|
class="block text-lg font-bold text-gray-900">{{ $peminjaman->anggota?->nama ?? ($peminjaman->user?->name ?? 'Anonim') }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="block text-xs font-bold text-gray-500 uppercase">Aset Buku</span>
|
||||||
|
<span class="block text-base font-bold text-gray-900">{{ $buku->judul }}</span>
|
||||||
|
<span class="block text-sm text-gray-600 font-mono">{{ $buku->bibid }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="block text-xs font-bold text-gray-500 uppercase">Tenggat Waktu</span>
|
||||||
|
<span class="block text-sm font-medium {{ $denda > 0 ? 'text-red-600' : 'text-gray-900' }}">
|
||||||
|
{{ \Carbon\Carbon::parse($peminjaman->tanggal_kembali)->format('d F Y') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col space-y-4">
|
||||||
|
<div
|
||||||
|
class="bg-blue-900 rounded-xl p-5 text-center text-white shadow-inner flex-1 flex flex-col justify-center border border-blue-800">
|
||||||
|
<span class="block text-xs font-bold text-blue-300 uppercase tracking-widest mb-2">Panduan
|
||||||
|
Pengembalian Rak Fisik</span>
|
||||||
|
<span class="block text-4xl font-black mb-1 drop-shadow-md">{{ $lokasi['rak'] }}</span>
|
||||||
|
<span
|
||||||
|
class="block text-sm text-blue-100 font-medium bg-blue-950/50 py-1 rounded-full w-max mx-auto px-4 mt-2 border border-blue-700/50">{{ $lokasi['area'] }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($denda > 0)
|
||||||
|
<div class="bg-red-50 border-2 border-red-200 rounded-xl p-4 text-center">
|
||||||
|
<span class="block text-xs font-bold text-red-600 uppercase mb-1">Denda Keterlambatan</span>
|
||||||
|
<span class="block text-2xl font-black text-red-700">Rp
|
||||||
|
{{ number_format($denda, 0, ',', '.') }}</span>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6 border-t border-gray-100 bg-gray-50 flex justify-end">
|
||||||
|
<form action="{{ route('admin.peminjaman.kembalikan', $peminjaman->id_peminjaman) }}" method="POST"
|
||||||
|
onsubmit="return confirm('Selesaikan transaksi dan pulihkan stok buku?');">
|
||||||
|
@csrf
|
||||||
|
@method('PUT')
|
||||||
|
<button type="submit"
|
||||||
|
class="bg-green-600 hover:bg-green-700 text-white font-bold py-4 px-8 rounded-lg shadow-md transition duration-300 w-full md:w-auto text-lg flex items-center gap-2">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||||
|
</svg>
|
||||||
|
Konfirmasi Pengembalian
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endsection
|
||||||
</div>
|
|
||||||
@endsection
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="jenis_anggota" value="Jenis Anggota" />
|
<x-input-label for="jenis_anggota" value="Jenis Anggota" />
|
||||||
<select id="jenis_anggota" name="jenis_anggota" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm" required onchange="toggleProdi()">
|
<select id="jenis_anggota" name="jenis_anggota" x-data x-init="new TomSelect($el, { maxOptions: null })" class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50" required onchange="toggleProdi()">
|
||||||
<option value="" disabled {{ old('jenis_anggota') ? '' : 'selected' }}>-- Pilih Jenis --</option>
|
<option value="" disabled {{ old('jenis_anggota') ? '' : 'selected' }}>-- Pilih Jenis --</option>
|
||||||
<option value="Mahasiswa" {{ old('jenis_anggota') == 'Mahasiswa' ? 'selected' : '' }}>Mahasiswa</option>
|
<option value="Mahasiswa" {{ old('jenis_anggota') == 'Mahasiswa' ? 'selected' : '' }}>Mahasiswa</option>
|
||||||
<option value="Siswa" {{ old('jenis_anggota') == 'Siswa' ? 'selected' : '' }}>Siswa</option>
|
<option value="Siswa" {{ old('jenis_anggota') == 'Siswa' ? 'selected' : '' }}>Siswa</option>
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
|
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
<x-input-label for="alamat" value="Alamat Lengkap" />
|
<x-input-label for="alamat" value="Alamat Lengkap" />
|
||||||
<textarea id="alamat" name="alamat" rows="3" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm" placeholder="Masukkan alamat lengkap" required>{{ old('alamat') }}</textarea>
|
<textarea id="alamat" name="alamat" rows="3" class="mt-1 block w-full border border-gray-200 bg-gray-50 text-gray-900 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 p-3 shadow-sm hover:bg-white transition-all duration-200 outline-none" placeholder="Masukkan alamat lengkap" required>{{ old('alamat') }}</textarea>
|
||||||
<x-input-error class="mt-2" :messages="$errors->get('alamat')" />
|
<x-input-error class="mt-2" :messages="$errors->get('alamat')" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -98,7 +98,7 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="hubungan_wali" value="Hubungan" />
|
<x-input-label for="hubungan_wali" value="Hubungan" />
|
||||||
<select id="hubungan_wali" name="hubungan_wali" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm" required>
|
<select id="hubungan_wali" name="hubungan_wali" x-data x-init="new TomSelect($el, { maxOptions: null })" class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50" required>
|
||||||
<option value="" disabled {{ old('hubungan_wali') ? '' : 'selected' }}>-- Pilih Hubungan --</option>
|
<option value="" disabled {{ old('hubungan_wali') ? '' : 'selected' }}>-- Pilih Hubungan --</option>
|
||||||
<option value="Orang Tua" {{ old('hubungan_wali') == 'Orang Tua' ? 'selected' : '' }}>Orang Tua</option>
|
<option value="Orang Tua" {{ old('hubungan_wali') == 'Orang Tua' ? 'selected' : '' }}>Orang Tua</option>
|
||||||
<option value="Saudara" {{ old('hubungan_wali') == 'Saudara' ? 'selected' : '' }}>Saudara</option>
|
<option value="Saudara" {{ old('hubungan_wali') == 'Saudara' ? 'selected' : '' }}>Saudara</option>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="jenis_anggota" value="Jenis Anggota" />
|
<x-input-label for="jenis_anggota" value="Jenis Anggota" />
|
||||||
<select id="jenis_anggota" name="jenis_anggota" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm" required onchange="toggleProdi()">
|
<select id="jenis_anggota" name="jenis_anggota" x-data x-init="new TomSelect($el, { maxOptions: null })" class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50" required onchange="toggleProdi()">
|
||||||
<option value="" disabled>-- Pilih Jenis --</option>
|
<option value="" disabled>-- Pilih Jenis --</option>
|
||||||
@foreach(['Mahasiswa', 'Siswa', 'Dosen', 'Umum'] as $jenis)
|
@foreach(['Mahasiswa', 'Siswa', 'Dosen', 'Umum'] as $jenis)
|
||||||
<option value="{{ $jenis }}" {{ old('jenis_anggota', $anggota->jenis_anggota) == $jenis ? 'selected' : '' }}>{{ $jenis }}</option>
|
<option value="{{ $jenis }}" {{ old('jenis_anggota', $anggota->jenis_anggota) == $jenis ? 'selected' : '' }}>{{ $jenis }}</option>
|
||||||
|
|
@ -64,13 +64,13 @@
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="no_hp" value="No. HP" />
|
<x-input-label for="no_hp" value="No. HP" />
|
||||||
<x-text-input id="no_hp" name="no_hp" type="text" class="mt-1 block w-full" :value="old('no_hp', $anggota->no_hp)" required />
|
<x-text-input id="no_hp" name="no_hp" type="number" class="mt-1 block w-full" :value="old('no_hp', $anggota->no_hp)" required />
|
||||||
<x-input-error class="mt-2" :messages="$errors->get('no_hp')" />
|
<x-input-error class="mt-2" :messages="$errors->get('no_hp')" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
<x-input-label for="alamat" value="Alamat Lengkap" />
|
<x-input-label for="alamat" value="Alamat Lengkap" />
|
||||||
<textarea id="alamat" name="alamat" rows="3" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm" required>{{ old('alamat', $anggota->alamat) }}</textarea>
|
<textarea id="alamat" name="alamat" rows="3" class="mt-1 block w-full border border-gray-200 bg-gray-50 text-gray-900 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 p-3 shadow-sm hover:bg-white transition-all duration-200 outline-none" required>{{ old('alamat', $anggota->alamat) }}</textarea>
|
||||||
<x-input-error class="mt-2" :messages="$errors->get('alamat')" />
|
<x-input-error class="mt-2" :messages="$errors->get('alamat')" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -92,13 +92,13 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="no_hp_wali" value="No. HP Wali" />
|
<x-input-label for="no_hp_wali" value="No. HP Wali" />
|
||||||
<x-text-input id="no_hp_wali" name="no_hp_wali" type="text" class="mt-1 block w-full" :value="old('no_hp_wali', $anggota->no_hp_wali)" required />
|
<x-text-input id="no_hp_wali" name="no_hp_wali" type="number" class="mt-1 block w-full" :value="old('no_hp_wali', $anggota->no_hp_wali)" required />
|
||||||
<x-input-error class="mt-2" :messages="$errors->get('no_hp_wali')" />
|
<x-input-error class="mt-2" :messages="$errors->get('no_hp_wali')" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="hubungan_wali" value="Hubungan" />
|
<x-input-label for="hubungan_wali" value="Hubungan" />
|
||||||
<select id="hubungan_wali" name="hubungan_wali" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm" required>
|
<select id="hubungan_wali" name="hubungan_wali" x-data x-init="new TomSelect($el, { maxOptions: null })" class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50" required>
|
||||||
<option value="" disabled>-- Pilih Hubungan --</option>
|
<option value="" disabled>-- Pilih Hubungan --</option>
|
||||||
@foreach(['Orang Tua', 'Saudara', 'Dosen Wali', 'Lainnya'] as $hub)
|
@foreach(['Orang Tua', 'Saudara', 'Dosen Wali', 'Lainnya'] as $hub)
|
||||||
<option value="{{ $hub }}" {{ old('hubungan_wali', $anggota->hubungan_wali) == $hub ? 'selected' : '' }}>{{ $hub }}</option>
|
<option value="{{ $hub }}" {{ old('hubungan_wali', $anggota->hubungan_wali) == $hub ? 'selected' : '' }}>{{ $hub }}</option>
|
||||||
|
|
@ -131,8 +131,10 @@ function toggleProdi() {
|
||||||
if (jenis === 'Umum') {
|
if (jenis === 'Umum') {
|
||||||
prodiField.style.display = 'none';
|
prodiField.style.display = 'none';
|
||||||
document.getElementById('prodi').value = '';
|
document.getElementById('prodi').value = '';
|
||||||
|
document.getElementById('prodi').removeAttribute('required');
|
||||||
} else {
|
} else {
|
||||||
prodiField.style.display = 'block';
|
prodiField.style.display = 'block';
|
||||||
|
document.getElementById('prodi').setAttribute('required', 'required');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logika Dynamic Form untuk Identitas Siswa / Pelajar
|
// Logika Dynamic Form untuk Identitas Siswa / Pelajar
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<div x-data="{ isModalAnggotaOpen: {{ ($errors->any() || request('add') == 'true') ? 'true' : 'false' }} }">
|
<div x-data="{ isModalAnggotaOpen: {{ ($errors->any() || request('add') == 'true') ? 'true' : 'false' }} }">
|
||||||
<x-page-header title="Data Anggota">
|
<x-page-header title="Data Anggota">
|
||||||
<x-slot name="actions">
|
<x-slot name="actions">
|
||||||
<button @click="isModalAnggotaOpen = true" class="bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-700 hover:to-teal-700 text-white px-5 py-2.5 rounded-xl shadow-lg shadow-emerald-500/30 transition-all font-semibold flex items-center gap-2 transform hover:scale-105">
|
<button @click="isModalAnggotaOpen = true" class="bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white px-5 py-2.5 rounded-xl shadow-lg shadow-blue-500/30 transition-all font-semibold flex items-center gap-2 transform hover:scale-105">
|
||||||
<i class="fas fa-user-plus"></i> Tambah Anggota
|
<i class="fas fa-user-plus"></i> Tambah Anggota
|
||||||
</button>
|
</button>
|
||||||
</x-slot>
|
</x-slot>
|
||||||
|
|
@ -164,7 +164,7 @@ class="inline-block px-4 pt-5 pb-4 overflow-hidden text-left align-bottom transi
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="jenis_anggota" value="Jenis Anggota" />
|
<x-input-label for="jenis_anggota" value="Jenis Anggota" />
|
||||||
<select id="jenis_anggota" name="jenis_anggota" x-model="jenisAnggota" class="mt-1 block w-full rounded-xl border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm bg-gray-50/50" required>
|
<select id="jenis_anggota" name="jenis_anggota" x-model="jenisAnggota" x-init="new TomSelect($el, { maxOptions: null })" class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50" required>
|
||||||
<option value="" disabled>-- Pilih Jenis --</option>
|
<option value="" disabled>-- Pilih Jenis --</option>
|
||||||
<option value="Mahasiswa">Mahasiswa</option>
|
<option value="Mahasiswa">Mahasiswa</option>
|
||||||
<option value="Siswa">Siswa</option>
|
<option value="Siswa">Siswa</option>
|
||||||
|
|
@ -185,7 +185,7 @@ class="inline-block px-4 pt-5 pb-4 overflow-hidden text-left align-bottom transi
|
||||||
|
|
||||||
<div x-show="jenisAnggota !== 'Umum'" x-transition>
|
<div x-show="jenisAnggota !== 'Umum'" x-transition>
|
||||||
<x-input-label for="prodi" value="Program Studi / Jurusan" />
|
<x-input-label for="prodi" value="Program Studi / Jurusan" />
|
||||||
<x-text-input id="prodi" name="prodi" type="text" class="mt-1 block w-full bg-gray-50/50" :value="old('prodi')" placeholder="Contoh: Teknik Informatika" />
|
<x-text-input id="prodi" name="prodi" type="text" class="mt-1 block w-full bg-gray-50/50" :value="old('prodi')" placeholder="Contoh: Teknik Informatika" x-bind:required="jenisAnggota !== 'Umum'" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -199,7 +199,7 @@ class="inline-block px-4 pt-5 pb-4 overflow-hidden text-left align-bottom transi
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="no_hp" value="No. HP" />
|
<x-input-label for="no_hp" value="No. HP" />
|
||||||
<x-text-input id="no_hp" name="no_hp" type="text" class="mt-1 block w-full bg-gray-50/50" :value="old('no_hp')" placeholder="08xxxxxxxxx" required />
|
<x-text-input id="no_hp" name="no_hp" type="number" class="mt-1 block w-full bg-gray-50/50" :value="old('no_hp')" placeholder="08xxxxxxxxx" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
|
|
@ -224,12 +224,12 @@ class="inline-block px-4 pt-5 pb-4 overflow-hidden text-left align-bottom transi
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="no_hp_wali" value="No. HP Wali" />
|
<x-input-label for="no_hp_wali" value="No. HP Wali" />
|
||||||
<x-text-input id="no_hp_wali" name="no_hp_wali" type="text" class="mt-1 block w-full bg-gray-50/50" :value="old('no_hp_wali')" placeholder="08xxxxxxxxx" required />
|
<x-text-input id="no_hp_wali" name="no_hp_wali" type="number" class="mt-1 block w-full bg-gray-50/50" :value="old('no_hp_wali')" placeholder="08xxxxxxxxx" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-input-label for="hubungan_wali" value="Hubungan" />
|
<x-input-label for="hubungan_wali" value="Hubungan" />
|
||||||
<select id="hubungan_wali" name="hubungan_wali" class="mt-1 block w-full rounded-xl border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm bg-gray-50/50" required>
|
<select id="hubungan_wali" name="hubungan_wali" x-init="new TomSelect($el, { maxOptions: null })" class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-xl shadow-sm bg-gray-50/50" required>
|
||||||
<option value="" disabled {{ old('hubungan_wali') ? '' : 'selected' }}>-- Pilih Hubungan --</option>
|
<option value="" disabled {{ old('hubungan_wali') ? '' : 'selected' }}>-- Pilih Hubungan --</option>
|
||||||
<option value="Orang Tua" {{ old('hubungan_wali') == 'Orang Tua' ? 'selected' : '' }}>Orang Tua</option>
|
<option value="Orang Tua" {{ old('hubungan_wali') == 'Orang Tua' ? 'selected' : '' }}>Orang Tua</option>
|
||||||
<option value="Saudara" {{ old('hubungan_wali') == 'Saudara' ? 'selected' : '' }}>Saudara</option>
|
<option value="Saudara" {{ old('hubungan_wali') == 'Saudara' ? 'selected' : '' }}>Saudara</option>
|
||||||
|
|
@ -244,7 +244,7 @@ class="inline-block px-4 pt-5 pb-4 overflow-hidden text-left align-bottom transi
|
||||||
<button type="button" @click="isModalAnggotaOpen = false" class="px-5 py-2.5 text-gray-600 bg-gray-100 hover:bg-gray-200 hover:text-gray-900 rounded-xl font-semibold transition-colors">
|
<button type="button" @click="isModalAnggotaOpen = false" class="px-5 py-2.5 text-gray-600 bg-gray-100 hover:bg-gray-200 hover:text-gray-900 rounded-xl font-semibold transition-colors">
|
||||||
Batal
|
Batal
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-700 hover:to-teal-700 text-white px-6 py-2.5 rounded-xl font-bold shadow-lg shadow-emerald-500/30 transition-all transform hover:scale-105">
|
<button type="submit" class="bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white px-6 py-2.5 rounded-xl font-bold shadow-lg shadow-blue-500/30 transition-all transform hover:scale-105">
|
||||||
<i class="fas fa-save mr-2"></i> Simpan Anggota
|
<i class="fas fa-save mr-2"></i> Simpan Anggota
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,31 @@
|
||||||
@keyframes blob { 0% { transform: translate(0,0) scale(1); } 33% { transform: translate(30px,-50px) scale(1.1); } 66% { transform: translate(-20px,20px) scale(0.9); } 100% { transform: translate(0,0) scale(1); } }
|
@keyframes blob { 0% { transform: translate(0,0) scale(1); } 33% { transform: translate(30px,-50px) scale(1.1); } 66% { transform: translate(-20px,20px) scale(0.9); } 100% { transform: translate(0,0) scale(1); } }
|
||||||
.animate-blob { animation: blob 7s infinite; }
|
.animate-blob { animation: blob 7s infinite; }
|
||||||
.animation-delay-2000 { animation-delay: 2s; }
|
.animation-delay-2000 { animation-delay: 2s; }
|
||||||
|
|
||||||
|
/* Hide spin buttons for number input */
|
||||||
|
input::-webkit-outer-spin-button,
|
||||||
|
input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
input[type=number] {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Scrollbar */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
@ -79,8 +104,12 @@
|
||||||
{{-- RIGHT PANEL (Form) --}}
|
{{-- RIGHT PANEL (Form) --}}
|
||||||
<div class="w-full md:w-7/12 bg-gradient-to-br from-primary-900 via-primary-800 to-primary-900 relative flex flex-col p-8 md:p-12">
|
<div class="w-full md:w-7/12 bg-gradient-to-br from-primary-900 via-primary-800 to-primary-900 relative flex flex-col p-8 md:p-12">
|
||||||
|
|
||||||
<div class="mb-6 pb-4">
|
@php $activeTipe = old('tipe', request('tipe', 'member')); @endphp
|
||||||
<h2 class="text-2xl font-bold text-white tracking-wide mb-2">Buku Tamu</h2>
|
|
||||||
|
<div class="mb-5 pb-3">
|
||||||
|
<h2 id="form-title" class="text-2xl font-bold text-white tracking-wide mb-1">
|
||||||
|
{{ $activeTipe == 'member' ? 'Buku Tamu Anggota' : 'Buku Tamu Pengunjung Umum' }}
|
||||||
|
</h2>
|
||||||
<p class="text-primary-300/60 text-sm">Isi data kunjungan Anda hari ini.</p>
|
<p class="text-primary-300/60 text-sm">Isi data kunjungan Anda hari ini.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -96,65 +125,73 @@
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{{-- Toggle --}}
|
{{-- Toggle --}}
|
||||||
<div class="flex gap-3 mb-6">
|
<div class="flex gap-3 mb-6">
|
||||||
<label class="toggle-option flex-1 flex items-center gap-3 p-4 rounded-xl border border-white/10 active" id="toggle-member" onclick="switchMode('member')">
|
<label class="toggle-option flex-1 flex items-center gap-3 p-4 rounded-xl border border-white/10 {{ $activeTipe == 'member' ? 'active' : '' }}" id="toggle-member" onclick="switchMode('member')">
|
||||||
<input type="radio" name="mode_toggle" value="member" checked class="hidden">
|
<input type="radio" name="mode_toggle" value="member" {{ $activeTipe == 'member' ? 'checked' : '' }} class="hidden">
|
||||||
<div class="w-5 h-5 rounded-full border-2 border-white/40 flex items-center justify-center" id="radio-member">
|
<div class="w-5 h-5 rounded-full border-2 border-white/40 flex items-center justify-center" id="radio-member">
|
||||||
|
@if($activeTipe == 'member')
|
||||||
<div class="w-3 h-3 rounded-full bg-primary-400"></div>
|
<div class="w-3 h-3 rounded-full bg-primary-400"></div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-white text-sm font-semibold">Anggota</p>
|
<p class="text-white text-sm font-semibold">Anggota</p>
|
||||||
<p class="text-primary-300/50 text-[10px]">Sudah punya No. Anggota</p>
|
<p class="text-primary-300/50 text-[10px]">Punya NIK / No. Anggota</p>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label class="toggle-option flex-1 flex items-center gap-3 p-4 rounded-xl border border-white/10" id="toggle-tamu" onclick="switchMode('tamu')">
|
<label class="toggle-option flex-1 flex items-center gap-3 p-4 rounded-xl border border-white/10 {{ $activeTipe == 'tamu' ? 'active' : '' }}" id="toggle-tamu" onclick="switchMode('tamu')">
|
||||||
<input type="radio" name="mode_toggle" value="tamu" class="hidden">
|
<input type="radio" name="mode_toggle" value="tamu" {{ $activeTipe == 'tamu' ? 'checked' : '' }} class="hidden">
|
||||||
<div class="w-5 h-5 rounded-full border-2 border-white/40 flex items-center justify-center" id="radio-tamu"></div>
|
<div class="w-5 h-5 rounded-full border-2 border-white/40 flex items-center justify-center" id="radio-tamu">
|
||||||
|
@if($activeTipe == 'tamu')
|
||||||
|
<div class="w-3 h-3 rounded-full bg-primary-400"></div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-white text-sm font-semibold">Pengunjung</p>
|
<p class="text-white text-sm font-semibold">Pengunjung</p>
|
||||||
<p class="text-primary-300/50 text-[10px]">Belum punya No. Anggota</p>
|
<p class="text-primary-300/50 text-[10px]">Belum punya NIK / Anggota</p>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="POST" action="{{ route('buku_tamu.store') }}" class="space-y-6" id="buku-tamu-form">
|
<form method="POST" action="{{ route('buku_tamu.store') }}" class="space-y-6" id="buku-tamu-form">
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" name="tipe" id="tipe-input" value="member">
|
<input type="hidden" name="tipe" id="tipe-input" value="{{ $activeTipe }}">
|
||||||
|
|
||||||
<div id="form-member" class="space-y-5">
|
<div id="form-member" class="space-y-5" style="{{ $activeTipe == 'member' ? '' : 'display: none;' }}">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">No. Anggota</label>
|
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">NIK / No. KTP / No. Anggota</label>
|
||||||
<input type="text" name="no_anggota" placeholder="Masukkan Nomor Anggota"
|
<input type="text" name="no_anggota" placeholder="Masukkan 16 digit NIK atau Nomor Anggota"
|
||||||
class="w-full glass-input rounded-xl px-5 py-4 text-sm" value="{{ old('no_anggota') }}">
|
class="w-full glass-input rounded-xl px-5 py-4 text-sm" value="{{ old('no_anggota') }}" {{ $activeTipe == 'member' ? 'required' : '' }}>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="form-tamu" class="space-y-5" style="display: none;">
|
<div id="form-tamu" class="space-y-5" style="{{ $activeTipe == 'tamu' ? '' : 'display: none;' }}">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">Nama Lengkap</label>
|
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">Nama Lengkap</label>
|
||||||
<input type="text" name="nama_tamu" placeholder="Isi nama lengkap anda"
|
<input type="text" name="nama_tamu" placeholder="Isi nama lengkap anda"
|
||||||
class="w-full glass-input rounded-xl px-5 py-4 text-sm" value="{{ old('nama_tamu') }}">
|
class="w-full glass-input rounded-xl px-5 py-4 text-sm" value="{{ old('nama_tamu') }}" {{ $activeTipe == 'tamu' ? 'required' : '' }}>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">Email</label>
|
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">Email</label>
|
||||||
<input type="email" name="email" placeholder="Isi email anda"
|
<input type="email" name="email" placeholder="Isi email anda"
|
||||||
class="w-full glass-input rounded-xl px-5 py-4 text-sm" value="{{ old('email') }}">
|
class="w-full glass-input rounded-xl px-5 py-4 text-sm" value="{{ old('email') }}" {{ $activeTipe == 'tamu' ? 'required' : '' }}>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">No HP</label>
|
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">No HP</label>
|
||||||
<input type="text" name="no_hp" placeholder="+62"
|
<input type="number" name="no_hp" placeholder="08..."
|
||||||
class="w-full glass-input rounded-xl px-5 py-4 text-sm" value="{{ old('no_hp') }}">
|
class="w-full glass-input rounded-xl px-5 py-4 text-sm" value="{{ old('no_hp') }}" {{ $activeTipe == 'tamu' ? 'required' : '' }} oninput="if(this.value.length > 13) this.value = this.value.slice(0, 13);">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">Asal Instansi/ Sekolah / Kampus</label>
|
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">Asal Instansi/ Sekolah / Kampus</label>
|
||||||
<input type="text" name="asal_instansi" placeholder="Politeknik Negeri Jember"
|
<input type="text" name="asal_instansi" placeholder="Politeknik Negeri Jember"
|
||||||
class="w-full glass-input rounded-xl px-5 py-4 text-sm" value="{{ old('asal_instansi') }}">
|
class="w-full glass-input rounded-xl px-5 py-4 text-sm" value="{{ old('asal_instansi') }}" {{ $activeTipe == 'tamu' ? 'required' : '' }}>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">Status</label>
|
<label class="block text-xs font-semibold text-primary-200/70 mb-2 ml-1 uppercase tracking-wider">Status</label>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<select name="status" class="w-full glass-input rounded-xl px-5 py-4 text-sm appearance-none cursor-pointer">
|
<select name="status" class="w-full glass-input rounded-xl px-5 py-4 text-sm appearance-none cursor-pointer" {{ $activeTipe == 'tamu' ? 'required' : '' }}>
|
||||||
<option value="" disabled selected>Pilih Status</option>
|
<option value="" disabled selected>Pilih Status</option>
|
||||||
<option value="Siswa" {{ old('status') == 'Siswa' ? 'selected' : '' }}>Siswa</option>
|
<option value="Siswa" {{ old('status') == 'Siswa' ? 'selected' : '' }}>Siswa</option>
|
||||||
<option value="Mahasiswa" {{ old('status') == 'Mahasiswa' ? 'selected' : '' }}>Mahasiswa</option>
|
<option value="Mahasiswa" {{ old('status') == 'Mahasiswa' ? 'selected' : '' }}>Mahasiswa</option>
|
||||||
|
|
@ -204,14 +241,7 @@ class="group w-full py-4 bg-gradient-to-r from-primary-400 to-primary-500 hover:
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center pt-2">
|
|
||||||
<p class="text-sm text-primary-200/40">
|
|
||||||
Belum punya akun anggota?
|
|
||||||
<a href="{{ route('register') }}" class="text-white font-semibold hover:text-primary-300 transition underline decoration-primary-400/30 underline-offset-4">
|
|
||||||
Daftar Sekarang
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -226,6 +256,10 @@ function switchMode(mode) {
|
||||||
const radioMember = document.getElementById('radio-member');
|
const radioMember = document.getElementById('radio-member');
|
||||||
const radioTamu = document.getElementById('radio-tamu');
|
const radioTamu = document.getElementById('radio-tamu');
|
||||||
const tipeInput = document.getElementById('tipe-input');
|
const tipeInput = document.getElementById('tipe-input');
|
||||||
|
const formTitle = document.getElementById('form-title');
|
||||||
|
|
||||||
|
const reqMember = formMember.querySelectorAll('input, select');
|
||||||
|
const reqTamu = formTamu.querySelectorAll('input, select');
|
||||||
|
|
||||||
if (mode === 'member') {
|
if (mode === 'member') {
|
||||||
formMember.style.display = 'block';
|
formMember.style.display = 'block';
|
||||||
|
|
@ -235,6 +269,10 @@ function switchMode(mode) {
|
||||||
radioMember.innerHTML = '<div class="w-3 h-3 rounded-full bg-primary-400"></div>';
|
radioMember.innerHTML = '<div class="w-3 h-3 rounded-full bg-primary-400"></div>';
|
||||||
radioTamu.innerHTML = '';
|
radioTamu.innerHTML = '';
|
||||||
tipeInput.value = 'member';
|
tipeInput.value = 'member';
|
||||||
|
if(formTitle) formTitle.innerText = 'Buku Tamu Anggota';
|
||||||
|
|
||||||
|
reqMember.forEach(el => el.setAttribute('required', 'required'));
|
||||||
|
reqTamu.forEach(el => el.removeAttribute('required'));
|
||||||
} else {
|
} else {
|
||||||
formMember.style.display = 'none';
|
formMember.style.display = 'none';
|
||||||
formTamu.style.display = 'block';
|
formTamu.style.display = 'block';
|
||||||
|
|
@ -243,6 +281,10 @@ function switchMode(mode) {
|
||||||
radioMember.innerHTML = '';
|
radioMember.innerHTML = '';
|
||||||
radioTamu.innerHTML = '<div class="w-3 h-3 rounded-full bg-primary-400"></div>';
|
radioTamu.innerHTML = '<div class="w-3 h-3 rounded-full bg-primary-400"></div>';
|
||||||
tipeInput.value = 'tamu';
|
tipeInput.value = 'tamu';
|
||||||
|
if(formTitle) formTitle.innerText = 'Buku Tamu Pengunjung Umum';
|
||||||
|
|
||||||
|
reqMember.forEach(el => el.removeAttribute('required'));
|
||||||
|
reqTamu.forEach(el => el.setAttribute('required', 'required'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<div {{ $attributes->merge(['class' => 'bg-white overflow-hidden shadow-sm sm:rounded-lg']) }}>
|
<div {{ $attributes->merge(['class' => 'bg-white overflow-hidden shadow-[0_8px_30px_rgb(0,0,0,0.04)] sm:rounded-2xl border border-gray-100']) }}>
|
||||||
<div class="p-6 text-gray-900">
|
<div class="p-8 text-gray-800">
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 focus:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}>
|
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex justify-center items-center px-6 py-3 bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 border border-transparent rounded-xl font-bold text-xs text-white uppercase tracking-widest shadow-md shadow-blue-500/30 active:scale-[0.98] focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-all duration-200']) }}>
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
@props(['disabled' => false])
|
@props(['disabled' => false])
|
||||||
|
|
||||||
<input @disabled($disabled) {{ $attributes->merge(['class' => 'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm']) }}>
|
<input @disabled($disabled) {{ $attributes->merge(['class' => 'border border-gray-200 bg-gray-50 text-gray-900 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block w-full p-3 shadow-sm hover:bg-white transition-all duration-200 outline-none']) }}>
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,78 @@
|
||||||
@section('content')
|
@section('content')
|
||||||
<x-page-header title="Laporan Kehadiran" />
|
<x-page-header title="Laporan Kehadiran" />
|
||||||
<x-card>
|
<x-card>
|
||||||
<div class="mb-4 flex flex-col md:flex-row justify-between items-center gap-4">
|
<!-- Filter Form (Sembunyikan saat cetak) -->
|
||||||
<p class="text-gray-500 text-sm">Berikut adalah daftar rekapitulasi kehadiran pengunjung dan anggota perpustakaan.</p>
|
<form method="GET" action="{{ route('admin.laporan.kehadiran') }}" class="mb-6 bg-gray-50 p-5 rounded-2xl border border-gray-200/60 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4 items-end print:hidden">
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-700 mb-1.5 uppercase tracking-wide">Bulan</label>
|
||||||
|
<select name="bulan" class="w-full bg-white border border-gray-200 text-gray-800 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block p-3 shadow-sm outline-none">
|
||||||
|
<option value="">-- Semua Bulan --</option>
|
||||||
|
@foreach([
|
||||||
|
'1' => 'Januari', '2' => 'Februari', '3' => 'Maret', '4' => 'April',
|
||||||
|
'5' => 'Mei', '6' => 'Juni', '7' => 'Juli', '8' => 'Agustus',
|
||||||
|
'9' => 'September', '10' => 'Oktober', '11' => 'November', '12' => 'Desember'
|
||||||
|
] as $num => $name)
|
||||||
|
<option value="{{ $num }}" {{ request('bulan') == $num ? 'selected' : '' }}>{{ $name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-700 mb-1.5 uppercase tracking-wide">Tahun</label>
|
||||||
|
<select name="tahun" class="w-full bg-white border border-gray-200 text-gray-800 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block p-3 shadow-sm outline-none">
|
||||||
|
<option value="">-- Semua Tahun --</option>
|
||||||
|
@for($y = date('Y') + 1; $y >= date('Y') - 4; $y--)
|
||||||
|
<option value="{{ $y }}" {{ request('tahun', date('Y')) == $y ? 'selected' : '' }}>{{ $y }}</option>
|
||||||
|
@endfor
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-700 mb-1.5 uppercase tracking-wide">Dari Baris</label>
|
||||||
|
<input type="number" name="limit_start" value="{{ request('limit_start') }}" min="1" placeholder="Contoh: 1" class="w-full bg-white border border-gray-200 text-gray-800 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block p-3 shadow-sm outline-none">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-700 mb-1.5 uppercase tracking-wide">Sampai Baris</label>
|
||||||
|
<input type="number" name="limit_end" value="{{ request('limit_end') }}" min="1" placeholder="Contoh: 20" class="w-full bg-white border border-gray-200 text-gray-800 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block p-3 shadow-sm outline-none">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button type="submit" class="flex-grow bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-xl shadow-md transition duration-300 text-sm flex items-center justify-center gap-2">
|
||||||
|
<i class="fas fa-filter"></i> Filter
|
||||||
|
</button>
|
||||||
|
@if(request()->anyFilled(['bulan', 'tahun', 'limit_start', 'limit_end']))
|
||||||
|
<a href="{{ route('admin.laporan.kehadiran') }}" class="bg-gray-100 hover:bg-red-50 border border-gray-200 text-gray-500 hover:text-red-500 p-3 rounded-xl shadow-sm transition duration-300 flex items-center justify-center" title="Reset Filter">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="mb-4 flex flex-col md:flex-row justify-between items-center gap-4 print:hidden">
|
||||||
|
<p class="text-gray-500 text-sm">Berikut adalah daftar rekapitulasi kehadiran pengunjung yang disaring.</p>
|
||||||
<button onclick="window.print()" class="bg-gray-800 hover:bg-gray-900 text-white px-4 py-2 rounded-lg text-sm font-bold shadow-sm transition-all flex items-center gap-2">
|
<button onclick="window.print()" class="bg-gray-800 hover:bg-gray-900 text-white px-4 py-2 rounded-lg text-sm font-bold shadow-sm transition-all flex items-center gap-2">
|
||||||
<i class="fas fa-print"></i> Cetak PDF / Print
|
<i class="fas fa-print"></i> Cetak PDF / Print
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="overflow-x-auto print:overflow-visible">
|
<div class="overflow-x-auto print:overflow-visible">
|
||||||
|
<!-- Print Header (Hanya terlihat saat cetak/print) -->
|
||||||
|
<div class="hidden print:block mb-6 border-b-2 border-gray-800 pb-4 text-center">
|
||||||
|
<h1 class="text-2xl font-black uppercase tracking-wide text-gray-900">Laporan Rekapitulasi Kehadiran</h1>
|
||||||
|
<h2 class="text-sm font-bold text-gray-700 mt-1">PERPUSTAKAAN DAERAH JEMBER</h2>
|
||||||
|
<p class="text-xs text-gray-500 mt-0.5">Jl. Mastrip No. 1, Kabupaten Jember</p>
|
||||||
|
<div class="text-[11px] text-gray-600 mt-3 flex flex-wrap justify-center gap-x-4 gap-y-1 font-medium border-t border-gray-100 pt-3">
|
||||||
|
@if(request('bulan') || request('tahun'))
|
||||||
|
<span>Periode:
|
||||||
|
{{ request('bulan') ? Carbon\Carbon::create()->month((int) request('bulan'))->translatedFormat('F') : 'Semua Bulan' }}
|
||||||
|
{{ request('tahun') ?? '' }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
<span>Dicetak Pada: {{ \Carbon\Carbon::now()->translatedFormat('d F Y H:i') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<x-table>
|
<x-table>
|
||||||
<x-slot name="head">
|
<x-slot name="head">
|
||||||
<x-th>No</x-th>
|
<x-th>No</x-th>
|
||||||
|
|
@ -24,7 +88,7 @@
|
||||||
|
|
||||||
@forelse($bukuTamu as $item)
|
@forelse($bukuTamu as $item)
|
||||||
<tr class="hover:bg-gray-50 transition-colors border-b border-gray-100 last:border-0">
|
<tr class="hover:bg-gray-50 transition-colors border-b border-gray-100 last:border-0">
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $loop->iteration }}</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ (request('limit_start') ? (int)request('limit_start') : 1) + $loop->index }}</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
@if($item->user)
|
@if($item->user)
|
||||||
{{-- Member path: data dari users --}}
|
{{-- Member path: data dari users --}}
|
||||||
|
|
@ -59,22 +123,68 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@media print {
|
@media print {
|
||||||
body * {
|
/* Hide layout elements not needed for print */
|
||||||
visibility: hidden;
|
header, aside, .sidebar, nav, footer, button, .print\:hidden, form, .mb-4 {
|
||||||
}
|
|
||||||
.sidebar, header, nav, footer, button, .mb-4 {
|
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
.print\:overflow-visible, .print\:overflow-visible * {
|
|
||||||
visibility: visible !important;
|
/* Reset body, main, and containers to allow natural multi-page flow */
|
||||||
|
html, body {
|
||||||
|
height: auto !important;
|
||||||
|
min-height: auto !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
background-color: #fff !important;
|
||||||
|
color: #000 !important;
|
||||||
}
|
}
|
||||||
.print\:overflow-visible {
|
|
||||||
position: absolute;
|
/* Force the relative layout container to overflow naturally */
|
||||||
left: 0;
|
div.flex.flex-1.overflow-hidden.relative {
|
||||||
top: 0;
|
display: block !important;
|
||||||
width: 100%;
|
height: auto !important;
|
||||||
margin: 0 !important;
|
min-height: auto !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
position: static !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: auto !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
position: static !important;
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset card and table parent wrappers */
|
||||||
|
.bg-white, .shadow-xl, .rounded-3xl, .p-6, .p-8 {
|
||||||
|
background: transparent !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: auto !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust table styles for print */
|
||||||
|
table {
|
||||||
|
width: 100% !important;
|
||||||
|
border-collapse: collapse !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 8px 12px !important;
|
||||||
|
border: 1px solid #ddd !important;
|
||||||
|
font-size: 11px !important;
|
||||||
|
color: #000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Avoid page breaks inside table rows */
|
||||||
|
tr {
|
||||||
|
page-break-inside: avoid !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,91 @@
|
||||||
@section('content')
|
@section('content')
|
||||||
<x-page-header title="Laporan Peminjaman" />
|
<x-page-header title="Laporan Peminjaman" />
|
||||||
<x-card>
|
<x-card>
|
||||||
<div class="mb-4 flex flex-col md:flex-row justify-between items-center gap-4">
|
<!-- Filter Form (Sembunyikan saat cetak) -->
|
||||||
<p class="text-gray-500 text-sm">Berikut adalah seluruh rekap data peminjaman buku perpustakaan.</p>
|
<form method="GET" action="{{ route('admin.laporan.peminjaman') }}" class="mb-6 bg-gray-50 p-5 rounded-2xl border border-gray-200/60 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6 gap-4 items-end print:hidden">
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-700 mb-1.5 uppercase tracking-wide">Bulan</label>
|
||||||
|
<select name="bulan" class="w-full bg-white border border-gray-200 text-gray-800 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block p-3 shadow-sm outline-none">
|
||||||
|
<option value="">-- Semua Bulan --</option>
|
||||||
|
@foreach([
|
||||||
|
'1' => 'Januari', '2' => 'Februari', '3' => 'Maret', '4' => 'April',
|
||||||
|
'5' => 'Mei', '6' => 'Juni', '7' => 'Juli', '8' => 'Agustus',
|
||||||
|
'9' => 'September', '10' => 'Oktober', '11' => 'November', '12' => 'Desember'
|
||||||
|
] as $num => $name)
|
||||||
|
<option value="{{ $num }}" {{ request('bulan') == $num ? 'selected' : '' }}>{{ $name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-700 mb-1.5 uppercase tracking-wide">Tahun</label>
|
||||||
|
<select name="tahun" class="w-full bg-white border border-gray-200 text-gray-800 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block p-3 shadow-sm outline-none">
|
||||||
|
<option value="">-- Semua Tahun --</option>
|
||||||
|
@for($y = date('Y') + 1; $y >= date('Y') - 4; $y--)
|
||||||
|
<option value="{{ $y }}" {{ request('tahun', date('Y')) == $y ? 'selected' : '' }}>{{ $y }}</option>
|
||||||
|
@endfor
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-700 mb-1.5 uppercase tracking-wide">Kategori & Lokasi Buku</label>
|
||||||
|
<select name="id_kategori" class="w-full bg-white border border-gray-200 text-gray-800 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block p-3 shadow-sm outline-none">
|
||||||
|
<option value="">-- Semua Kategori --</option>
|
||||||
|
@foreach($categories as $key => $name)
|
||||||
|
<option value="{{ $key }}" {{ request('id_kategori') === (string)$key ? 'selected' : '' }}>{{ $name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-700 mb-1.5 uppercase tracking-wide">Dari Baris</label>
|
||||||
|
<input type="number" name="limit_start" value="{{ request('limit_start') }}" min="1" placeholder="Contoh: 1" class="w-full bg-white border border-gray-200 text-gray-800 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block p-3 shadow-sm outline-none">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-gray-700 mb-1.5 uppercase tracking-wide">Sampai Baris</label>
|
||||||
|
<input type="number" name="limit_end" value="{{ request('limit_end') }}" min="1" placeholder="Contoh: 20" class="w-full bg-white border border-gray-200 text-gray-800 text-sm rounded-xl focus:ring-blue-500 focus:border-blue-500 block p-3 shadow-sm outline-none">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button type="submit" class="flex-grow bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-xl shadow-md transition duration-300 text-sm flex items-center justify-center gap-2">
|
||||||
|
<i class="fas fa-filter"></i> Filter
|
||||||
|
</button>
|
||||||
|
@if(request()->anyFilled(['bulan', 'tahun', 'id_kategori', 'limit_start', 'limit_end']))
|
||||||
|
<a href="{{ route('admin.laporan.peminjaman') }}" class="bg-gray-100 hover:bg-red-50 border border-gray-200 text-gray-500 hover:text-red-500 p-3 rounded-xl shadow-sm transition duration-300 flex items-center justify-center" title="Reset Filter">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="mb-4 flex flex-col md:flex-row justify-between items-center gap-4 print:hidden">
|
||||||
|
<p class="text-gray-500 text-sm">Berikut adalah rekap data peminjaman buku perpustakaan yang disaring.</p>
|
||||||
<button onclick="window.print()" class="bg-gray-800 hover:bg-gray-900 text-white px-4 py-2 rounded-lg text-sm font-bold shadow-sm transition-all flex items-center gap-2">
|
<button onclick="window.print()" class="bg-gray-800 hover:bg-gray-900 text-white px-4 py-2 rounded-lg text-sm font-bold shadow-sm transition-all flex items-center gap-2">
|
||||||
<i class="fas fa-print"></i> Cetak Laporan
|
<i class="fas fa-print"></i> Cetak Laporan
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="overflow-x-auto print:overflow-visible">
|
<div class="overflow-x-auto print:overflow-visible">
|
||||||
|
<!-- Print Header (Hanya terlihat saat cetak/print) -->
|
||||||
|
<div class="hidden print:block mb-6 border-b-2 border-gray-800 pb-4 text-center">
|
||||||
|
<h1 class="text-2xl font-black uppercase tracking-wide text-gray-900">Laporan Rekapitulasi Peminjaman</h1>
|
||||||
|
<h2 class="text-sm font-bold text-gray-700 mt-1">PERPUSTAKAAN DAERAH JEMBER</h2>
|
||||||
|
<p class="text-xs text-gray-500 mt-0.5">Jl. Mastrip No. 1, Kabupaten Jember</p>
|
||||||
|
<div class="text-[11px] text-gray-600 mt-3 flex flex-wrap justify-center gap-x-4 gap-y-1 font-medium border-t border-gray-100 pt-3">
|
||||||
|
@if(request('bulan') || request('tahun'))
|
||||||
|
<span>Periode:
|
||||||
|
{{ request('bulan') ? Carbon\Carbon::create()->month((int) request('bulan'))->translatedFormat('F') : 'Semua Bulan' }}
|
||||||
|
{{ request('tahun') ?? '' }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
@if(request('id_kategori') !== null && isset($categories[request('id_kategori')]))
|
||||||
|
<span>Kategori: {{ $categories[request('id_kategori')] }}</span>
|
||||||
|
@endif
|
||||||
|
<span>Dicetak Pada: {{ \Carbon\Carbon::now()->translatedFormat('d F Y H:i') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<x-table>
|
<x-table>
|
||||||
<x-slot name="head">
|
<x-slot name="head">
|
||||||
<x-th>No</x-th>
|
<x-th>No</x-th>
|
||||||
|
|
@ -27,7 +104,7 @@
|
||||||
|
|
||||||
@forelse($peminjaman as $index => $item)
|
@forelse($peminjaman as $index => $item)
|
||||||
<tr class="hover:bg-gray-50 transition-colors border-b border-gray-100 last:border-0">
|
<tr class="hover:bg-gray-50 transition-colors border-b border-gray-100 last:border-0">
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $index + 1 }}</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ (request('limit_start') ? (int)request('limit_start') : 1) + $loop->index }}</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
@if($item->anggota)
|
@if($item->anggota)
|
||||||
<div class="text-sm font-bold text-gray-900">{{ $item->anggota->nama }}</div>
|
<div class="text-sm font-bold text-gray-900">{{ $item->anggota->nama }}</div>
|
||||||
|
|
@ -80,22 +157,68 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@media print {
|
@media print {
|
||||||
body * {
|
/* Hide layout elements not needed for print */
|
||||||
visibility: hidden;
|
header, aside, .sidebar, nav, footer, button, .print\:hidden, form, .mb-4 {
|
||||||
}
|
|
||||||
.sidebar, header, nav, footer, button, .mb-4 {
|
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
.print\:overflow-visible, .print\:overflow-visible * {
|
|
||||||
visibility: visible !important;
|
/* Reset body, main, and containers to allow natural multi-page flow */
|
||||||
|
html, body {
|
||||||
|
height: auto !important;
|
||||||
|
min-height: auto !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
background-color: #fff !important;
|
||||||
|
color: #000 !important;
|
||||||
}
|
}
|
||||||
.print\:overflow-visible {
|
|
||||||
position: absolute;
|
/* Force the relative layout container to overflow naturally */
|
||||||
left: 0;
|
div.flex.flex-1.overflow-hidden.relative {
|
||||||
top: 0;
|
display: block !important;
|
||||||
width: 100%;
|
height: auto !important;
|
||||||
margin: 0 !important;
|
min-height: auto !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
position: static !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: auto !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
position: static !important;
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset card and table parent wrappers */
|
||||||
|
.bg-white, .shadow-xl, .rounded-3xl, .p-6, .p-8 {
|
||||||
|
background: transparent !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: auto !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust table styles for print */
|
||||||
|
table {
|
||||||
|
width: 100% !important;
|
||||||
|
border-collapse: collapse !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 8px 12px !important;
|
||||||
|
border: 1px solid #ddd !important;
|
||||||
|
font-size: 11px !important;
|
||||||
|
color: #000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Avoid page breaks inside table rows */
|
||||||
|
tr {
|
||||||
|
page-break-inside: avoid !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="id">
|
<html lang="id">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>@yield('title', 'Admin Dashboard') - SARAKATA</title>
|
<title>@yield('title', 'Admin Dashboard') - SARAKATA</title>
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap"
|
||||||
|
rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/css/tom-select.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/css/tom-select.css" rel="stylesheet">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/js/tom-select.complete.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/js/tom-select.complete.min.js"></script>
|
||||||
|
|
@ -16,64 +18,108 @@
|
||||||
extend: {
|
extend: {
|
||||||
fontFamily: { sans: ['Inter', 'sans-serif'] },
|
fontFamily: { sans: ['Inter', 'sans-serif'] },
|
||||||
colors: {
|
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' },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.gradient-text { background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 50%, #2563eb 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
.gradient-text {
|
||||||
|
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 50%, #2563eb 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TomSelect Custom Styling */
|
||||||
|
.ts-control {
|
||||||
|
border: 1px solid #e5e7eb !important;
|
||||||
|
background-color: #f9fafb !important;
|
||||||
|
border-radius: 0.75rem !important;
|
||||||
|
padding: 0.75rem 1rem !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ts-control.focus {
|
||||||
|
border-color: #6366f1 !important;
|
||||||
|
box-shadow: 0 0 0 1px #6366f1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ts-control input {
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-50 font-sans antialiased flex flex-col h-screen overflow-hidden">
|
|
||||||
|
<body class="bg-gray-50 font-sans antialiased flex flex-col h-screen overflow-hidden" x-data="{ sidebarOpen: false }">
|
||||||
|
|
||||||
{{-- TOP HEADER --}}
|
{{-- TOP HEADER --}}
|
||||||
<header class="bg-gradient-to-r from-primary-600 via-primary-700 to-primary-800 text-white h-16 flex items-center justify-between px-6 shadow-lg z-20">
|
<header
|
||||||
|
class="bg-gradient-to-r from-primary-600 via-primary-700 to-primary-800 text-white h-16 flex items-center justify-between px-6 shadow-lg z-20">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div class="w-9 h-9 bg-white/15 rounded-xl flex items-center justify-center backdrop-blur-sm">
|
<!-- Mobile Hamburger -->
|
||||||
|
<button @click="sidebarOpen = true"
|
||||||
|
class="md:hidden w-9 h-9 bg-white/15 hover:bg-white/25 rounded-xl flex items-center justify-center backdrop-blur-sm transition focus:outline-none">
|
||||||
|
<i class="fas fa-bars text-white"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="hidden md:flex w-9 h-9 bg-white/15 rounded-xl items-center justify-center backdrop-blur-sm">
|
||||||
<i class="fas fa-book-open text-lg"></i>
|
<i class="fas fa-book-open text-lg"></i>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="text-base font-bold tracking-wide">SARAKATA <span class="font-normal text-primary-200 hidden sm:inline">— Sistem Informasi Perpustakaan</span></h1>
|
<h1 class="text-base font-bold tracking-wide">SARAKATA <span
|
||||||
|
class="font-normal text-primary-200 hidden sm:inline">— Sistem Informasi Perpustakaan</span></h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<form method="POST" action="{{ route('logout') }}">
|
<form method="POST" action="{{ route('logout') }}">
|
||||||
@csrf
|
@csrf
|
||||||
<button class="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-white/10 hover:bg-white/20 transition text-sm font-medium">
|
<button
|
||||||
|
class="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-white/10 hover:bg-white/20 transition text-sm font-medium">
|
||||||
<i class="fas fa-sign-out-alt text-xs"></i> Logout
|
<i class="fas fa-sign-out-alt text-xs"></i> Logout
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<div class="w-9 h-9 rounded-xl overflow-hidden border-2 border-white/30">
|
<div class="w-9 h-9 rounded-xl overflow-hidden border-2 border-white/30">
|
||||||
<img src="https://ui-avatars.com/api/?name={{ auth()->user()->name ?? 'Admin' }}&background=6366f1&color=fff" class="w-full h-full object-cover">
|
<img src="https://ui-avatars.com/api/?name={{ auth()->user()->name ?? 'Admin' }}&background=6366f1&color=fff"
|
||||||
|
class="w-full h-full object-cover">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="flex flex-1 overflow-hidden">
|
<div class="flex flex-1 overflow-hidden relative">
|
||||||
|
<!-- Overlay Khusus Mobile -->
|
||||||
|
<div x-show="sidebarOpen" x-transition.opacity
|
||||||
|
class="fixed inset-0 bg-gray-900/60 z-40 md:hidden backdrop-blur-sm" @click="sidebarOpen = false"
|
||||||
|
style="display: none;"></div>
|
||||||
|
|
||||||
{{-- SIDEBAR --}}
|
{{-- SIDEBAR --}}
|
||||||
<aside class="w-64 bg-white shadow-xl flex flex-col overflow-y-auto z-10 border-r border-gray-100">
|
<aside :class="sidebarOpen ? 'translate-x-0' : '-translate-x-full'"
|
||||||
|
class="w-64 bg-white shadow-2xl flex flex-col overflow-y-auto z-50 border-r border-gray-100 absolute inset-y-0 left-0 transform md:relative md:translate-x-0 transition-transform duration-300 ease-in-out h-full">
|
||||||
|
|
||||||
{{-- Profile --}}
|
{{-- Profile --}}
|
||||||
<div class="flex flex-col items-center py-8 border-b border-gray-100 px-4">
|
<div class="flex flex-col items-center py-8 border-b border-gray-100 px-4">
|
||||||
<div class="w-20 h-20 rounded-2xl overflow-hidden border-4 border-primary-100 shadow-lg shadow-primary-100">
|
<div
|
||||||
<img src="https://ui-avatars.com/api/?name={{ auth()->user()->name ?? 'Admin' }}&background=6366f1&color=fff&size=128" class="w-full h-full object-cover">
|
class="w-20 h-20 rounded-2xl overflow-hidden border-4 border-primary-100 shadow-lg shadow-primary-100">
|
||||||
|
<img src="https://ui-avatars.com/api/?name={{ auth()->user()->name ?? 'Admin' }}&background=6366f1&color=fff&size=128"
|
||||||
|
class="w-full h-full object-cover">
|
||||||
</div>
|
</div>
|
||||||
<h2 class="mt-4 font-bold text-gray-800 text-lg">{{ auth()->user()->name ?? 'Admin' }}</h2>
|
<h2 class="mt-4 font-bold text-gray-800 text-lg">{{ auth()->user()->name ?? 'Admin' }}</h2>
|
||||||
<span class="px-3 py-1 mt-1.5 text-[10px] font-bold text-primary-700 bg-primary-50 rounded-full uppercase tracking-wider">Administrator</span>
|
<span
|
||||||
|
class="px-3 py-1 mt-1.5 text-[10px] font-bold text-primary-700 bg-primary-50 rounded-full uppercase tracking-wider">Administrator</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Navigation --}}
|
{{-- Navigation --}}
|
||||||
<nav class="flex-1 py-6 space-y-1 px-3">
|
<nav class="flex-1 py-6 space-y-1 px-3">
|
||||||
|
|
||||||
<a href="{{ route('admin.dashboard') }}"
|
<a href="{{ route('admin.dashboard') }}"
|
||||||
class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
||||||
{{ request()->routeIs('admin.dashboard') ? 'bg-gradient-to-r from-primary-500 to-primary-600 text-white shadow-lg shadow-primary-200' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
{{ request()->routeIs('admin.dashboard') ? 'bg-gradient-to-r from-primary-500 to-primary-600 text-white shadow-lg shadow-primary-200' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
||||||
<i class="fas fa-th-large w-5 text-center"></i>
|
<i class="fas fa-th-large w-5 text-center"></i>
|
||||||
<span class="font-semibold text-sm">Dashboard</span>
|
<span class="font-semibold text-sm">Dashboard</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="{{ route('admin.buku.index') }}"
|
<a href="{{ route('admin.buku.index') }}"
|
||||||
class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
||||||
{{ request()->routeIs('admin.buku.*') ? 'bg-gradient-to-r from-primary-500 to-primary-600 text-white shadow-lg shadow-primary-200' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
{{ request()->routeIs('admin.buku.*') ? 'bg-gradient-to-r from-primary-500 to-primary-600 text-white shadow-lg shadow-primary-200' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
||||||
<i class="fas fa-book w-5 text-center"></i>
|
<i class="fas fa-book w-5 text-center"></i>
|
||||||
<span class="font-semibold text-sm">Data Buku</span>
|
<span class="font-semibold text-sm">Data Buku</span>
|
||||||
|
|
@ -82,30 +128,35 @@ class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300
|
||||||
{{-- Anggota Dropdown --}}
|
{{-- Anggota Dropdown --}}
|
||||||
<div x-data="{ open: {{ request()->routeIs('admin.anggota.*') ? 'true' : 'false' }} }">
|
<div x-data="{ open: {{ request()->routeIs('admin.anggota.*') ? 'true' : 'false' }} }">
|
||||||
<button @click="open = !open"
|
<button @click="open = !open"
|
||||||
class="w-full flex items-center justify-between px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
class="w-full flex items-center justify-between px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
||||||
{{ request()->routeIs('admin.anggota.*') ? 'bg-primary-50 text-primary-600' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
{{ request()->routeIs('admin.anggota.*') ? 'bg-primary-50 text-primary-600' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<i class="fas fa-users w-5 text-center"></i>
|
<i class="fas fa-users w-5 text-center"></i>
|
||||||
<span class="font-semibold text-sm">Data Anggota</span>
|
<span class="font-semibold text-sm">Data Anggota</span>
|
||||||
</div>
|
</div>
|
||||||
<i :class="open ? 'rotate-180' : ''" class="fas fa-chevron-down text-[10px] transition-transform duration-200"></i>
|
<i :class="open ? 'rotate-180' : ''"
|
||||||
|
class="fas fa-chevron-down text-[10px] transition-transform duration-200"></i>
|
||||||
</button>
|
</button>
|
||||||
<div x-show="open" x-transition class="pl-12 pr-3 py-1 space-y-1">
|
<div x-show="open" x-transition class="pl-12 pr-3 py-1 space-y-1">
|
||||||
<a href="{{ route('admin.anggota.tamu') }}" class="block px-3 py-2 text-xs font-medium text-gray-400 hover:text-primary-600 hover:bg-primary-50 rounded-lg transition">Buku Tamu</a>
|
<a href="{{ route('admin.anggota.tamu') }}"
|
||||||
<a href="{{ route('admin.anggota.member.index') }}" class="block px-3 py-2 text-xs font-medium text-gray-400 hover:text-primary-600 hover:bg-primary-50 rounded-lg transition">Data Member</a>
|
class="block px-3 py-2 text-xs font-medium text-gray-400 hover:text-primary-600 hover:bg-primary-50 rounded-lg transition">Buku
|
||||||
|
Tamu</a>
|
||||||
|
<a href="{{ route('admin.anggota.member.index') }}"
|
||||||
|
class="block px-3 py-2 text-xs font-medium text-gray-400 hover:text-primary-600 hover:bg-primary-50 rounded-lg transition">Data
|
||||||
|
Member</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href="{{ route('admin.peminjaman.index') }}"
|
<a href="{{ route('admin.peminjaman.index') }}"
|
||||||
class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
||||||
{{ request()->routeIs('admin.peminjaman.*') ? 'bg-gradient-to-r from-primary-500 to-primary-600 text-white shadow-lg shadow-primary-200' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
{{ request()->routeIs('admin.peminjaman.*') && !request()->routeIs('admin.peminjaman.scan') ? 'bg-gradient-to-r from-primary-500 to-primary-600 text-white shadow-lg shadow-primary-200' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
||||||
<i class="fas fa-file-export w-5 text-center"></i>
|
<i class="fas fa-file-export w-5 text-center"></i>
|
||||||
<span class="font-semibold text-sm">Data Peminjaman</span>
|
<span class="font-semibold text-sm">Data Peminjaman</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="{{ route('admin.pengembalian.index') }}"
|
<a href="{{ route('admin.pengembalian.index') }}"
|
||||||
class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
||||||
{{ request()->routeIs('admin.pengembalian.*') ? 'bg-gradient-to-r from-primary-500 to-primary-600 text-white shadow-lg shadow-primary-200' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
{{ request()->routeIs('admin.pengembalian.*') || request()->routeIs('admin.peminjaman.scan') ? 'bg-gradient-to-r from-primary-500 to-primary-600 text-white shadow-lg shadow-primary-200' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
||||||
<i class="fas fa-file-import w-5 text-center"></i>
|
<i class="fas fa-file-import w-5 text-center"></i>
|
||||||
<span class="font-semibold text-sm">Data Pengembalian</span>
|
<span class="font-semibold text-sm">Data Pengembalian</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -113,23 +164,28 @@ class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300
|
||||||
{{-- Laporan Dropdown --}}
|
{{-- Laporan Dropdown --}}
|
||||||
<div x-data="{ open: {{ request()->routeIs('admin.laporan.*') ? 'true' : 'false' }} }">
|
<div x-data="{ open: {{ request()->routeIs('admin.laporan.*') ? 'true' : 'false' }} }">
|
||||||
<button @click="open = !open"
|
<button @click="open = !open"
|
||||||
class="w-full flex items-center justify-between px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
class="w-full flex items-center justify-between px-4 py-3 rounded-xl transition-all duration-300 mb-1
|
||||||
{{ request()->routeIs('admin.laporan.*') ? 'bg-primary-50 text-primary-600' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
{{ request()->routeIs('admin.laporan.*') ? 'bg-primary-50 text-primary-600' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<i class="fas fa-chart-bar w-5 text-center"></i>
|
<i class="fas fa-chart-bar w-5 text-center"></i>
|
||||||
<span class="font-semibold text-sm">Laporan</span>
|
<span class="font-semibold text-sm">Laporan</span>
|
||||||
</div>
|
</div>
|
||||||
<i :class="open ? 'rotate-180' : ''" class="fas fa-chevron-down text-[10px] transition-transform duration-200"></i>
|
<i :class="open ? 'rotate-180' : ''"
|
||||||
|
class="fas fa-chevron-down text-[10px] transition-transform duration-200"></i>
|
||||||
</button>
|
</button>
|
||||||
<div x-show="open" x-transition class="pl-12 pr-3 py-1 space-y-1">
|
<div x-show="open" x-transition class="pl-12 pr-3 py-1 space-y-1">
|
||||||
<a href="{{ route('admin.laporan.kehadiran') }}" class="block px-3 py-2 text-xs font-medium text-gray-400 hover:text-primary-600 hover:bg-primary-50 rounded-lg transition">Lap. Kehadiran</a>
|
<a href="{{ route('admin.laporan.kehadiran') }}"
|
||||||
<a href="{{ route('admin.laporan.peminjaman') }}" class="block px-3 py-2 text-xs font-medium text-gray-400 hover:text-primary-600 hover:bg-primary-50 rounded-lg transition">Lap. Peminjaman</a>
|
class="block px-3 py-2 text-xs font-medium text-gray-400 hover:text-primary-600 hover:bg-primary-50 rounded-lg transition">Lap.
|
||||||
|
Kehadiran</a>
|
||||||
|
<a href="{{ route('admin.laporan.peminjaman') }}"
|
||||||
|
class="block px-3 py-2 text-xs font-medium text-gray-400 hover:text-primary-600 hover:bg-primary-50 rounded-lg transition">Lap.
|
||||||
|
Peminjaman</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Akun Admin Menu --}}
|
{{-- Akun Admin Menu --}}
|
||||||
<a href="{{ route('admin.akun.index') }}"
|
<a href="{{ route('admin.akun.index') }}"
|
||||||
class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 mb-1 mt-4 border-t border-gray-100 pt-5
|
class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 mb-1 mt-4 border-t border-gray-100 pt-5
|
||||||
{{ request()->routeIs('admin.akun.*') ? 'bg-gradient-to-r from-primary-500 to-primary-600 text-white shadow-lg shadow-primary-200' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
{{ request()->routeIs('admin.akun.*') ? 'bg-gradient-to-r from-primary-500 to-primary-600 text-white shadow-lg shadow-primary-200' : 'text-gray-500 hover:bg-primary-50 hover:text-primary-600' }}">
|
||||||
<i class="fas fa-user-shield w-5 text-center"></i>
|
<i class="fas fa-user-shield w-5 text-center"></i>
|
||||||
<span class="font-semibold text-sm">Kelola Admin</span>
|
<span class="font-semibold text-sm">Kelola Admin</span>
|
||||||
|
|
@ -144,5 +200,53 @@ class="flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- SweetAlert2 for Global Delete Confirmation --}}
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
// Intercept all forms that have a confirm() in their onsubmit attribute
|
||||||
|
const deleteForms = document.querySelectorAll('form[onsubmit*="return confirm"]');
|
||||||
|
|
||||||
|
deleteForms.forEach(form => {
|
||||||
|
// Extract the message from the confirm('Message') call
|
||||||
|
const originalOnsubmit = form.getAttribute('onsubmit');
|
||||||
|
const messageMatch = originalOnsubmit.match(/confirm\(['"](.*?)['"]\)/);
|
||||||
|
const message = messageMatch ? messageMatch[1] : 'Apakah Anda yakin ingin menghapus data ini?';
|
||||||
|
|
||||||
|
// Remove the default onsubmit to prevent browser confirm
|
||||||
|
form.removeAttribute('onsubmit');
|
||||||
|
|
||||||
|
// Add the custom SweetAlert event listener
|
||||||
|
form.addEventListener('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Konfirmasi Hapus',
|
||||||
|
text: message,
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#ef4444',
|
||||||
|
cancelButtonColor: '#6b7280',
|
||||||
|
confirmButtonText: '<i class="fas fa-trash-alt mr-2"></i> Ya, Hapus',
|
||||||
|
cancelButtonText: 'Batal',
|
||||||
|
reverseButtons: true,
|
||||||
|
customClass: {
|
||||||
|
popup: 'rounded-3xl shadow-2xl',
|
||||||
|
title: 'text-2xl font-bold text-gray-800',
|
||||||
|
htmlContainer: 'text-gray-500',
|
||||||
|
confirmButton: 'rounded-xl px-6 py-2.5 font-bold shadow-lg shadow-red-500/30 transition-all ml-3',
|
||||||
|
cancelButton: 'rounded-xl px-6 py-2.5 font-bold bg-gray-100 text-gray-600 hover:bg-gray-200 transition-all'
|
||||||
|
},
|
||||||
|
buttonsStyling: false
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@stack('scripts')
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
<title>@yield('title', 'Sarakata — Perpustakaan Digital')</title>
|
<title>@yield('title', 'Sarakata — Perpustakaan Digital')</title>
|
||||||
<meta name="description" content="Sarakata - Sistem Informasi Perpustakaan Digital untuk Generasi Modern">
|
<meta name="description" content="Sarakata - Sistem Informasi Perpustakaan Digital untuk Generasi Modern">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="shortcut icon" href="{{ asset('favicon.ico') }}" type="image/x-icon">
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap"
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap"
|
||||||
rel="stylesheet">
|
rel="stylesheet">
|
||||||
|
|
@ -107,11 +108,11 @@
|
||||||
@stack('styles')
|
@stack('styles')
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-white text-gray-800 font-sans antialiased">
|
<body class="bg-white text-gray-800 font-sans antialiased overflow-x-hidden w-full">
|
||||||
|
|
||||||
{{-- NAVBAR --}}
|
{{-- NAVBAR --}}
|
||||||
<nav class="glass-nav fixed w-full top-0 z-50 border-b border-gray-100/50">
|
<nav class="glass-nav fixed w-full top-0 z-50 border-b border-gray-100/50">
|
||||||
<div class="max-w-7xl mx-auto px-6 py-4 flex justify-between items-center">
|
<div class="max-w-7xl mx-auto px-6 py-4 flex justify-between items-center relative">
|
||||||
<a href="{{ route('home') }}" class="flex items-center gap-3 group">
|
<a href="{{ route('home') }}" class="flex items-center gap-3 group">
|
||||||
<div
|
<div
|
||||||
class="w-10 h-10 bg-gradient-to-br from-primary-500 to-primary-700 rounded-xl flex items-center justify-center shadow-lg shadow-primary-200 group-hover:shadow-primary-300 transition-shadow">
|
class="w-10 h-10 bg-gradient-to-br from-primary-500 to-primary-700 rounded-xl flex items-center justify-center shadow-lg shadow-primary-200 group-hover:shadow-primary-300 transition-shadow">
|
||||||
|
|
@ -126,20 +127,27 @@ class="text-[10px] font-bold text-gray-500 uppercase tracking-widest mt-1 hidden
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="flex items-center gap-8 text-sm font-medium">
|
<!-- Hamburger Button for Mobile -->
|
||||||
|
<button id="mobile-menu-btn"
|
||||||
|
class="md:hidden text-gray-600 hover:text-primary-600 focus:outline-none p-2 rounded-lg bg-gray-50">
|
||||||
|
<i class="fas fa-bars text-xl"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Desktop Menu -->
|
||||||
|
<div class="hidden md:flex items-center gap-8 text-sm font-medium">
|
||||||
<a href="{{ route('home') }}"
|
<a href="{{ route('home') }}"
|
||||||
class="relative transition-colors {{ request()->routeIs('home') ? 'text-primary-600 font-semibold' : 'text-gray-600 hover:text-primary-600' }} after:absolute after:bottom-[-4px] after:left-0 {{ request()->routeIs('home') ? 'after:w-full' : 'after:w-0 hover:after:w-full' }} after:h-0.5 after:bg-primary-500 after:transition-all">Beranda</a>
|
class="relative transition-colors {{ request()->routeIs('home') ? 'text-primary-600 font-semibold' : 'text-gray-600 hover:text-primary-600' }} after:absolute after:bottom-[-4px] after:left-0 {{ request()->routeIs('home') ? 'after:w-full' : 'after:w-0 hover:after:w-full' }} after:h-0.5 after:bg-primary-500 after:transition-all">Beranda</a>
|
||||||
<a href="{{ route('katalog.index') }}"
|
<a href="{{ route('katalog.index') }}"
|
||||||
class="relative transition-colors {{ request()->routeIs('katalog.*') ? 'text-primary-600 font-semibold' : 'text-gray-600 hover:text-primary-600' }} after:absolute after:bottom-[-4px] after:left-0 {{ request()->routeIs('katalog.*') ? 'after:w-full' : 'after:w-0 hover:after:w-full' }} after:h-0.5 after:bg-primary-500 after:transition-all">Katalog
|
class="relative transition-colors {{ request()->routeIs('katalog.*') ? 'text-primary-600 font-semibold' : 'text-gray-600 hover:text-primary-600' }} after:absolute after:bottom-[-4px] after:left-0 {{ request()->routeIs('katalog.*') ? 'after:w-full' : 'after:w-0 hover:after:w-full' }} after:h-0.5 after:bg-primary-500 after:transition-all">Katalog
|
||||||
Buku</a>
|
Buku</a>
|
||||||
<a href="{{ route('home') }}#fitur"
|
<a href="{{ route('home') }}#fitur"
|
||||||
class="relative transition-colors {{ request()->routeIs('home') && request()->getQueryString() == '' ? 'text-primary-600 font-semibold' : 'text-gray-600 hover:text-primary-600' }} after:absolute after:bottom-[-4px] after:left-0 after:w-0 after:h-0.5 after:bg-primary-500 after:transition-all hover:after:w-full">Fitur</a>
|
class="relative transition-colors text-gray-600 hover:text-primary-600 after:absolute after:bottom-[-4px] after:left-0 after:w-0 after:h-0.5 after:bg-primary-500 after:transition-all hover:after:w-full">Fitur</a>
|
||||||
<a href="{{ route('home') }}#rekomendasi"
|
<a href="{{ route('home') }}#rekomendasi"
|
||||||
class="relative transition-colors {{ request()->routeIs('home') && request()->getQueryString() == '' ? 'text-primary-600 font-semibold' : 'text-gray-600 hover:text-primary-600' }} after:absolute after:bottom-[-4px] after:left-0 after:w-0 after:h-0.5 after:bg-primary-500 after:transition-all hover:after:w-full">Koleksi</a>
|
class="relative transition-colors text-gray-600 hover:text-primary-600 after:absolute after:bottom-[-4px] after:left-0 after:w-0 after:h-0.5 after:bg-primary-500 after:transition-all hover:after:w-full">Koleksi</a>
|
||||||
|
|
||||||
@guest
|
@guest
|
||||||
<button onclick="openGuestModal()"
|
<button onclick="openGuestModal()"
|
||||||
class="px-5 py-2.5 bg-primary-50 text-primary-600 rounded-xl font-semibold hover:bg-primary-100 transition-colors cursor-pointer">
|
class="px-5 py-2.5 bg-primary-50 text-primary-600 rounded-xl font-semibold hover:bg-primary-100 transition-colors cursor-pointer flex items-center justify-center">
|
||||||
<i class="fas fa-book-reader mr-1.5"></i> Buku Tamu
|
<i class="fas fa-book-reader mr-1.5"></i> Buku Tamu
|
||||||
</button>
|
</button>
|
||||||
<a href="{{ route('login') }}"
|
<a href="{{ route('login') }}"
|
||||||
|
|
@ -163,6 +171,47 @@ class="px-5 py-2.5 bg-gradient-to-r from-green-600 to-green-700 text-white round
|
||||||
@endauth
|
@endauth
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile Menu -->
|
||||||
|
<div id="mobile-menu"
|
||||||
|
class="hidden md:hidden bg-white/95 backdrop-blur-xl border-t border-gray-100 shadow-xl absolute w-full left-0 top-full flex-col gap-4 px-6 py-6 transition-all">
|
||||||
|
<a href="{{ route('home') }}"
|
||||||
|
class="block font-bold text-gray-700 hover:text-primary-600 py-2 border-b border-gray-50">Beranda</a>
|
||||||
|
<a href="{{ route('katalog.index') }}"
|
||||||
|
class="block font-bold text-gray-700 hover:text-primary-600 py-2 border-b border-gray-50">Katalog
|
||||||
|
Buku</a>
|
||||||
|
<a href="{{ route('home') }}#fitur"
|
||||||
|
class="block font-bold text-gray-700 hover:text-primary-600 py-2 border-b border-gray-50">Fitur</a>
|
||||||
|
<a href="{{ route('home') }}#rekomendasi"
|
||||||
|
class="block font-bold text-gray-700 hover:text-primary-600 py-2 border-b border-gray-50">Koleksi</a>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-3 mt-4">
|
||||||
|
@guest
|
||||||
|
<button onclick="openGuestModal()"
|
||||||
|
class="w-full px-5 py-3 bg-primary-50 text-primary-600 rounded-xl font-bold hover:bg-primary-100 transition-colors text-center flex items-center justify-center">
|
||||||
|
<i class="fas fa-book-reader mr-1.5"></i> Buku Tamu
|
||||||
|
</button>
|
||||||
|
<a href="{{ route('login') }}"
|
||||||
|
class="w-full px-5 py-3 bg-gradient-to-r from-primary-600 to-primary-700 text-white rounded-xl font-bold shadow-lg shadow-primary-200 text-center">
|
||||||
|
<i class="fas fa-sign-in-alt mr-1.5"></i> Login
|
||||||
|
</a>
|
||||||
|
@endguest
|
||||||
|
|
||||||
|
@auth
|
||||||
|
@if (auth()->user()->role === 'admin')
|
||||||
|
<a href="{{ route('admin.dashboard') }}"
|
||||||
|
class="w-full px-5 py-3 bg-gradient-to-r from-primary-600 to-primary-700 text-white rounded-xl font-bold shadow-lg text-center">
|
||||||
|
<i class="fas fa-tachometer-alt mr-1.5"></i> Dashboard Admin
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<a href="{{ route('user.dashboard') }}"
|
||||||
|
class="w-full px-5 py-3 bg-gradient-to-r from-green-600 to-green-700 text-white rounded-xl font-bold shadow-lg text-center">
|
||||||
|
<i class="fas fa-user mr-1.5"></i> Dashboard Anggota
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
@endauth
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{{-- CONTENT --}}
|
{{-- CONTENT --}}
|
||||||
|
|
@ -198,8 +247,7 @@ class="w-10 h-10 bg-gradient-to-br from-primary-500 to-primary-700 rounded-xl fl
|
||||||
Buku</a></li>
|
Buku</a></li>
|
||||||
<li><a href="{{ route('home') }}#fitur" class="hover:text-white transition-colors">Fitur</a>
|
<li><a href="{{ route('home') }}#fitur" class="hover:text-white transition-colors">Fitur</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="{{ route('home') }}#rekomendasi"
|
<li><a href="{{ route('home') }}#rekomendasi" class="hover:text-white transition-colors">Koleksi
|
||||||
class="hover:text-white transition-colors">Koleksi
|
|
||||||
Buku</a></li>
|
Buku</a></li>
|
||||||
<li><a href="{{ route('buku_tamu.index') }}" class="hover:text-white transition-colors">Buku
|
<li><a href="{{ route('buku_tamu.index') }}" class="hover:text-white transition-colors">Buku
|
||||||
Tamu</a></li>
|
Tamu</a></li>
|
||||||
|
|
@ -238,7 +286,93 @@ class="fab fa-twitter text-sm"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<!-- Global Guest Modal -->
|
||||||
|
<div id="guestModal"
|
||||||
|
class="fixed inset-0 bg-gray-900/60 backdrop-blur-sm hidden z-[100] flex items-center justify-center p-4 transition-opacity duration-300">
|
||||||
|
<div class="bg-white rounded-[2rem] shadow-2xl max-w-md w-full overflow-hidden transform transition-all">
|
||||||
|
<div class="p-8 text-center relative">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<button onclick="closeGuestModal()"
|
||||||
|
class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 transition-colors w-8 h-8 rounded-full hover:bg-gray-100 flex items-center justify-center">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Icon Box -->
|
||||||
|
<div
|
||||||
|
class="w-16 h-16 bg-gradient-to-br from-primary-500 to-primary-600 text-white rounded-2xl flex items-center justify-center mx-auto mb-5 text-2xl shadow-lg shadow-primary-200">
|
||||||
|
<i class="fas fa-user-check"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="text-2xl font-black text-gray-900 tracking-tight">Selamat Datang!</h3>
|
||||||
|
<p class="text-gray-500 text-sm mt-2 leading-relaxed">Apakah Anda sudah terdaftar sebagai anggota
|
||||||
|
Sarakata?</p>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-3 mt-8">
|
||||||
|
<!-- Member Option -->
|
||||||
|
<a href="{{ route('buku_tamu.index') }}"
|
||||||
|
class="flex items-center gap-4 p-4 border-2 border-primary-100 rounded-2xl hover:border-primary-300 hover:bg-primary-50/50 transition-all group cursor-pointer shadow-sm hover:shadow-md">
|
||||||
|
<div
|
||||||
|
class="w-12 h-12 bg-primary-100 rounded-xl flex items-center justify-center text-primary-600 group-hover:bg-gradient-to-br group-hover:from-primary-500 group-hover:to-primary-600 group-hover:text-white transition-all shadow-sm group-hover:shadow-primary-300">
|
||||||
|
<i class="fas fa-id-card"></i>
|
||||||
|
</div>
|
||||||
|
<div class="text-left flex-1 border-gray-100">
|
||||||
|
<div class="font-bold text-gray-900 text-sm">Saya Anggota</div>
|
||||||
|
<div class="text-xs text-gray-400">Punya No. Anggota</div>
|
||||||
|
</div>
|
||||||
|
<i
|
||||||
|
class="fas fa-chevron-right text-primary-300 group-hover:text-primary-500 transition-colors"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Visitor Option -->
|
||||||
|
<a href="{{ route('buku_tamu.index', ['tipe' => 'tamu']) }}"
|
||||||
|
class="flex items-center gap-4 p-4 border-2 border-gray-100 rounded-2xl hover:border-gray-300 hover:bg-gray-50 transition-all group cursor-pointer shadow-sm hover:shadow-md">
|
||||||
|
<div
|
||||||
|
class="w-12 h-12 bg-gray-100 rounded-xl flex items-center justify-center text-gray-500 group-hover:bg-gray-800 group-hover:text-white transition-all shadow-sm">
|
||||||
|
<i class="fas fa-user-friends"></i>
|
||||||
|
</div>
|
||||||
|
<div class="text-left flex-1 border-gray-100">
|
||||||
|
<div class="font-bold text-gray-900 text-sm">Pengunjung Umum</div>
|
||||||
|
<div class="text-xs text-gray-400">Belum punya member</div>
|
||||||
|
</div>
|
||||||
|
<i class="fas fa-chevron-right text-gray-300 group-hover:text-gray-600 transition-colors"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@stack('scripts')
|
@stack('scripts')
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const btn = document.getElementById('mobile-menu-btn');
|
||||||
|
const menu = document.getElementById('mobile-menu');
|
||||||
|
const icon = btn.querySelector('i');
|
||||||
|
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
menu.classList.toggle('hidden');
|
||||||
|
menu.classList.toggle('flex');
|
||||||
|
|
||||||
|
// Toggle icon
|
||||||
|
if (menu.classList.contains('flex')) {
|
||||||
|
icon.classList.remove('fa-bars');
|
||||||
|
icon.classList.add('fa-times');
|
||||||
|
} else {
|
||||||
|
icon.classList.remove('fa-times');
|
||||||
|
icon.classList.add('fa-bars');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Global Modal Scripts
|
||||||
|
function openGuestModal() {
|
||||||
|
document.getElementById('guestModal').classList.remove('hidden');
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
function closeGuestModal() {
|
||||||
|
document.getElementById('guestModal').classList.add('hidden');
|
||||||
|
document.body.style.overflow = 'auto';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||||
|
<span class="page-link" aria-hidden="true">‹</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@if (is_string($element))
|
||||||
|
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<li class="page-item active" aria-current="page"><span class="page-link">{{ $page }}</span></li>
|
||||||
|
@else
|
||||||
|
<li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">›</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||||
|
<span class="page-link" aria-hidden="true">›</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav class="d-flex justify-items-center justify-content-between">
|
||||||
|
<div class="d-flex justify-content-between flex-fill d-sm-none">
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">@lang('pagination.previous')</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">@lang('pagination.next')</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-none flex-sm-fill d-sm-flex align-items-sm-center justify-content-sm-between">
|
||||||
|
<div>
|
||||||
|
<p class="small text-muted">
|
||||||
|
{!! __('Showing') !!}
|
||||||
|
<span class="fw-semibold">{{ $paginator->firstItem() }}</span>
|
||||||
|
{!! __('to') !!}
|
||||||
|
<span class="fw-semibold">{{ $paginator->lastItem() }}</span>
|
||||||
|
{!! __('of') !!}
|
||||||
|
<span class="fw-semibold">{{ $paginator->total() }}</span>
|
||||||
|
{!! __('results') !!}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||||
|
<span class="page-link" aria-hidden="true">‹</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@if (is_string($element))
|
||||||
|
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<li class="page-item active" aria-current="page"><span class="page-link">{{ $page }}</span></li>
|
||||||
|
@else
|
||||||
|
<li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">›</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||||
|
<span class="page-link" aria-hidden="true">›</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||||
|
<span aria-hidden="true">‹</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li>
|
||||||
|
<a href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@if (is_string($element))
|
||||||
|
<li class="disabled" aria-disabled="true"><span>{{ $element }}</span></li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<li class="active" aria-current="page"><span>{{ $page }}</span></li>
|
||||||
|
@else
|
||||||
|
<li><a href="{{ $url }}">{{ $page }}</a></li>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li>
|
||||||
|
<a href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">›</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||||
|
<span aria-hidden="true">›</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<div class="ui pagination menu" role="navigation">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<a class="icon item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')"> <i class="left chevron icon"></i> </a>
|
||||||
|
@else
|
||||||
|
<a class="icon item" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')"> <i class="left chevron icon"></i> </a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@if (is_string($element))
|
||||||
|
<a class="icon item disabled" aria-disabled="true">{{ $element }}</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<a class="item active" href="{{ $url }}" aria-current="page">{{ $page }}</a>
|
||||||
|
@else
|
||||||
|
<a class="item" href="{{ $url }}">{{ $page }}</a>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<a class="icon item" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')"> <i class="right chevron icon"></i> </a>
|
||||||
|
@else
|
||||||
|
<a class="icon item disabled" aria-disabled="true" aria-label="@lang('pagination.next')"> <i class="right chevron icon"></i> </a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">@lang('pagination.previous')</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">@lang('pagination.next')</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav role="navigation" aria-label="{!! __('Pagination Navigation') !!}">
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">{!! __('pagination.previous') !!}</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">
|
||||||
|
{!! __('pagination.previous') !!}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">{!! __('pagination.next') !!}</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">{!! __('pagination.next') !!}</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="disabled" aria-disabled="true"><span>@lang('pagination.previous')</span></li>
|
||||||
|
@else
|
||||||
|
<li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a></li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li><a href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a></li>
|
||||||
|
@else
|
||||||
|
<li class="disabled" aria-disabled="true"><span>@lang('pagination.next')</span></li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav role="navigation" aria-label="{!! __('Pagination Navigation') !!}" class="flex justify-between">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md dark:text-gray-600 dark:bg-gray-800 dark:border-gray-600">
|
||||||
|
{!! __('pagination.previous') !!}
|
||||||
|
</span>
|
||||||
|
@else
|
||||||
|
<a href="{{ $paginator->previousPageUrl() }}" rel="prev" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
|
||||||
|
{!! __('pagination.previous') !!}
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<a href="{{ $paginator->nextPageUrl() }}" rel="next" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:focus:border-blue-700 dark:active:bg-gray-700 dark:active:text-gray-300">
|
||||||
|
{!! __('pagination.next') !!}
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md dark:text-gray-600 dark:bg-gray-800 dark:border-gray-600">
|
||||||
|
{!! __('pagination.next') !!}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav role="navigation" aria-label="{{ __('Pagination Navigation') }}" class="flex flex-col sm:flex-row items-center justify-between gap-4 mt-6">
|
||||||
|
{{-- Info text --}}
|
||||||
|
<div>
|
||||||
|
<p class="text-sm text-gray-500 leading-5">
|
||||||
|
Menampilkan
|
||||||
|
@if ($paginator->firstItem())
|
||||||
|
<span class="font-semibold text-gray-700">{{ $paginator->firstItem() }}</span>
|
||||||
|
sampai
|
||||||
|
<span class="font-semibold text-gray-700">{{ $paginator->lastItem() }}</span>
|
||||||
|
@else
|
||||||
|
{{ $paginator->count() }}
|
||||||
|
@endif
|
||||||
|
dari
|
||||||
|
<span class="font-semibold text-gray-700">{{ $paginator->total() }}</span>
|
||||||
|
data
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Pagination Buttons --}}
|
||||||
|
<div class="flex items-center gap-1.5">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<span class="inline-flex items-center justify-center w-9 h-9 rounded-lg text-gray-300 cursor-not-allowed" aria-disabled="true">
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
@else
|
||||||
|
<a href="{{ $paginator->previousPageUrl() }}" rel="prev"
|
||||||
|
class="inline-flex items-center justify-center w-9 h-9 rounded-lg text-gray-500 bg-white border border-gray-200 shadow-sm hover:bg-indigo-50 hover:text-indigo-600 hover:border-indigo-200 transition-all duration-200"
|
||||||
|
aria-label="{{ __('pagination.previous') }}">
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5L8.25 12l7.5-7.5"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@if (is_string($element))
|
||||||
|
<span class="inline-flex items-center justify-center w-9 h-9 text-sm text-gray-400 select-none" aria-disabled="true">
|
||||||
|
{{ $element }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<span aria-current="page"
|
||||||
|
class="inline-flex items-center justify-center w-9 h-9 rounded-lg text-sm font-bold text-white bg-gradient-to-br from-indigo-500 to-blue-600 shadow-md shadow-indigo-200 cursor-default transition-all duration-200">
|
||||||
|
{{ $page }}
|
||||||
|
</span>
|
||||||
|
@else
|
||||||
|
<a href="{{ $url }}"
|
||||||
|
class="inline-flex items-center justify-center w-9 h-9 rounded-lg text-sm font-medium text-gray-600 bg-white border border-gray-200 shadow-sm hover:bg-indigo-50 hover:text-indigo-600 hover:border-indigo-200 hover:shadow-md transition-all duration-200"
|
||||||
|
aria-label="{{ __('Go to page :page', ['page' => $page]) }}">
|
||||||
|
{{ $page }}
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<a href="{{ $paginator->nextPageUrl() }}" rel="next"
|
||||||
|
class="inline-flex items-center justify-center w-9 h-9 rounded-lg text-gray-500 bg-white border border-gray-200 shadow-sm hover:bg-indigo-50 hover:text-indigo-600 hover:border-indigo-200 transition-all duration-200"
|
||||||
|
aria-label="{{ __('pagination.next') }}">
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<span class="inline-flex items-center justify-center w-9 h-9 rounded-lg text-gray-300 cursor-not-allowed" aria-disabled="true">
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
@section('content')
|
@section('content')
|
||||||
<!-- Hero Section -->
|
<!-- Hero Section -->
|
||||||
<div
|
<div
|
||||||
class="relative bg-gradient-to-br from-blue-900 via-indigo-800 to-blue-900 py-16 sm:py-24 overflow-hidden shadow-2xl mb-12">
|
class="relative bg-gradient-to-br from-blue-900 via-indigo-800 to-blue-900 py-12 sm:py-24 overflow-hidden shadow-2xl mb-8 sm:mb-12">
|
||||||
<!-- Decorative background elements -->
|
<!-- Decorative background elements -->
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 opacity-10 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] mix-blend-overlay">
|
class="absolute inset-0 opacity-10 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] mix-blend-overlay">
|
||||||
|
|
@ -17,29 +17,31 @@ class="absolute bottom-0 left-0 -ml-20 -mb-20 w-72 h-72 rounded-full bg-indigo-3
|
||||||
|
|
||||||
<div class="container mx-auto px-4 relative z-10">
|
<div class="container mx-auto px-4 relative z-10">
|
||||||
<div class="text-center max-w-3xl mx-auto">
|
<div class="text-center max-w-3xl mx-auto">
|
||||||
<h1 class="text-4xl sm:text-5xl font-extrabold text-white mb-6 tracking-tight">
|
<h1
|
||||||
Eksplorasi <span class="text-transparent bg-clip-text bg-gradient-to-r from-blue-200 to-cyan-200">Katalog
|
class="text-3xl sm:text-4xl md:text-5xl font-extrabold text-white mb-4 sm:mb-6 tracking-tight leading-tight">
|
||||||
|
Eksplorasi <span
|
||||||
|
class="text-transparent bg-clip-text bg-gradient-to-r from-blue-200 to-cyan-200">Katalog
|
||||||
Perpustakaan</span>
|
Perpustakaan</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-blue-100 text-lg sm:text-xl mb-10 font-light">
|
<p class="text-blue-100 text-sm sm:text-lg md:text-xl mb-8 sm:mb-10 font-light px-2 sm:px-0">
|
||||||
Temukan ribuan koleksi buku, jurnal, dan referensi akademik untuk menginspirasi perjalanan literasi
|
Temukan ribuan koleksi buku, jurnal, dan referensi akademik untuk menginspirasi perjalanan literasi
|
||||||
Anda.
|
Anda.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Search Bar -->
|
<!-- Search Bar -->
|
||||||
<form action="{{ route('katalog.index') }}" method="GET" class="relative group max-w-2xl mx-auto">
|
<form action="{{ route('katalog.index') }}" method="GET"
|
||||||
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
|
class="relative group max-w-2xl mx-auto px-2 sm:px-0">
|
||||||
<svg class="h-6 w-6 text-gray-400 group-focus-within:text-blue-500 transition-colors"
|
<div class="absolute inset-y-0 left-2 sm:left-0 pl-4 flex items-center pointer-events-none">
|
||||||
|
<svg class="h-5 w-5 sm:h-6 sm:w-6 text-gray-400 group-focus-within:text-blue-500 transition-colors"
|
||||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" name="search" value="{{ $search }}"
|
<input type="text" name="search" value="{{ $search }}" placeholder="Cari judul buku, pengarang..."
|
||||||
placeholder="Cari judul buku, pengarang, atau penerbit..."
|
class="block w-full pl-10 pr-24 sm:pl-12 sm:pr-32 py-3.5 sm:py-4 bg-white/95 backdrop-blur-sm border-0 rounded-xl sm:rounded-2xl shadow-xl focus:ring-4 focus:ring-blue-500/30 text-gray-800 text-sm sm:text-lg transition-all placeholder-gray-400">
|
||||||
class="block w-full pl-12 pr-32 py-4 bg-white/95 backdrop-blur-sm border-0 rounded-2xl shadow-xl focus:ring-4 focus:ring-blue-500/30 text-gray-800 text-lg transition-all placeholder-gray-400">
|
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
class="absolute right-2 top-2 bottom-2 bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white font-semibold px-6 lg:px-8 rounded-xl shadow-md transition-all transform hover:scale-[1.02] active:scale-[0.98]">
|
class="absolute right-3.5 sm:right-2 top-1.5 bottom-1.5 sm:top-2 sm:bottom-2 bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white font-semibold px-5 sm:px-6 lg:px-8 rounded-lg sm:rounded-xl shadow-md transition-all transform hover:scale-[1.02] active:scale-[0.98] text-sm sm:text-base">
|
||||||
Cari
|
Cari
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -65,19 +67,14 @@ class="absolute right-2 top-2 bottom-2 bg-gradient-to-r from-blue-600 to-indigo-
|
||||||
class="group bg-white rounded-2xl shadow-sm hover:shadow-2xl border border-gray-100 overflow-hidden transition-all duration-300 transform {{ $habis ? 'grayscale opacity-75 cursor-not-allowed' : 'hover:-translate-y-1' }} flex flex-col h-full">
|
class="group bg-white rounded-2xl shadow-sm hover:shadow-2xl border border-gray-100 overflow-hidden transition-all duration-300 transform {{ $habis ? 'grayscale opacity-75 cursor-not-allowed' : 'hover:-translate-y-1' }} flex flex-col h-full">
|
||||||
<!-- Book Cover -->
|
<!-- Book Cover -->
|
||||||
<div class="relative w-full pt-[140%] overflow-hidden bg-gray-50 flex-shrink-0">
|
<div class="relative w-full pt-[140%] overflow-hidden bg-gray-50 flex-shrink-0">
|
||||||
@if ($item->cover)
|
@php
|
||||||
<img src="{{ asset('storage/' . $item->cover) }}" alt="{{ $item->judul }}"
|
$coverPath = $item->cover ?? $item->sampul;
|
||||||
|
$finalPath = $coverPath ? (Str::contains($coverPath, '/') ? $coverPath : 'covers/' . $coverPath) : null;
|
||||||
|
@endphp
|
||||||
|
@if ($finalPath)
|
||||||
|
<img src="{{ asset('storage/' . $finalPath) }}" alt="{{ $item->judul }}"
|
||||||
class="absolute inset-0 w-full h-full object-cover {{ !$habis ? 'group-hover:scale-110' : '' }} transition-transform duration-700 ease-in-out"
|
class="absolute inset-0 w-full h-full object-cover {{ !$habis ? 'group-hover:scale-110' : '' }} transition-transform duration-700 ease-in-out"
|
||||||
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
|
onerror="this.src='https://ui-avatars.com/api/?name={{ urlencode($item->judul) }}&background=F3F4F6&color=6366F1&size=512&bold=true';">
|
||||||
<div
|
|
||||||
class="hidden absolute inset-0 flex flex-col items-center justify-center text-gray-300 bg-gray-100 {{ !$habis ? 'group-hover:bg-gray-200' : '' }} transition-colors duration-300">
|
|
||||||
<svg class="w-12 h-12 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
|
||||||
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253">
|
|
||||||
</path>
|
|
||||||
</svg>
|
|
||||||
<span class="text-xs font-medium">Cover Tidak Tersedia</span>
|
|
||||||
</div>
|
|
||||||
@else
|
@else
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 flex flex-col items-center justify-center text-gray-300 bg-gray-100 {{ !$habis ? 'group-hover:bg-gray-200' : '' }} transition-colors duration-300">
|
class="absolute inset-0 flex flex-col items-center justify-center text-gray-300 bg-gray-100 {{ !$habis ? 'group-hover:bg-gray-200' : '' }} transition-colors duration-300">
|
||||||
|
|
@ -92,8 +89,7 @@ class="absolute inset-0 flex flex-col items-center justify-center text-gray-300
|
||||||
|
|
||||||
@if ($habis)
|
@if ($habis)
|
||||||
<!-- Overlay Dipinjam Semua -->
|
<!-- Overlay Dipinjam Semua -->
|
||||||
<div
|
<div class="absolute inset-0 bg-red-900/30 backdrop-blur-[1px] flex items-center justify-center z-10">
|
||||||
class="absolute inset-0 bg-red-900/30 backdrop-blur-[1px] flex items-center justify-center z-10">
|
|
||||||
<div
|
<div
|
||||||
class="bg-red-600/90 text-white font-black text-sm md:text-base px-6 py-2 border-y-2 border-white transform -rotate-12 shadow-2xl tracking-widest uppercase pointer-events-none">
|
class="bg-red-600/90 text-white font-black text-sm md:text-base px-6 py-2 border-y-2 border-white transform -rotate-12 shadow-2xl tracking-widest uppercase pointer-events-none">
|
||||||
TIDAK TERSEDIA
|
TIDAK TERSEDIA
|
||||||
|
|
@ -229,4 +225,4 @@ class="inline-flex items-center gap-1.5 bg-gradient-to-r from-blue-50 to-indigo-
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
@ -202,9 +202,14 @@ class="absolute right-1 top-1 bottom-1 w-10 flex items-center justify-center bg-
|
||||||
<div class="md:col-span-12 lg:col-span-3">
|
<div class="md:col-span-12 lg:col-span-3">
|
||||||
<div
|
<div
|
||||||
class="bg-white rounded-3xl shadow-[0_10px_40px_-10px_rgba(0,0,0,0.08)] border border-gray-50 p-5 flex flex-col items-center text-center overflow-hidden transition-transform duration-500 hover:-translate-y-2">
|
class="bg-white rounded-3xl shadow-[0_10px_40px_-10px_rgba(0,0,0,0.08)] border border-gray-50 p-5 flex flex-col items-center text-center overflow-hidden transition-transform duration-500 hover:-translate-y-2">
|
||||||
@if ($buku->cover)
|
@php
|
||||||
<img src="{{ asset('storage/' . $buku->cover) }}" alt="{{ $buku->judul }}"
|
$coverPath = $buku->cover ?? $buku->sampul;
|
||||||
class="w-full aspect-[3/4] object-cover rounded-2xl mb-6 shadow-md border border-gray-100">
|
$finalPath = $coverPath ? (Str::contains($coverPath, '/') ? $coverPath : 'covers/' . $coverPath) : null;
|
||||||
|
@endphp
|
||||||
|
@if ($finalPath)
|
||||||
|
<img src="{{ asset('storage/' . $finalPath) }}" alt="{{ $buku->judul }}"
|
||||||
|
class="w-full aspect-[3/4] object-cover rounded-2xl mb-6 shadow-md border border-gray-100"
|
||||||
|
onerror="this.src='https://ui-avatars.com/api/?name={{ urlencode($buku->judul) }}&background=F3F4F6&color=6366F1&size=512&bold=true';">
|
||||||
@else
|
@else
|
||||||
<div
|
<div
|
||||||
class="w-full aspect-[3/4] bg-gradient-to-br from-indigo-50 to-blue-50 rounded-2xl flex items-center justify-center mb-6 overflow-hidden relative shadow-inner border border-blue-100/50">
|
class="w-full aspect-[3/4] bg-gradient-to-br from-indigo-50 to-blue-50 rounded-2xl flex items-center justify-center mb-6 overflow-hidden relative shadow-inner border border-blue-100/50">
|
||||||
|
|
@ -279,21 +284,32 @@ class="text-blue-600 font-bold text-sm">{{ $buku->kategori->nama_kategori ?? $lo
|
||||||
|
|
||||||
<div class="w-full lg:w-96 flex-shrink-0 flex flex-col gap-6 mx-auto relative">
|
<div class="w-full lg:w-96 flex-shrink-0 flex flex-col gap-6 mx-auto relative">
|
||||||
|
|
||||||
<!-- MAP CONTAINER -->
|
<!-- MAP CONTAINER RESPONSIVE -->
|
||||||
<div class="map-container shadow-sm border-2 border-slate-300 select-none font-sans min-h-[300px]"
|
<div class="relative inline-block w-full max-w-4xl shadow-sm border-2 border-slate-300 select-none font-sans overflow-hidden rounded-lg">
|
||||||
id="mapContainer">
|
|
||||||
<!-- Background Image Map -->
|
<!-- Background Image Map -->
|
||||||
<img src="{{ asset('img/img 2.png') }}" alt="Denah Perpustakaan" id="mapImage">
|
<img src="{{ asset('img/denah.webp') }}" loading="lazy" alt="Denah Perpustakaan" class="w-full h-auto block">
|
||||||
|
|
||||||
<!-- Pin Container (JavaScript akan merender pin di sini) -->
|
@if ($buku->lokasi_x && $buku->lokasi_y)
|
||||||
<div id="pinContainer"></div>
|
<!-- Pin Marker (Render via Blade) -->
|
||||||
|
<div class="absolute -translate-x-1/2 -translate-y-full flex flex-col items-center"
|
||||||
|
style="top: {{ $buku->lokasi_y }}%; left: {{ $buku->lokasi_x }}%; margin-top: 4px;">
|
||||||
|
|
||||||
|
<!-- Tooltip / Label -->
|
||||||
|
<div class="bg-blue-700 text-white text-[10px] sm:text-xs font-bold px-2 py-1 rounded shadow-md mb-1 whitespace-nowrap">
|
||||||
|
{{ $lokasi['rak'] }} — {{ $lokasi['area'] }}
|
||||||
|
</div>
|
||||||
|
|
||||||
@if (!$buku->lokasi_x || !$buku->lokasi_y)
|
<!-- Ikon Pin Merah -->
|
||||||
|
<div class="relative flex flex-col items-center animate-bounce">
|
||||||
|
<i class="fas fa-map-marker-alt text-red-600 text-3xl sm:text-4xl drop-shadow-md"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
<!-- Overlay Not Found -->
|
<!-- Overlay Not Found -->
|
||||||
<div class="map-overlay-notfound">
|
<div class="absolute inset-0 flex flex-col items-center justify-center bg-white/70 backdrop-blur-sm z-10 text-center p-4">
|
||||||
<i class="fas fa-exclamation-triangle text-red-500 text-3xl mb-2 animate-pulse"></i>
|
<i class="fas fa-exclamation-triangle text-red-500 text-3xl sm:text-4xl mb-2 animate-pulse"></i>
|
||||||
<h4 class="font-bold text-red-600 text-sm sm:text-base">Lokasi Rak Belum Dipetakan</h4>
|
<h4 class="font-bold text-red-600 text-sm sm:text-lg">Lokasi Rak Belum Dipetakan</h4>
|
||||||
<p class="text-xs text-gray-500 mt-1">Silakan hubungi petugas perpustakaan untuk bantuan.</p>
|
<p class="text-xs sm:text-sm text-gray-700 mt-1">Silakan hubungi petugas perpustakaan untuk bantuan.</p>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -419,69 +435,5 @@ class="text-sm font-semibold text-blue-600 hover:text-blue-800 flex items-center
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
@php
|
<!-- Skrip lama pembuat pin JS sudah dihapus dan digantikan full Tailwind CSS di HTML -->
|
||||||
$pinDataJson = [
|
|
||||||
'x' => $buku->lokasi_x,
|
|
||||||
'y' => $buku->lokasi_y,
|
|
||||||
'label' => $lokasi['rak'] . ' — ' . $lokasi['area'],
|
|
||||||
];
|
|
||||||
@endphp
|
|
||||||
<script>
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
// Data koordinat dari Laravel
|
|
||||||
const pinData = @json($pinDataJson);
|
|
||||||
|
|
||||||
const container = document.getElementById('pinContainer');
|
|
||||||
if (!container || !pinData.x || !pinData.y) return;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Membuat elemen pin pointer dan menambahkannya ke container.
|
|
||||||
* @param {number} x - Posisi X dalam persentase (0-100)
|
|
||||||
* @param {number} y - Posisi Y dalam persentase (0-100)
|
|
||||||
* @param {string} label - Label teks untuk pin
|
|
||||||
* @param {boolean} isActive - Apakah pin ini aktif (bounce + label visible)
|
|
||||||
*/
|
|
||||||
function renderPin(x, y, label, isActive) {
|
|
||||||
// Wrapper pin
|
|
||||||
const pin = document.createElement('div');
|
|
||||||
pin.className = 'map-pin' + (isActive ? ' active' : '');
|
|
||||||
pin.style.left = x + '%';
|
|
||||||
pin.style.top = y + '%';
|
|
||||||
|
|
||||||
// Ikon pin (circle + tail)
|
|
||||||
const icon = document.createElement('div');
|
|
||||||
icon.className = 'map-pin__icon';
|
|
||||||
|
|
||||||
const circle = document.createElement('div');
|
|
||||||
circle.className = 'map-pin__circle';
|
|
||||||
circle.innerHTML = '<i class="fas fa-map-marker-alt"></i>';
|
|
||||||
|
|
||||||
const tail = document.createElement('div');
|
|
||||||
tail.className = 'map-pin__tail';
|
|
||||||
|
|
||||||
icon.appendChild(circle);
|
|
||||||
icon.appendChild(tail);
|
|
||||||
pin.appendChild(icon);
|
|
||||||
|
|
||||||
// Pulse effect
|
|
||||||
const pulse = document.createElement('div');
|
|
||||||
pulse.className = 'map-pin__pulse';
|
|
||||||
pin.appendChild(pulse);
|
|
||||||
|
|
||||||
// Label tooltip
|
|
||||||
if (label) {
|
|
||||||
const labelEl = document.createElement('span');
|
|
||||||
labelEl.className = 'map-pin__label';
|
|
||||||
labelEl.textContent = label;
|
|
||||||
pin.appendChild(labelEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
container.appendChild(pin);
|
|
||||||
return pin;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render pin utama buku ini
|
|
||||||
renderPin(pinData.x, pinData.y, pinData.label, true);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@endpush
|
@endpush
|
||||||
|
|
|
||||||
|
|
@ -11,61 +11,4 @@
|
||||||
@include('welcome.rekomendasi')
|
@include('welcome.rekomendasi')
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div id="guestModal" class="fixed inset-0 bg-gray-900/60 backdrop-blur-sm hidden z-[100] flex items-center justify-center p-4 transition-opacity duration-300">
|
@endsection
|
||||||
<div class="bg-white rounded-[2rem] shadow-2xl max-w-md w-full overflow-hidden transform transition-all">
|
|
||||||
<div class="p-8 text-center relative">
|
|
||||||
<!-- Close Button -->
|
|
||||||
<button onclick="closeGuestModal()" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 transition-colors w-8 h-8 rounded-full hover:bg-gray-100 flex items-center justify-center">
|
|
||||||
<i class="fas fa-times"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Icon Box -->
|
|
||||||
<div class="w-16 h-16 bg-gradient-to-br from-primary-500 to-primary-600 text-white rounded-2xl flex items-center justify-center mx-auto mb-5 text-2xl shadow-lg shadow-primary-200">
|
|
||||||
<i class="fas fa-user-check"></i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 class="text-2xl font-black text-gray-900 tracking-tight">Selamat Datang!</h3>
|
|
||||||
<p class="text-gray-500 text-sm mt-2 leading-relaxed">Apakah Anda sudah terdaftar sebagai anggota Sarakata?</p>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-3 mt-8">
|
|
||||||
<!-- Member Option -->
|
|
||||||
<a href="{{ route('buku_tamu.index') }}" class="flex items-center gap-4 p-4 border-2 border-primary-100 rounded-2xl hover:border-primary-300 hover:bg-primary-50/50 transition-all group cursor-pointer shadow-sm hover:shadow-md">
|
|
||||||
<div class="w-12 h-12 bg-primary-100 rounded-xl flex items-center justify-center text-primary-600 group-hover:bg-gradient-to-br group-hover:from-primary-500 group-hover:to-primary-600 group-hover:text-white transition-all shadow-sm group-hover:shadow-primary-300">
|
|
||||||
<i class="fas fa-id-card"></i>
|
|
||||||
</div>
|
|
||||||
<div class="text-left flex-1 border-gray-100">
|
|
||||||
<div class="font-bold text-gray-900 text-sm">Saya Anggota</div>
|
|
||||||
<div class="text-xs text-gray-400">Punya No. Anggota</div>
|
|
||||||
</div>
|
|
||||||
<i class="fas fa-chevron-right text-primary-300 group-hover:text-primary-500 transition-colors"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- Visitor Option -->
|
|
||||||
<a href="{{ route('buku_tamu.index') }}" class="flex items-center gap-4 p-4 border-2 border-gray-100 rounded-2xl hover:border-gray-300 hover:bg-gray-50 transition-all group cursor-pointer shadow-sm hover:shadow-md">
|
|
||||||
<div class="w-12 h-12 bg-gray-100 rounded-xl flex items-center justify-center text-gray-500 group-hover:bg-gray-800 group-hover:text-white transition-all shadow-sm">
|
|
||||||
<i class="fas fa-user-friends"></i>
|
|
||||||
</div>
|
|
||||||
<div class="text-left flex-1 border-gray-100">
|
|
||||||
<div class="font-bold text-gray-900 text-sm">Pengunjung Umum</div>
|
|
||||||
<div class="text-xs text-gray-400">Belum punya member</div>
|
|
||||||
</div>
|
|
||||||
<i class="fas fa-chevron-right text-gray-300 group-hover:text-gray-600 transition-colors"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endsection
|
|
||||||
|
|
||||||
@push('scripts')
|
|
||||||
<script>
|
|
||||||
function openGuestModal() {
|
|
||||||
document.getElementById('guestModal').classList.remove('hidden');
|
|
||||||
document.body.style.overflow = 'hidden';
|
|
||||||
}
|
|
||||||
function closeGuestModal() {
|
|
||||||
document.getElementById('guestModal').classList.add('hidden');
|
|
||||||
document.body.style.overflow = 'auto';
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@endpush
|
|
||||||
|
|
@ -5,58 +5,128 @@
|
||||||
<div class="absolute bottom-10 right-10 w-96 h-96 bg-purple-200/20 blob blur-3xl"></div>
|
<div class="absolute bottom-10 right-10 w-96 h-96 bg-purple-200/20 blob blur-3xl"></div>
|
||||||
<div class="absolute top-40 right-1/3 w-48 h-48 bg-blue-200/20 rounded-full blur-2xl"></div>
|
<div class="absolute top-40 right-1/3 w-48 h-48 bg-blue-200/20 rounded-full blur-2xl"></div>
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto px-6 py-24 md:py-32 grid md:grid-cols-2 gap-16 items-center relative z-10">
|
<div
|
||||||
|
class="max-w-7xl mx-auto px-4 sm:px-6 py-14 sm:py-20 md:py-26
|
||||||
|
grid md:grid-cols-2 gap-10 md:gap-16 items-center relative z-10">
|
||||||
|
|
||||||
<div class="fade-up">
|
<div class="fade-up">
|
||||||
{{-- Badge --}}
|
{{-- Badge --}}
|
||||||
<div class="inline-flex items-center gap-2 px-4 py-2 bg-white/80 rounded-full shadow-sm border border-primary-100 mb-8">
|
<div
|
||||||
|
class="inline-flex items-center gap-2 px-3 py-2 bg-white/80
|
||||||
|
rounded-full shadow-sm border border-primary-100 mb-6">
|
||||||
<span class="w-2 h-2 bg-green-400 rounded-full animate-pulse"></span>
|
<span class="w-2 h-2 bg-green-400 rounded-full animate-pulse"></span>
|
||||||
<span class="text-xs font-semibold text-primary-700 tracking-wide">Sistem Informasi Perpustakaan</span>
|
|
||||||
|
<span class="text-[11px] sm:text-xs font-semibold text-primary-700 tracking-wide">
|
||||||
|
Sistem Informasi Perpustakaan
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 class="text-4xl md:text-5xl lg:text-6xl font-black leading-[1.1] text-gray-900 tracking-tight">
|
<h1
|
||||||
|
class="text-3xl sm:text-4xl md:text-5xl lg:text-6xl
|
||||||
|
font-black leading-tight text-gray-900 tracking-tight">
|
||||||
Sistem Informasi Perpustakaan
|
Sistem Informasi Perpustakaan
|
||||||
<span class="gradient-text block mt-2">Daerah Jember</span>
|
|
||||||
|
<span class="gradient-text block mt-1 sm:mt-2">
|
||||||
|
Daerah Jember
|
||||||
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p class="mt-6 text-lg text-gray-500 max-w-lg leading-relaxed">
|
<p
|
||||||
Akses ribuan koleksi buku, jurnal, dan referensi akademik dalam satu platform terintegrasi — kapan saja, di mana saja.
|
class="mt-5 text-sm sm:text-base md:text-lg text-gray-500
|
||||||
|
max-w-lg leading-relaxed">
|
||||||
|
Akses ribuan koleksi buku, jurnal, dan referensi akademik
|
||||||
|
dalam satu platform terintegrasi — kapan saja, di mana saja.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="mt-10 flex flex-col sm:flex-row gap-4 flex-wrap">
|
{{-- BUTTONS --}}
|
||||||
<a href="{{ route('katalog.index') }}" class="px-8 py-4 bg-gradient-to-r from-blue-600 to-blue-700 text-white rounded-2xl font-bold text-sm shadow-xl shadow-blue-200 hover:shadow-blue-300 hover:from-blue-700 hover:to-blue-800 transition-all inline-flex items-center gap-2 justify-center">
|
<div class="mt-8 flex flex-col gap-3 sm:flex-row sm:flex-wrap">
|
||||||
<i class="fas fa-search"></i> Cari Katalog Buku
|
|
||||||
|
<a href="{{ route('katalog.index') }}"
|
||||||
|
class="w-full sm:w-auto px-6 py-3.5
|
||||||
|
bg-gradient-to-r from-blue-600 to-blue-700
|
||||||
|
text-white rounded-xl sm:rounded-2xl
|
||||||
|
font-bold text-sm shadow-xl shadow-blue-200
|
||||||
|
hover:from-blue-700 hover:to-blue-800
|
||||||
|
transition-all inline-flex items-center
|
||||||
|
gap-2 justify-center">
|
||||||
|
|
||||||
|
<i class="fas fa-search"></i>
|
||||||
|
Cari Katalog Buku
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ route('buku_tamu.index') }}" class="px-8 py-4 bg-gradient-to-r from-primary-600 to-primary-700 text-white rounded-2xl font-bold text-sm shadow-xl shadow-primary-200 hover:shadow-primary-300 hover:from-primary-700 hover:to-primary-800 transition-all inline-flex items-center gap-2 justify-center">
|
|
||||||
<i class="fas fa-book-reader"></i> Mulai Kunjungan
|
<a href="{{ route('buku_tamu.index') }}"
|
||||||
|
class="w-full sm:w-auto px-6 py-3.5
|
||||||
|
bg-gradient-to-r from-primary-600 to-primary-700
|
||||||
|
text-white rounded-xl sm:rounded-2xl
|
||||||
|
font-bold text-sm shadow-xl shadow-primary-200
|
||||||
|
hover:from-primary-700 hover:to-primary-800
|
||||||
|
transition-all inline-flex items-center
|
||||||
|
gap-2 justify-center">
|
||||||
|
|
||||||
|
<i class="fas fa-book-reader"></i>
|
||||||
|
Mulai Kunjungan
|
||||||
</a>
|
</a>
|
||||||
<a href="#fitur" class="px-8 py-4 bg-white text-gray-700 rounded-2xl font-bold text-sm shadow-sm border border-gray-200 hover:border-primary-200 hover:text-primary-600 transition-all inline-flex items-center gap-2 justify-center">
|
|
||||||
<i class="fas fa-info-circle"></i> Pelajari Lebih Lanjut
|
<a href="#fitur"
|
||||||
|
class="w-full sm:w-auto px-6 py-3.5
|
||||||
|
bg-white text-gray-700 rounded-xl sm:rounded-2xl
|
||||||
|
font-bold text-sm shadow-sm border border-gray-200
|
||||||
|
hover:border-primary-200 hover:text-primary-600
|
||||||
|
transition-all inline-flex items-center
|
||||||
|
gap-2 justify-center">
|
||||||
|
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
Pelajari Lebih Lanjut
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Stats --}}
|
{{-- STATS --}}
|
||||||
<div class="mt-14 flex gap-10">
|
<div class="mt-10 grid grid-cols-3 gap-3 sm:gap-6">
|
||||||
<div>
|
|
||||||
<div class="text-3xl font-black text-gray-900">1000+</div>
|
<div class="bg-white/50 rounded-xl p-3 sm:p-4 text-center">
|
||||||
<div class="text-xs text-gray-400 font-medium mt-1">Koleksi Buku</div>
|
<div class="text-xl sm:text-3xl font-black text-gray-900">
|
||||||
|
1000+
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-[10px] sm:text-xs text-gray-400 font-medium mt-1">
|
||||||
|
Koleksi Buku
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-l border-gray-200 pl-10">
|
|
||||||
<div class="text-3xl font-black text-gray-900">500+</div>
|
<div class="bg-white/50 rounded-xl p-3 sm:p-4 text-center">
|
||||||
<div class="text-xs text-gray-400 font-medium mt-1">Anggota Aktif</div>
|
<div class="text-xl sm:text-3xl font-black text-gray-900">
|
||||||
|
500+
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-[10px] sm:text-xs text-gray-400 font-medium mt-1">
|
||||||
|
Anggota Aktif
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-l border-gray-200 pl-10">
|
|
||||||
<div class="text-3xl font-black text-gray-900">24/7</div>
|
<div class="bg-white/50 rounded-xl p-3 sm:p-4 text-center">
|
||||||
<div class="text-xs text-gray-400 font-medium mt-1">Akses Digital</div>
|
<div class="text-xl sm:text-3xl font-black text-gray-900">
|
||||||
|
24/7
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-[10px] sm:text-xs text-gray-400 font-medium mt-1">
|
||||||
|
Akses Digital
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{-- IMAGE --}}
|
||||||
<div class="hidden md:flex justify-center relative items-center">
|
<div class="hidden md:flex justify-center relative items-center">
|
||||||
<div class="absolute inset-0 bg-gradient-to-tr from-primary-400/10 to-purple-400/10 rounded-[3rem] blur-2xl"></div>
|
<div
|
||||||
|
class="absolute inset-0 bg-gradient-to-tr
|
||||||
|
from-primary-400/10 to-purple-400/10
|
||||||
|
rounded-[3rem] blur-2xl">
|
||||||
|
</div>
|
||||||
|
|
||||||
<img src="{{ asset('img/buku sampul awal.png') }}"
|
<img src="{{ asset('img/buku sampul awal.png') }}"
|
||||||
class="w-[600px] object-contain drop-shadow-2xl relative z-10 rounded-3xl"
|
class="w-[500px] lg:w-[600px] object-contain
|
||||||
alt="Animasi Perpustakaan Digital">
|
drop-shadow-2xl relative z-10 rounded-3xl"
|
||||||
|
alt="Animasi Perpustakaan Digital">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@
|
||||||
<div
|
<div
|
||||||
class="flex flex-col md:flex-row justify-between items-start md:items-center mb-10 gap-4 text-center md:text-left">
|
class="flex flex-col md:flex-row justify-between items-start md:items-center mb-10 gap-4 text-center md:text-left">
|
||||||
<div class="w-full md:w-auto">
|
<div class="w-full md:w-auto">
|
||||||
<div
|
<div class="inline-flex items-center gap-2 px-4 py-2 bg-blue-100 rounded-full mb-4 mx-auto md:mx-0">
|
||||||
class="inline-flex items-center gap-2 px-4 py-2 bg-blue-100 rounded-full mb-4 mx-auto md:mx-0">
|
|
||||||
<i class="fas fa-fire text-red-500 text-xs"></i>
|
<i class="fas fa-fire text-red-500 text-xs"></i>
|
||||||
<span class="text-xs font-semibold text-blue-800 tracking-wide">Paling Laris</span>
|
<span class="text-xs font-semibold text-blue-800 tracking-wide">Paling Laris</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -39,15 +38,18 @@ class="bg-blue-600/90 backdrop-blur-md text-white text-[10px] sm:text-xs font-bo
|
||||||
|
|
||||||
<!-- Book Cover -->
|
<!-- Book Cover -->
|
||||||
<div class="relative w-full pt-[140%] overflow-hidden bg-gray-50 flex-shrink-0">
|
<div class="relative w-full pt-[140%] overflow-hidden bg-gray-50 flex-shrink-0">
|
||||||
@if ($item->cover ?? $item->sampul)
|
@php
|
||||||
<img src="{{ asset('storage/' . ($item->cover ?? $item->sampul)) }}"
|
$coverPath = $item->cover ?? $item->sampul;
|
||||||
alt="{{ $item->judul }}"
|
$finalPath = $coverPath ? (Str::contains($coverPath, '/') ? $coverPath : 'covers/' . $coverPath) : null;
|
||||||
class="absolute inset-0 w-full h-full object-cover {{ !$habis ? 'group-hover:scale-110' : '' }} transition-transform duration-700 ease-in-out">
|
@endphp
|
||||||
|
@if ($finalPath)
|
||||||
|
<img src="{{ asset('storage/' . $finalPath) }}" alt="{{ $item->judul }}"
|
||||||
|
class="absolute inset-0 w-full h-full object-cover {{ !$habis ? 'group-hover:scale-110' : '' }} transition-transform duration-700 ease-in-out"
|
||||||
|
onerror="this.src='https://ui-avatars.com/api/?name={{ urlencode($item->judul) }}&background=F3F4F6&color=6366F1&size=512&bold=true';">
|
||||||
@else
|
@else
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 flex flex-col items-center justify-center text-gray-300 bg-gray-100 {{ !$habis ? 'group-hover:bg-gray-200' : '' }} transition-colors duration-300">
|
class="absolute inset-0 flex flex-col items-center justify-center text-gray-300 bg-gray-100 {{ !$habis ? 'group-hover:bg-gray-200' : '' }} transition-colors duration-300">
|
||||||
<svg class="w-12 h-12 mb-2" fill="none" stroke="currentColor"
|
<svg class="w-12 h-12 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
||||||
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253">
|
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253">
|
||||||
</path>
|
</path>
|
||||||
|
|
@ -113,4 +115,4 @@ class="block w-full text-center bg-gray-50 hover:bg-blue-600 text-gray-700 hover
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -42,15 +42,19 @@ class="bg-gradient-to-r from-yellow-400 to-orange-500 text-white text-[9px] sm:t
|
||||||
|
|
||||||
<!-- Book Cover -->
|
<!-- Book Cover -->
|
||||||
<div class="relative w-full pt-[140%] overflow-hidden bg-gray-50 flex-shrink-0">
|
<div class="relative w-full pt-[140%] overflow-hidden bg-gray-50 flex-shrink-0">
|
||||||
@if ($item->cover ?? $item->sampul)
|
@php
|
||||||
<img src="{{ asset('storage/' . ($item->cover ?? $item->sampul)) }}"
|
$coverPath = $item->cover ?? $item->sampul;
|
||||||
alt="{{ $item->judul }}"
|
// Cek apakah path sudah mengandung folder, jika tidak default ke 'covers/'
|
||||||
class="absolute inset-0 w-full h-full object-cover {{ !$habis ? 'group-hover:scale-110' : '' }} transition-transform duration-700 ease-in-out">
|
$finalPath = $coverPath ? (Str::contains($coverPath, '/') ? $coverPath : 'covers/' . $coverPath) : null;
|
||||||
|
@endphp
|
||||||
|
@if ($finalPath)
|
||||||
|
<img src="{{ asset('storage/' . $finalPath) }}" alt="{{ $item->judul }}"
|
||||||
|
class="absolute inset-0 w-full h-full object-cover {{ !$habis ? 'group-hover:scale-110' : '' }} transition-transform duration-700 ease-in-out"
|
||||||
|
onerror="this.src='https://ui-avatars.com/api/?name={{ urlencode($item->judul) }}&background=EBF4FF&color=7F9CF5&size=512&bold=true';">
|
||||||
@else
|
@else
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 flex flex-col items-center justify-center text-gray-300 bg-gray-100 {{ !$habis ? 'group-hover:bg-gray-200' : '' }} transition-colors duration-300">
|
class="absolute inset-0 flex flex-col items-center justify-center text-gray-300 bg-gray-100 {{ !$habis ? 'group-hover:bg-gray-200' : '' }} transition-colors duration-300">
|
||||||
<svg class="w-12 h-12 mb-2" fill="none" stroke="currentColor"
|
<svg class="w-12 h-12 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
||||||
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253">
|
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253">
|
||||||
</path>
|
</path>
|
||||||
|
|
@ -124,4 +128,4 @@ class="block w-full text-center bg-gray-50 hover:bg-blue-600 text-gray-700 hover
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -129,4 +129,5 @@
|
||||||
Route::get('/admin/buku/{id}/edit', [AdminBukuController::class, 'edit'])->name('admin.buku.edit');
|
Route::get('/admin/buku/{id}/edit', [AdminBukuController::class, 'edit'])->name('admin.buku.edit');
|
||||||
Route::put('/admin/buku/{id}', [AdminBukuController::class, 'update'])->name('admin.buku.update');
|
Route::put('/admin/buku/{id}', [AdminBukuController::class, 'update'])->name('admin.buku.update');
|
||||||
Route::delete('/admin/buku/{id}', [AdminBukuController::class, 'destroy'])->name('admin.buku.destroy');
|
Route::delete('/admin/buku/{id}', [AdminBukuController::class, 'destroy'])->name('admin.buku.destroy');
|
||||||
Route::get('/admin/peminjaman/{id}/struk', [AdminPeminjamanController::class, 'cetakStruk'])->name('admin.peminjaman.struk');
|
Route::get('/admin/peminjaman/{id}/struk', [AdminPeminjamanController::class, 'cetakStruk'])->name('admin.peminjaman.struk');
|
||||||
|
Route::post('/admin/peminjaman/{id}/resend-wa', [AdminPeminjamanController::class, 'resendWa'])->name('admin.peminjaman.resend_wa');
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
require __DIR__.'/vendor/autoload.php';
|
||||||
|
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||||
|
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||||
|
$kernel->bootstrap();
|
||||||
|
|
||||||
|
$bukus = App\Models\Buku::all();
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
foreach ($bukus as $buku) {
|
||||||
|
if (empty($buku->nomor_panggil)) continue;
|
||||||
|
|
||||||
|
$kode_utama = (int) substr(trim(preg_replace('/[^0-9]/', '', $buku->nomor_panggil)), 0, 3);
|
||||||
|
|
||||||
|
$koordinat = match (true) {
|
||||||
|
$kode_utama >= 0 && $kode_utama <= 99 => match (true) {
|
||||||
|
$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' => 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' => 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' => 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' => 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' => 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' => 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' => 25.00], // Rak 77-79
|
||||||
|
$kode_utama >= 900 && $kode_utama <= 999 => match (true) {
|
||||||
|
$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],
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($koordinat['x'] !== null) {
|
||||||
|
$buku->lokasi_x = $koordinat['x'];
|
||||||
|
$buku->lokasi_y = $koordinat['y'];
|
||||||
|
$buku->save();
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Berhasil update $count buku presisi.";
|
||||||
Loading…
Reference in New Issue