diff --git a/app/Http/Controllers/Admin/AdminPeminjamanController.php b/app/Http/Controllers/Admin/AdminPeminjamanController.php
index fc5a32a..bb17f7c 100644
--- a/app/Http/Controllers/Admin/AdminPeminjamanController.php
+++ b/app/Http/Controllers/Admin/AdminPeminjamanController.php
@@ -31,6 +31,7 @@ public function index(Request $request)
'tanggal_pinjam' => $firstLoan->borrowed_at,
'tenggat_kembali' => $firstLoan->due_at,
'status' => $firstLoan->status,
+ 'role' => $user->role,
'books' => $userLoans->map(fn($l) => [
'id' => $l->book->id,
'judul' => $l->book->judul,
@@ -148,26 +149,36 @@ public function dendaIndex()
$tenggat = Carbon::parse($firstLoan->due_at);
$hariTelat = $now->greaterThan($tenggat) ? (int) $tenggat->diffInDays($now) : 0;
- $totalDenda = $hariTelat * 1000;
+
+ $isGuru = $user->role === 'guru';
+ $totalDenda = $isGuru ? 0 : ($hariTelat * 1000);
// Link WA
$hp = $user->phone ?? '';
$waLink = '#';
if ($hp) {
if (substr($hp, 0, 1) == '0') $hp = '62' . substr($hp, 1);
- $pesan = $hariTelat > 0
- ? "Halo {$user->nama_lengkap}, anda terlambat pengembalian buku. Total Denda: Rp " . number_format($totalDenda, 0, ',', '.')
- : "Halo {$user->nama_lengkap}, akun anda sedang dinonaktifkan sementara. Mohon hubungi petugas.";
+
+ if ($isGuru && $hariTelat > 0) {
+ $pesan = "Halo Bapak/Ibu {$user->nama_lengkap}, anda terlambat pengembalian buku selama {$hariTelat} hari. Mohon segera dikembalikan ke perpustakaan. Terima kasih.";
+ } else {
+ $pesan = $hariTelat > 0
+ ? "Halo {$user->nama_lengkap}, anda terlambat pengembalian buku. Total Denda: Rp " . number_format($totalDenda, 0, ',', '.')
+ : "Halo {$user->nama_lengkap}, akun anda sedang dinonaktifkan sementara. Mohon hubungi petugas.";
+ }
+
$waLink = "https://wa.me/{$hp}?text=" . urlencode($pesan);
}
return [
'id' => $firstLoan->id,
+ 'user_id' => $userId,
'peminjam' => $user->nama_lengkap,
'nomor_hp' => $user->phone ?? '-',
'kelas' => $user->kelas ?? 'Guru',
'hari_terlambat' => $hariTelat,
'total_denda' => $totalDenda,
+ 'is_guru' => $isGuru,
'wa_link' => $waLink,
'is_banned' => $user->is_banned,
'tenggat_kembali' => $firstLoan->due_at,
@@ -189,7 +200,61 @@ public function dendaIndex()
public function berikanSanksi(Request $request)
{
- // Actually implement banning logic here if needed
- return response()->json(['status' => 'success']);
+ $validated = $request->validate([
+ 'user_id' => 'required|exists:users,id',
+ 'action' => 'required|in:ban,unban',
+ ]);
+
+ $user = User::findOrFail($validated['user_id']);
+ $user->is_banned = ($validated['action'] === 'ban');
+ $user->save();
+
+ return response()->json([
+ 'status' => 'success',
+ 'message' => $user->is_banned ? "Akun {$user->nama_lengkap} berhasil dibekukan." : "Akun {$user->nama_lengkap} telah diaktifkan kembali."
+ ]);
+ }
+
+ public function kembalikan(Request $request)
+ {
+ $validated = $request->validate([
+ 'user_id' => 'required|exists:users,id',
+ 'returns' => 'required|array',
+ 'returns.*.book_id' => 'required|exists:books,id',
+ 'returns.*.condition' => 'required|string',
+ 'returns.*.fine_damage' => 'required|integer',
+ 'returns.*.fine_overdue' => 'required|integer',
+ 'returns.*.notes' => 'nullable|string',
+ ]);
+
+ \DB::beginTransaction();
+ try {
+ foreach ($validated['returns'] as $item) {
+ $loan = Loan::where('user_id', $validated['user_id'])
+ ->where('book_id', $item['book_id'])
+ ->whereIn('status', ['Dipinjam', 'Terlambat'])
+ ->first();
+
+ if ($loan) {
+ $loan->update([
+ 'status' => 'Dikembalikan',
+ 'returned_at' => now(),
+ 'condition' => $item['condition'],
+ 'fine_damage' => $item['fine_damage'],
+ 'fine_overdue' => $item['fine_overdue'],
+ 'return_notes' => $item['notes'],
+ ]);
+
+ // Update book status
+ $loan->book->update(['status' => 'Tersedia']);
+ }
+ }
+
+ \DB::commit();
+ return response()->json(['status' => 'success', 'message' => 'Buku berhasil dikembalikan.']);
+ } catch (\Exception $e) {
+ \DB::rollBack();
+ return response()->json(['status' => 'error', 'message' => $e->getMessage()], 500);
+ }
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Admin/BookController.php b/app/Http/Controllers/Admin/BookController.php
index d47f9a4..caf1908 100644
--- a/app/Http/Controllers/Admin/BookController.php
+++ b/app/Http/Controllers/Admin/BookController.php
@@ -21,10 +21,13 @@ public function index(Request $request)
$semuaBuku = $query->latest()->get();
- // Memisahkan buku menjadi dua koleksi: online dan offline
- [$bukuOnline, $bukuOffline] = $semuaBuku->partition(function ($buku) {
- $tipe = $buku->tipe_akses;
- return in_array('online', $tipe ?? []);
+ // Memisahkan buku menjadi dua koleksi: online dan offline secara independen
+ $bukuOnline = $semuaBuku->filter(function ($buku) {
+ return in_array('online', $buku->tipe_akses ?? []);
+ });
+
+ $bukuOffline = $semuaBuku->filter(function ($buku) {
+ return in_array('offline', $buku->tipe_akses ?? []);
});
return view('admin.buku.index', [
diff --git a/app/Http/Controllers/Admin/DashboardController.php b/app/Http/Controllers/Admin/DashboardController.php
index ae82837..ae77115 100644
--- a/app/Http/Controllers/Admin/DashboardController.php
+++ b/app/Http/Controllers/Admin/DashboardController.php
@@ -22,7 +22,7 @@ public function index()
['label' => 'Total Buku', 'value' => $allBooks, 'icon' => 'bi-journal-bookmark-fill', 'color' => 'primary'],
['label' => 'Total Anggota', 'value' => $allUsers, 'icon' => 'bi-people-fill', 'color' => 'success'],
['label' => 'Buku Dipinjam', 'value' => $bukuDipinjam, 'icon' => 'bi-arrow-up-right-circle-fill', 'color' => 'warning'],
- ['label' => 'Denda Menunggu', 'value' => Loan::where('status', 'Terlambat')->count(), 'icon' => 'bi-cash-coin', 'color' => 'danger'],
+ ['label' => 'Total Denda', 'value' => 'Rp ' . number_format(Loan::where('status', 'Terlambat')->sum('fine_overdue'), 0, ',', '.'), 'icon' => 'bi-cash-coin', 'color' => 'danger'],
];
// Monthly stats (last 7 months)
diff --git a/app/Http/Controllers/BacaOnlineController.php b/app/Http/Controllers/BacaOnlineController.php
index 6204780..1249cbe 100644
--- a/app/Http/Controllers/BacaOnlineController.php
+++ b/app/Http/Controllers/BacaOnlineController.php
@@ -9,6 +9,7 @@
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
@@ -134,15 +135,15 @@ public function streamPdf(int $id): BinaryFileResponse|Response
abort(403, 'Akses Ditolak.');
}
$book = Book::findOrFail($id);
- $filePath = 'books/' . ($book->file_pdf ?? 'sample.pdf');
- $absolutePath = storage_path('app/' . $filePath);
-
- // For demo purposes, if file doesn't exist, we might want to use a placeholder or handle it gracefully
- if (!file_exists($absolutePath)) {
- // Create a dummy file for testing if it doesn't exist? (Optional, maybe just abort)
+ $fileName = $book->file_pdf ?? 'sample.pdf';
+ $filePath = 'books/' . $fileName;
+
+ if (!Storage::disk('local')->exists($filePath)) {
abort(404, 'File PDF tidak ditemukan di server.');
}
+ $absolutePath = Storage::disk('local')->path($filePath);
+
return response()->file($absolutePath);
}
}
diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php
index 44fb5b3..ef9da8e 100644
--- a/app/Http/Controllers/DashboardController.php
+++ b/app/Http/Controllers/DashboardController.php
@@ -57,10 +57,28 @@ public function index()
['type' => 'info', 'icon' => 'bi-bell-fill', 'title' => 'Selamat Datang', 'content' => 'Selamat datang di perpustakaan digital SMKN 1.', 'badge' => 'Baru']
]);
- $progressMembaca = ['selesai' => 70, 'sisa' => 30]; // Still dummy as we don't track pages yet
+ // Dynamic reading progress based on returned vs total books
+ $totalUserLoans = Loan::where('user_id', $user->id)->count();
+ $returnedLoans = Loan::where('user_id', $user->id)->where('status', 'Dikembalikan')->count();
+ $progressSelesai = $totalUserLoans > 0 ? round(($returnedLoans / $totalUserLoans) * 100) : 0;
+
+ $progressMembaca = ['selesai' => $progressSelesai, 'sisa' => 100 - $progressSelesai];
+
+ // Dynamic monthly stats for the last 7 months
+ $labels = [];
+ $data = [];
+ for ($i = 6; $i >= 0; $i--) {
+ $month = Carbon::now()->subMonths($i);
+ $labels[] = $month->translatedFormat('M');
+ $data[] = Loan::where('user_id', $user->id)
+ ->whereMonth('borrowed_at', $month->month)
+ ->whereYear('borrowed_at', $month->year)
+ ->count();
+ }
+
$statistikBulanan = [
- 'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul'],
- 'data' => [10, 15, 8, 20, 18, 25, 22],
+ 'labels' => $labels,
+ 'data' => $data,
];
// Online books (books with 'online' in tipe_akses)
diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php
index fad62f6..32c6e2d 100644
--- a/app/Http/Controllers/ProfileController.php
+++ b/app/Http/Controllers/ProfileController.php
@@ -112,13 +112,15 @@ public function edit(Request $request): View
*/
public function update(ProfileUpdateRequest $request): RedirectResponse
{
- $request->user()->fill($request->validated());
+ $user = $request->user();
+ $user->fill($request->validated());
+ $user->name = $user->nama_lengkap; // Sync for compatibility
- if ($request->user()->isDirty('email')) {
- $request->user()->email_verified_at = null;
+ if ($user->isDirty('email')) {
+ $user->email_verified_at = null;
}
- $request->user()->save();
+ $user->save();
return Redirect::route('profile.edit')->with('status', 'profile-updated');
}
diff --git a/app/Http/Requests/ProfileUpdateRequest.php b/app/Http/Requests/ProfileUpdateRequest.php
index 3622a8f..f01b359 100644
--- a/app/Http/Requests/ProfileUpdateRequest.php
+++ b/app/Http/Requests/ProfileUpdateRequest.php
@@ -16,7 +16,7 @@ class ProfileUpdateRequest extends FormRequest
public function rules(): array
{
return [
- 'name' => ['required', 'string', 'max:255'],
+ 'nama_lengkap' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
@@ -25,6 +25,7 @@ public function rules(): array
'max:255',
Rule::unique(User::class)->ignore($this->user()->id),
],
+ 'phone' => ['required', 'string', 'max:20'],
];
}
}
diff --git a/app/Models/Book.php b/app/Models/Book.php
index 6942685..1caa369 100644
--- a/app/Models/Book.php
+++ b/app/Models/Book.php
@@ -8,7 +8,7 @@ class Book extends Model
{
protected $fillable = [
'judul', 'penulis', 'cover', 'kode_buku',
- 'category_id', 'tahun', 'status', 'is_new', 'tipe_akses'
+ 'category_id', 'tahun', 'status', 'is_new', 'tipe_akses', 'file_pdf'
];
protected $casts = [
diff --git a/app/Models/Loan.php b/app/Models/Loan.php
index b825af0..d2c823e 100644
--- a/app/Models/Loan.php
+++ b/app/Models/Loan.php
@@ -8,7 +8,8 @@ class Loan extends Model
{
protected $fillable = [
'user_id', 'book_id', 'loan_code',
- 'borrowed_at', 'due_at', 'returned_at', 'status'
+ 'borrowed_at', 'due_at', 'returned_at', 'status',
+ 'fine_overdue', 'fine_damage', 'condition', 'return_notes'
];
protected $casts = [
diff --git a/database/migrations/2026_02_09_153905_add_file_pdf_to_books_table.php b/database/migrations/2026_02_09_153905_add_file_pdf_to_books_table.php
new file mode 100644
index 0000000..ee95903
--- /dev/null
+++ b/database/migrations/2026_02_09_153905_add_file_pdf_to_books_table.php
@@ -0,0 +1,28 @@
+string('file_pdf')->nullable()->after('tipe_akses');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('books', function (Blueprint $table) {
+ $table->dropColumn('file_pdf');
+ });
+ }
+};
diff --git a/database/migrations/2026_02_09_181448_add_return_details_to_loans_table.php b/database/migrations/2026_02_09_181448_add_return_details_to_loans_table.php
new file mode 100644
index 0000000..44f644b
--- /dev/null
+++ b/database/migrations/2026_02_09_181448_add_return_details_to_loans_table.php
@@ -0,0 +1,31 @@
+integer('fine_overdue')->default(0)->after('status');
+ $table->integer('fine_damage')->default(0)->after('fine_overdue');
+ $table->string('condition')->nullable()->after('fine_damage');
+ $table->text('return_notes')->nullable()->after('condition');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('loans', function (Blueprint $table) {
+ $table->dropColumn(['fine_overdue', 'fine_damage', 'condition', 'return_notes']);
+ });
+ }
+};
diff --git a/resources/views/admin/buku/create.blade.php b/resources/views/admin/buku/create.blade.php
index d83a82f..2b96f7f 100644
--- a/resources/views/admin/buku/create.blade.php
+++ b/resources/views/admin/buku/create.blade.php
@@ -40,7 +40,7 @@
Tahun Terbit
+ placeholder="Contoh: 2024" min="0" required>
Kode Buku
diff --git a/resources/views/admin/buku/index.blade.php b/resources/views/admin/buku/index.blade.php
index 625544a..22a2fea 100644
--- a/resources/views/admin/buku/index.blade.php
+++ b/resources/views/admin/buku/index.blade.php
@@ -62,7 +62,7 @@ class="badge bg-warning-subtle text-warning-emphasis">Dipinjam
data-judul="{{ $buku['judul'] }}"
data-kode_buku="{{ $buku['kode_buku'] }}"
data-penulis="{{ $buku['penulis'] }}"
- data-kategori="{{ $buku['kategori'] }}"
+ data-kategori="{{ $buku->category->name ?? '-' }}"
data-tahun="{{ $buku['tahun'] }}" data-status="{{ $buku['status'] }}">
Detail
@@ -109,7 +109,7 @@ class="badge bg-info-subtle text-info-emphasis">{{ $buku['file_pdf'] ?? 'N/A' }}
data-cover="{{ asset($buku['cover']) }}"
data-judul="{{ $buku['judul'] }}"
data-penulis="{{ $buku['penulis'] }}"
- data-kategori="{{ $buku['kategori'] }}"
+ data-kategori="{{ $buku->category->name ?? '-' }}"
data-tahun="{{ $buku['tahun'] }}" data-status="Dapat Dibaca Online">
Detail
diff --git a/resources/views/admin/denda/index.blade.php b/resources/views/admin/denda/index.blade.php
index d2199a8..0702fc1 100644
--- a/resources/views/admin/denda/index.blade.php
+++ b/resources/views/admin/denda/index.blade.php
@@ -82,9 +82,14 @@ class="badge bg-danger-subtle text-danger border border-danger-subtle rounded-pi
- Rp
- {{ number_format($item['total_denda'], 0, ',', '.') }}
- Rp 1.000/hari
+ @if($item['is_guru'])
+ Bebas Denda
+ Kebijakan Guru
+ @else
+ Rp
+ {{ number_format($item['total_denda'], 0, ',', '.') }}
+ Rp 1.000/hari
+ @endif
@@ -98,12 +103,14 @@ class="btn btn-sm btn-success text-white" title="Tagih via WhatsApp">
@if ($item['is_banned'])
{{-- Jika sudah dibekukan (Otomatis/Manual), muncul tombol AKTIFKAN --}}
Aktifkan
@else
{{-- Jika belum dibekukan, muncul tombol SANKSI (Manual) --}}
Sanksi
@@ -183,11 +190,12 @@ function(settings, data, dataIndex) {
});
});
- // --- LOGIC TOMBOL SANKSI ---
$(document).on('click', '.btn-sanksi', function() {
const nama = $(this).data('nama');
+ const userId = $(this).data('user-id');
+
modernSwal.fire({
- title: 'Nonaktifkan Guru?',
+ title: 'Bekukan Akun?',
text: `Apakah Anda yakin ingin memberikan sanksi pembekuan akun kepada ${nama}?`,
icon: 'warning',
showCancelButton: true,
@@ -198,23 +206,40 @@ function(settings, data, dataIndex) {
if (result.isConfirmed) {
modernSwal.fire({
title: 'Memproses...',
- timer: 1000,
didOpen: () => Swal.showLoading()
- })
- .then(() => {
- Toast.fire({
- icon: 'success',
- title: 'Sanksi Diterapkan',
- text: `Akun ${nama} berhasil dibekukan.`
- });
});
+
+ $.ajax({
+ url: '{{ route('admin.denda.sanksi') }}',
+ method: 'POST',
+ data: {
+ _token: '{{ csrf_token() }}',
+ user_id: userId,
+ action: 'ban'
+ },
+ success: function(response) {
+ if (response.status === 'success') {
+ Toast.fire({
+ icon: 'success',
+ title: 'Sanksi Diterapkan',
+ text: response.message
+ });
+ setTimeout(() => location.reload(), 1500);
+ } else {
+ modernSwal.fire('Gagal', response.message, 'error');
+ }
+ },
+ error: function() {
+ modernSwal.fire('Gagal', 'Terjadi kesalahan saat memproses data.', 'error');
+ }
+ });
}
});
});
- // Logic Tombol Aktifkan (Unban)
$(document).on('click', '.btn-aktifkan', function() {
const nama = $(this).data('nama');
+ const userId = $(this).data('user-id');
modernSwal.fire({
title: 'Aktifkan Akun?',
@@ -226,20 +251,35 @@ function(settings, data, dataIndex) {
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
- // Simulasi Loading & Sukses
modernSwal.fire({
title: 'Memproses...',
- timer: 1000,
didOpen: () => Swal.showLoading()
- })
- .then(() => {
- Toast.fire({
- icon: 'success',
- title: 'Akun Diaktifkan',
- text: `Status sanksi pada ${nama} telah dicabut.`
- });
- $(this).closest('td').html('
Aktif ');
});
+
+ $.ajax({
+ url: '{{ route('admin.denda.sanksi') }}',
+ method: 'POST',
+ data: {
+ _token: '{{ csrf_token() }}',
+ user_id: userId,
+ action: 'unban'
+ },
+ success: function(response) {
+ if (response.status === 'success') {
+ Toast.fire({
+ icon: 'success',
+ title: 'Akun Diaktifkan',
+ text: response.message
+ });
+ setTimeout(() => location.reload(), 1500);
+ } else {
+ modernSwal.fire('Gagal', response.message, 'error');
+ }
+ },
+ error: function() {
+ modernSwal.fire('Gagal', 'Terjadi kesalahan saat memproses data.', 'error');
+ }
+ });
}
});
});
diff --git a/resources/views/admin/peminjaman/index.blade.php b/resources/views/admin/peminjaman/index.blade.php
index 40be248..64edc22 100644
--- a/resources/views/admin/peminjaman/index.blade.php
+++ b/resources/views/admin/peminjaman/index.blade.php
@@ -108,7 +108,7 @@ class="badge bg-primary-subtle text-primary fw-bold">{{ $transaksi['id_peminjama
Daftar Buku yang Dikembalikan:
@foreach ($transaksi['books'] as $buku)
-
+
{{ $buku['judul'] }}
@@ -159,17 +159,25 @@ class="form-control form-control-sm denda-rusak-input"
Denda Keterlambatan ({{ $statusText }})
@php
+ $isGuru = ($transaksi['role'] ?? '') === 'guru';
$dendaTelat = 0;
- if ($isTerlambat) {
+ if ($isTerlambat && !$isGuru) {
$hari = $tenggat->startOfDay()->diffInDays($now->startOfDay());
$dendaTelat = $hari * 1000;
}
@endphp
{{-- Data Attribute untuk JS --}}
-
- Rp {{ number_format($dendaTelat, 0, ',', '.') }}
-
+ @if($isGuru && $isTerlambat)
+
+ Bebas Denda
+
+ @else
+
+ Rp {{ number_format($dendaTelat, 0, ',', '.') }}
+
+ @endif
Batal
+ data-nomor-hp="{{ $transaksi['nomor_hp'] }}"
+ data-user-id="{{ $transaksi['user_id'] }}">
Konfirmasi & Selesai
@@ -291,9 +300,11 @@ function hitungTotalDenda(modal) {
$(document).on('click', '.btn-konfirmasi-kembali', function() {
const nama = $(this).data('nama-peminjam');
+ const hp = $(this).data('nomor-hp');
const modalEl = $(this).closest('.modal');
const modalInstance = bootstrap.Modal.getInstance(modalEl[0]);
const isEmailChecked = modalEl.find('.email-toggle').is(':checked');
+ const isWaChecked = modalEl.find('input[role="switch"][id^="waStrukToggle"]').is(':checked');
modalInstance.hide();
@@ -313,10 +324,58 @@ function hitungTotalDenda(modal) {
timerProgressBar: true,
didOpen: () => Swal.showLoading()
}).then(() => {
- // Loading Kirim Email (Jika dicentang)
+ // Prepare Request Data
+ const returnsData = [];
+ const booksList = [];
+ const dendaOverdueTotal = parseInt(modalEl.find('.denda-keterlambatan-display').data('denda-keterlambatan'));
+ const dendaOverduePerBook = dendaOverdueTotal / modalEl.find('.book-return-item').length;
+
+ let totalDendaRusak = 0;
+ modalEl.find('.book-return-item').each(function() {
+ const bookId = $(this).data('book-id');
+ const judul = $(this).find('h6').text();
+ const condition = $(this).find('.radio-kondisi:checked').val();
+ const fineDamage = parseInt($(this).find('.denda-rusak-input').val()) || 0;
+ const notes = $(this).find('textarea').val();
+
+ totalDendaRusak += fineDamage;
+ booksList.push(judul);
+ returnsData.push({
+ book_id: bookId,
+ condition: condition,
+ fine_damage: fineDamage,
+ fine_overdue: Math.round(dendaOverduePerBook),
+ notes: notes
+ });
+ });
+
+ const userId = modalEl.find('.btn-konfirmasi-kembali').data('user-id');
+ const totalDenda = dendaOverdueTotal + totalDendaRusak;
+
+ // Construct WA Message
+ let waLink = null;
+ if (isWaChecked && hp && hp !== '-') {
+ let phone = hp;
+ if (phone.startsWith('0')) phone = '62' + phone.substring(1);
+
+ let message = `*BUKTI PENGEMBALIAN BUKU*\n\n`;
+ message += `Halo ${nama},\n`;
+ message += `Terima kasih telah mengembalikan buku:\n`;
+ booksList.forEach(b => message += `- ${b}\n`);
+ message += `\n*Status:* Berhasil Dikembalikan\n`;
+ if (totalDenda > 0) {
+ message += `*Total Denda:* Rp ${new Intl.NumberFormat('id-ID').format(totalDenda)}\n`;
+ } else {
+ message += `*Denda:* Bebas Denda\n`;
+ }
+ message += `\n_Simpan pesan ini sebagai bukti pengembalian._`;
+
+ waLink = `https://wa.me/${phone}?text=${encodeURIComponent(message)}`;
+ }
+
+ // Loading Kirim Email flow
if (isEmailChecked) {
- const dummyEmail = nama.replace(/\s+/g, '.').toLowerCase() +
- '@sekolah.sch.id';
+ const dummyEmail = nama.replace(/\s+/g, '.').toLowerCase() + '@sekolah.sch.id';
modernSwal.fire({
title: 'Mengirim Email...',
html: `Mengirim nota ke:
${dummyEmail} `,
@@ -324,10 +383,10 @@ function hitungTotalDenda(modal) {
timerProgressBar: true,
didOpen: () => Swal.showLoading()
}).then(() => {
- finishTransaction(true);
+ finishTransaction(returnsData, userId, waLink);
});
} else {
- finishTransaction(false);
+ finishTransaction(returnsData, userId, waLink);
}
});
} else {
@@ -335,13 +394,35 @@ function hitungTotalDenda(modal) {
}
});
- function finishTransaction(withEmail) {
- Toast.fire({
- icon: 'success',
- title: 'Berhasil',
- text: withEmail ? 'Buku kembali & Email terkirim.' : 'Buku berhasil dikembalikan.'
+ function finishTransaction(returnsData, userId, waLink) {
+ $.ajax({
+ url: '{{ route('admin.peminjaman.kembali') }}',
+ method: 'POST',
+ data: {
+ _token: '{{ csrf_token() }}',
+ user_id: userId,
+ returns: returnsData
+ },
+ success: function(response) {
+ if (response.status === 'success') {
+ if (waLink) {
+ window.open(waLink, '_blank');
+ }
+
+ Toast.fire({
+ icon: 'success',
+ title: 'Berhasil',
+ text: 'Buku berhasil dikembalikan.'
+ });
+ setTimeout(() => location.reload(), 1500);
+ } else {
+ modernSwal.fire('Gagal', response.message, 'error');
+ }
+ },
+ error: function(xhr) {
+ modernSwal.fire('Gagal', 'Terjadi kesalahan saat memproses data.', 'error');
+ }
});
- setTimeout(() => location.reload(), 1500);
}
});
diff --git a/resources/views/admin/pengguna/create.blade.php b/resources/views/admin/pengguna/create.blade.php
index 443a4e0..5d57415 100644
--- a/resources/views/admin/pengguna/create.blade.php
+++ b/resources/views/admin/pengguna/create.blade.php
@@ -22,19 +22,12 @@
Email
+ placeholder="Masukkan alamat email" required pattern="[^@\s]+@[^@\s]+\.[^@\s]+" title="Masukkan alamat email yang valid">
Nomor Induk (NISN/NUPTK)
-
-
-
- Nomor HP
-
-
-
- Kelas (jika siswa)
-
+
Role
@@ -45,6 +38,14 @@
Penjaga Perpus
+
+ Kelas (jika siswa)
+
+
+
+ Nomor HP
+
+
Password
@@ -60,6 +61,33 @@
Simpan Pengguna
+
+
diff --git a/resources/views/admin/pengguna/edit.blade.php b/resources/views/admin/pengguna/edit.blade.php
index c00d7f6..6c066ac 100644
--- a/resources/views/admin/pengguna/edit.blade.php
+++ b/resources/views/admin/pengguna/edit.blade.php
@@ -23,22 +23,12 @@
Email
+ value="{{ old('email', $pengguna->email) }}" required pattern="[^@\s]+@[^@\s]+\.[^@\s]+" title="Masukkan alamat email yang valid">
Nomor Induk (NISN/NUPTK)
-
-
-
- Nomor HP
-
-
-
- Kelas (jika siswa)
-
+
Role
@@ -52,6 +42,16 @@
Penjaga Perpus
+
+ Kelas (jika siswa)
+
+
+
+ Nomor HP
+
+
Password Baru
@@ -68,6 +68,44 @@
Simpan Perubahan
+
+
diff --git a/resources/views/admin/pengguna/index.blade.php b/resources/views/admin/pengguna/index.blade.php
index 9ff289e..1a0e8a1 100644
--- a/resources/views/admin/pengguna/index.blade.php
+++ b/resources/views/admin/pengguna/index.blade.php
@@ -39,7 +39,7 @@
data-nama="{{ $siswa['nama_lengkap'] }}"
data-email="{{ $siswa['email'] }}"
data-role="{{ Str::title($siswa['role']) }}"
- data-nisn="{{ $siswa['nisn'] ?? 'N/A' }}">
+ data-nisn="{{ $siswa['nomor_induk'] ?? 'N/A' }}">
Detail
diff --git a/resources/views/admin/pengumuman/index.blade.php b/resources/views/admin/pengumuman/index.blade.php
index 569a168..729404f 100644
--- a/resources/views/admin/pengumuman/index.blade.php
+++ b/resources/views/admin/pengumuman/index.blade.php
@@ -32,12 +32,20 @@
{{ $item['content'] }}
-
-
-
-
-
-
+
@empty
@@ -52,4 +60,27 @@
+
+ @push('scripts')
+
+ @endpush
\ No newline at end of file
diff --git a/resources/views/admin/rekomendasi/index.blade.php b/resources/views/admin/rekomendasi/index.blade.php
index aa11efd..12cbbb1 100644
--- a/resources/views/admin/rekomendasi/index.blade.php
+++ b/resources/views/admin/rekomendasi/index.blade.php
@@ -17,7 +17,20 @@
{{ $item['judul'] }}
{{ $item['kategori'] }}
-
+
@endforeach
@@ -26,4 +39,27 @@
+
+ @push('scripts')
+
+ @endpush
\ No newline at end of file
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php
index 3eb1e75..e8756e9 100644
--- a/resources/views/auth/login.blade.php
+++ b/resources/views/auth/login.blade.php
@@ -1,8 +1,8 @@
@if ($errors->has('forbidden'))
-
-
-
{{ $errors->first('forbidden') }}
+
+
+
{{ $errors->first('forbidden') }}
@endif
diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php
index 5d65e5c..ad644ac 100644
--- a/resources/views/auth/register.blade.php
+++ b/resources/views/auth/register.blade.php
@@ -16,7 +16,10 @@
Nama Lengkap
@error('name')
-
{{ $message }}
+
+
+ {{ $message }}
+
@enderror
@@ -26,7 +29,10 @@
Nomor Induk Siswa Nasional (NISN)
@error('nisn')
- {{ $message }}
+
+
+ {{ $message }}
+
@enderror
@else
@@ -34,7 +40,10 @@
Nomor Induk Pegawai (NIP)
@error('nip')
- {{ $message }}
+
+
+ {{ $message }}
+
@enderror
@endif
@@ -44,7 +53,10 @@
Password
@error('password')
- {{ $message }}
+
+
+ {{ $message }}
+
@enderror
diff --git a/resources/views/baca/request_code.blade.php b/resources/views/baca/request_code.blade.php
index 1e2cee2..9e30089 100644
--- a/resources/views/baca/request_code.blade.php
+++ b/resources/views/baca/request_code.blade.php
@@ -36,8 +36,9 @@
Jangan bagikan kode ini kepada orang lain.
@if (session('error'))
-
- {{ session('error') }}
+
+
+
{{ session('error') }}
@endif
diff --git a/resources/views/components/auth-session-status.blade.php b/resources/views/components/auth-session-status.blade.php
index c4bd6e2..8502916 100644
--- a/resources/views/components/auth-session-status.blade.php
+++ b/resources/views/components/auth-session-status.blade.php
@@ -1,7 +1,8 @@
@props(['status'])
@if ($status)
-
merge(['class' => 'font-medium text-sm text-green-600']) }}>
- {{ $status }}
+
merge(['class' => 'alert alert-success d-flex align-items-center mb-4']) }} role="alert">
+
+
{{ $status }}
@endif
diff --git a/resources/views/components/input-error.blade.php b/resources/views/components/input-error.blade.php
index 9e6da21..4e2bec2 100644
--- a/resources/views/components/input-error.blade.php
+++ b/resources/views/components/input-error.blade.php
@@ -1,9 +1,12 @@
@props(['messages'])
@if ($messages)
-
merge(['class' => 'text-sm text-red-600 space-y-1']) }}>
+ merge(['class' => 'list-unstyled mt-1 mb-0']) }}>
@foreach ((array) $messages as $message)
- {{ $message }}
+
+
+ {{ $message }}
+
@endforeach
@endif
diff --git a/resources/views/katalog/index.blade.php b/resources/views/katalog/index.blade.php
index b3ae114..842fbfa 100644
--- a/resources/views/katalog/index.blade.php
+++ b/resources/views/katalog/index.blade.php
@@ -113,11 +113,16 @@ class="btn btn-sm btn-primary w-100">
@empty
-
-
Tidak Ada Hasil
-
Tidak ada buku yang cocok dengan kriteria filter Anda. Coba reset atau ubah filter.
-
-
Reset Filter
+
+
+
+
+
Tidak Ada Hasil
+
Tidak ada buku yang cocok dengan kriteria filter Anda. Coba reset atau ubah filter pencarian Anda.
+
+
+ Reset Filter
+
@endforelse
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php
index 417a886..2626ef0 100644
--- a/resources/views/layouts/app.blade.php
+++ b/resources/views/layouts/app.blade.php
@@ -33,9 +33,40 @@
@include('layouts.navigation')
@if (session('error'))
-
-
-
DITOLAK! {{ session('error') }}
+
+
+
+
+ DITOLAK!
+ {{ session('error') }}
+
+
+
+
+ @endif
+
+ @if (session('success'))
+
+
+
+
+ BERHASIL!
+ {{ session('success') }}
+
+
+
+
+ @endif
+
+ @if (session('warning'))
+
+
+
+
+ PERINGATAN!
+ {{ session('warning') }}
+
+
@endif
diff --git a/resources/views/peminjaman/form.blade.php b/resources/views/peminjaman/form.blade.php
index 3408091..21479bd 100644
--- a/resources/views/peminjaman/form.blade.php
+++ b/resources/views/peminjaman/form.blade.php
@@ -29,7 +29,7 @@
Nomor Handphone
-
{{ $user['nomor_hp'] }}
+
{{ $user->phone ?? 'N/A' }}
Kelas
@@ -275,9 +275,9 @@ class="bi bi-star-fill me-1">Buku Utama
{{--
Setuju --}}
{{-- Button Setuju jika belum ada backend, akan ter direct ke peminjaman.index --}}
-
+
Setuju
-
+
@@ -286,7 +286,6 @@ class="bi bi-star-fill me-1">Buku Utama
document.addEventListener("DOMContentLoaded", function() {
// (Default Hari Ini + 2 Hari)
- // Simpan instance-nya ke variabel
const fpKembali = flatpickr("#tanggalKembali", {
dateFormat: "d F Y",
altInput: true,
@@ -306,7 +305,6 @@ class="bi bi-star-fill me-1">Buku Utama
locale: "id",
minDate: "today",
- // LOGIC : Saat Tanggal Pinjam Berubah
onChange: function(selectedDates, dateStr) {
if (selectedDates.length > 0) {
const tglMulai = selectedDates[0];
@@ -315,11 +313,117 @@ class="bi bi-star-fill me-1">Buku Utama
fpKembali.set("minDate", minDateBaru);
fpKembali.set("maxDate", maxDateBaru);
-
fpKembali.setDate(maxDateBaru);
}
}
});
+
+ // Inisialisasi data dari data-attributes
+ const form = document.getElementById('formPeminjaman');
+ const arrayBuku = JSON.parse(form.dataset.semuaBuku);
+ const bukuUtama = JSON.parse(form.dataset.bukuAwal);
+
+ let selectedIds = [bukuUtama.id];
+ const MAX_BOOKS = 2;
+
+ // Global functions
+ window.toggleBookSelection = function(id) {
+ const index = selectedIds.indexOf(id);
+ const checkbox = document.getElementById('book' + id);
+
+ if (index > -1) {
+ if (id === bukuUtama.id) return;
+ selectedIds.splice(index, 1);
+ if (checkbox) checkbox.checked = false;
+ } else {
+ if (selectedIds.length >= MAX_BOOKS) {
+ alert('Maksimal peminjaman adalah ' + MAX_BOOKS + ' buku.');
+ return;
+ }
+ selectedIds.push(id);
+ if (checkbox) checkbox.checked = true;
+ }
+ updateCounter();
+ };
+
+ window.konfirmasiPilihanBuku = function() {
+ renderDaftarBuku();
+ const modal = bootstrap.Modal.getInstance(document.getElementById('pilihBukuModal'));
+ modal.hide();
+ };
+
+ function updateCounter() {
+ const counterEl = document.getElementById('counterBuku');
+ const sisaSlotEl = document.getElementById('sisaSlot');
+ if (counterEl) counterEl.textContent = selectedIds.length;
+ if (sisaSlotEl) sisaSlotEl.textContent = MAX_BOOKS - selectedIds.length;
+ }
+
+ function renderDaftarBuku() {
+ const container = document.getElementById('daftarBukuPinjam');
+ const hiddenInputs = document.getElementById('hiddenInputs');
+ const ringkasan = document.getElementById('ringkasanBuku');
+
+ if (!container || !hiddenInputs || !ringkasan) return;
+
+ container.innerHTML = '';
+ hiddenInputs.innerHTML = '';
+ ringkasan.innerHTML = '';
+
+ selectedIds.forEach(id => {
+ const buku = arrayBuku.find(b => b.id == id);
+ if (!buku) return;
+
+ const isUtama = id === (typeof bukuUtama.id === 'string' ? parseInt(bukuUtama.id) : bukuUtama.id);
+
+ const itemHtml = `
+
+
+
+
+
${buku.judul}
+
${buku.penulis}
+
${buku.kategori || 'Kategori'}
+
+ ${isUtama ? '
Buku Utama ' :
+ '
'}
+
+
+ `;
+ container.insertAdjacentHTML('beforeend', itemHtml);
+
+ const input = document.createElement('input');
+ input.type = 'hidden';
+ input.name = 'buku_ids[]';
+ input.value = buku.id;
+ hiddenInputs.appendChild(input);
+
+ ringkasan.innerHTML += `${buku.judul} `;
+ });
+
+ ringkasan.innerHTML += ' ';
+ }
+
+ window.toggleSelectionAndRender = function(id) {
+ window.toggleBookSelection(id);
+ renderDaftarBuku();
+ };
+
+ document.getElementById('searchBuku').addEventListener('input', function(e) {
+ const keyword = e.target.value.toLowerCase();
+ document.querySelectorAll('.book-option').forEach(el => {
+ const title = el.dataset.bookTitle;
+ const author = el.dataset.bookAuthor;
+ if (title.includes(keyword) || author.includes(keyword)) {
+ el.style.display = 'block';
+ } else {
+ el.style.display = 'none';
+ }
+ });
+ });
+
+ renderDaftarBuku();
+ updateCounter();
});
diff --git a/resources/views/profile/index.blade.php b/resources/views/profile/index.blade.php
index a62a2ed..81dc7ca 100644
--- a/resources/views/profile/index.blade.php
+++ b/resources/views/profile/index.blade.php
@@ -110,6 +110,22 @@ class="btn btn-outline-primary rounded-pill w-100 w-md-auto ms-md-auto">
Edit Profil
+
+ Informasi Personal
+
+
+
NIP / NUPTK
+
{{ $user->nuptk ?? ($user->nomor_induk ?? 'N/A') }}
+
+
+
Email
+
{{ $user->email }}
+
+
+
Nomor HP
+
{{ $user->phone ?? 'N/A' }}
+
+
{{-- Ringkasan Laporan Minat Baca --}}
@@ -192,7 +208,7 @@ class="btn btn-outline-primary rounded-pill ms-md-auto">
NISN
-
{{ $user->nisn ?? 'N/A' }}
+
{{ $user->nomor_induk ?? 'N/A' }}
Email
@@ -200,7 +216,7 @@ class="btn btn-outline-primary rounded-pill ms-md-auto">
Nomor HP
-
{{ $user->nomor_hp ?? 'N/A' }}
+
{{ $user->phone ?? 'N/A' }}
Kelas
diff --git a/resources/views/profile/partials/update-profile-information-form.blade.php b/resources/views/profile/partials/update-profile-information-form.blade.php
index fea16f4..64bb793 100644
--- a/resources/views/profile/partials/update-profile-information-form.blade.php
+++ b/resources/views/profile/partials/update-profile-information-form.blade.php
@@ -55,43 +55,41 @@ class="form-control @error('email') is-invalid @enderror" value="{{ old('email',
{{-- Bagian Nomor Telepon --}}
- @if ($user->nomor_hp)
-
-
{{ __('Nomor Telepon (WA)') }}
-
- @error('nomor_hp')
-
{{ $message }}
- @enderror
-
*Nomor telepon wajib disi
-
- @endif
+
+
{{ __('Nomor Telepon (WA)') }}
+
+ @error('phone')
+
{{ $message }}
+ @enderror
+
*Nomor telepon wajib diisi
+
{{-- Bagian Info Spesifik Role (Tidak Dapat Diubah) --}}
@if ($user->role == 'siswa')
- NISN (Nomor Induk Siswa Nasional)
-
+ NISN (Nomor Induk Siswa Nasional)
+
NISN tidak dapat diubah.
@else
{{-- Untuk Guru atau Penjaga Perpus --}}
- NIP (Nomor Induk Pegawai)
-
- NIP tidak dapat diubah.
+ NIP / NUPTK
+
+ ID Kepegawaian tidak dapat diubah.
@endif
diff --git a/routes/web.php b/routes/web.php
index e77e22d..d8a5c0d 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -112,6 +112,7 @@
Route::get('/peminjaman', [AdminPeminjamanController::class, 'index'])->name('peminjaman.index');
Route::get('/peminjaman/tambah', [AdminPeminjamanController::class, 'create'])->name('peminjaman.create');
Route::post('/peminjaman', [AdminPeminjamanController::class, 'store'])->name('peminjaman.store');
+ Route::post('/peminjaman/kembali', [AdminPeminjamanController::class, 'kembalikan'])->name('peminjaman.kembali');
Route::get('/denda', [AdminPeminjamanController::class, 'dendaIndex'])->name('denda.index');
Route::post('/denda/sanksi', [AdminPeminjamanController::class, 'berikanSanksi'])->name('denda.sanksi');