Memperbaiki UI dan fitur kirim WA Fonnte

This commit is contained in:
wardhatul1765 2026-05-02 10:50:45 +07:00
parent ca98d0a665
commit 70a420ae47
25 changed files with 589 additions and 54 deletions

View File

@ -27,7 +27,7 @@ public function store(Request $request)
'nomor_panggil' => 'required|string|max:50', 'nomor_panggil' => 'required|string|max:50',
'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')) {
@ -78,7 +78,7 @@ public function update(Request $request, $id)
'nomor_panggil' => 'required|string|max:50', 'nomor_panggil' => 'required|string|max:50',
'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')) {

View File

@ -305,4 +305,61 @@ 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);
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)) {
$pesanStruk = "🏢 *PERPUSTAKAAN DAERAH JEMBER*\n";
$pesanStruk .= "Jl. Mastrip No. 1, Kabupaten Jember\n";
$pesanStruk .= "===============================\n\n";
$pesanStruk .= "📄 *SALINAN BUKTI PEMINJAMAN*\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 Panggil : {$peminjaman->buku->bibid}\n";
$pesanStruk .= "Pinjam : " . \Carbon\Carbon::parse($peminjaman->tanggal_pinjam)->format('d F Y') . "\n";
$pesanStruk .= "Kembali: *" . \Carbon\Carbon::parse($peminjaman->tanggal_kembali)->format('d F Y') . "*\n\n";
$pesanStruk .= "===============================\n";
$pesanStruk .= "⚠️ *Catatan:*\n";
$pesanStruk .= "Tunjukkan Kode Panggil ke Admin saat pengembalian buku.\n";
$pesanStruk .= "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 back()->with('success', 'Struk WhatsApp berhasil dikirim ulang ke nomor ' . $targetNum);
} else {
\Illuminate\Support\Facades\Log::warning("Fonnte Log: Gagal Terkirim Ulang", ['body' => $response->body()]);
return back()->with('error', 'Gagal mengirim ulang WA. API Fonnte menolak koneksi (Status: '.$response->status().').');
}
} else {
return back()->with('error', 'Nomor HP anggota kosong atau Token Fonnte belum dikonfigurasi.');
}
} catch (\Exception $e) {
\Illuminate\Support\Facades\Log::error("Error WA Pengiriman Ulang: " . $e->getMessage());
return back()->with('error', 'Terjadi kesalahan sistem saat menghubungi server WhatsApp.');
}
}
} }

View File

@ -33,7 +33,7 @@ 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' => 'nullable|numeric|digits_between:1,16',
'prodi' => 'nullable|string|max:255', 'prodi' => 'nullable|string|max:255',
'no_hp' => 'required|string|max:20', 'no_hp' => 'required|string|max:20',
'alamat' => 'required|string', 'alamat' => 'required|string',
@ -60,7 +60,7 @@ 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' => 'nullable|numeric|digits_between:1,16',
'prodi' => 'nullable|string|max:255', 'prodi' => 'nullable|string|max:255',
'no_hp' => 'required|string|max:20', 'no_hp' => 'required|string|max:20',
'alamat' => 'required|string', 'alamat' => 'required|string',

View File

@ -2,6 +2,7 @@
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
@ -19,7 +20,7 @@ public function register(): void
*/ */
public function boot(): void public function boot(): void
{ {
// Paginator::useTailwind();
} }
} }

View File

@ -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">

View File

@ -153,6 +153,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') }}')"

View File

@ -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>

View File

@ -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>
@ -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" 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>
@ -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>-- 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>

View File

@ -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>
@ -229,7 +229,7 @@ class="inline-block px-4 pt-5 pb-4 overflow-hidden text-left align-bottom transi
<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>

View File

@ -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>

View File

@ -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>

View File

@ -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']) }}>

View File

@ -26,12 +26,17 @@
.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; }
</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>
@ -49,9 +54,12 @@
</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">

View File

@ -107,11 +107,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,7 +126,13 @@ 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') }}"
@ -163,6 +169,37 @@ 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">
<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 --}}
@ -239,6 +276,27 @@ class="fab fa-twitter text-sm"></i></a>
</footer> </footer>
@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');
}
});
});
</script>
</body> </body>
</html> </html>

View File

@ -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">&lsaquo;</span>
</li>
@else
<li class="page-item">
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">&lsaquo;</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')">&rsaquo;</a>
</li>
@else
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
<span class="page-link" aria-hidden="true">&rsaquo;</span>
</li>
@endif
</ul>
</nav>
@endif

View File

@ -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">&lsaquo;</span>
</li>
@else
<li class="page-item">
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">&lsaquo;</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')">&rsaquo;</a>
</li>
@else
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
<span class="page-link" aria-hidden="true">&rsaquo;</span>
</li>
@endif
</ul>
</div>
</div>
</nav>
@endif

View File

@ -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">&lsaquo;</span>
</li>
@else
<li>
<a href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">&lsaquo;</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')">&rsaquo;</a>
</li>
@else
<li class="disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
<span aria-hidden="true">&rsaquo;</span>
</li>
@endif
</ul>
</nav>
@endif

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,29 @@ 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 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 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" class="relative group max-w-2xl mx-auto px-2 sm:px-0">
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none"> <div class="absolute inset-y-0 left-2 sm:left-0 pl-4 flex items-center pointer-events-none">
<svg class="h-6 w-6 text-gray-400 group-focus-within:text-blue-500 transition-colors" <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, atau penerbit..." placeholder="Cari judul buku, pengarang..."
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"> 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">
<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>

View File

@ -130,3 +130,4 @@
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');