Feat: Finalize denda system, implemented manual sanction for teachers, fixed dashboard error, and updated UI modal
This commit is contained in:
parent
156a4e00a9
commit
779ef38952
|
|
@ -29,14 +29,11 @@ public function retrieveById($identifier)
|
|||
|
||||
/**
|
||||
* Rehash the user's password if required.
|
||||
* Method ini wajib ada di Laravel 12. Kita biarkan kosong karena tidak relevan untuk data dummy.
|
||||
*/
|
||||
public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false): void
|
||||
{
|
||||
// Biarkan kosong
|
||||
}
|
||||
|
||||
// Fungsi-fungsi di bawah ini tidak kita pakai, tapi harus ada.
|
||||
public function retrieveByToken($identifier, $token) { return null; }
|
||||
public function updateRememberToken(Authenticatable $user, $token) { }
|
||||
public function retrieveByCredentials(array $credentials) { return null; }
|
||||
|
|
|
|||
|
|
@ -9,36 +9,78 @@
|
|||
|
||||
class AdminPeminjamanController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan halaman utama manajemen peminjaman (Tabel).
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
// Panggil data dari DummyDataService
|
||||
$peminjamanAktif = DummyDataService::getAdminPeminjamanAktif();
|
||||
|
||||
// LOGIC DENDA & WA LINK (Update Request Client)
|
||||
$peminjamanAktif = $peminjamanAktif->map(function ($item) {
|
||||
// Hitung Denda Flat 1000/hari
|
||||
$tenggat = Carbon::parse($item['tenggat_kembali']);
|
||||
$now = Carbon::now();
|
||||
|
||||
$item['hari_terlambat'] = 0;
|
||||
$item['total_denda'] = 0;
|
||||
|
||||
if ($now->greaterThan($tenggat)) {
|
||||
$hariTelat = $tenggat->diffInDays($now);
|
||||
$item['hari_terlambat'] = $hariTelat;
|
||||
$item['total_denda'] = $hariTelat * 1000;
|
||||
$item['denda_per_hari'] = 1000;
|
||||
}
|
||||
// Generate WA Link
|
||||
$hp = $item['nomor_hp'];
|
||||
if (substr($hp, 0, 1) == '0') {
|
||||
$hp = '62' . substr($hp, 1);
|
||||
}
|
||||
|
||||
$pesan = "Halo kak {$item['peminjam']}, buku anda sudah terlambat {$item['hari_terlambat']} hari dengan denda Rp " . number_format($item['total_denda'], 0, ',', '.') . ". Mohon segera dikembalikan ya.";
|
||||
$item['wa_link'] = "https://wa.me/{$hp}?text=" . urlencode($pesan);
|
||||
|
||||
return $item;
|
||||
});
|
||||
|
||||
$daftarPeminjam = $peminjamanAktif->pluck('peminjam')->unique();
|
||||
|
||||
|
||||
return view('admin.peminjaman.index', [
|
||||
'pageTitle' => 'Manajemen Peminjaman',
|
||||
'pageTitle' => 'Manajemen Peminjaman & Denda',
|
||||
'peminjamanAktif' => $peminjamanAktif,
|
||||
'daftarPeminjam' => $daftarPeminjam,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan formulir untuk membuat peminjaman manual.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$allUsers = collect(DummyDataService::getAllSiswa());
|
||||
$peminjamanAktif = DummyDataService::getAdminPeminjamanAktif();
|
||||
|
||||
// Kelompokkan berdasarkan 'role'
|
||||
$groupedUsers = $allUsers
|
||||
->whereIn('role', ['siswa', 'guru'])
|
||||
->map(function ($user) use ($peminjamanAktif) {
|
||||
|
||||
// Hitung berapa buku yang sedang dipinjam
|
||||
$jumlahPinjam = $peminjamanAktif->where('peminjam', $user['nama_lengkap'])->count();
|
||||
|
||||
// Cek Status
|
||||
$user['jumlah_pinjam'] = $jumlahPinjam;
|
||||
$user['kena_limit'] = $jumlahPinjam >= 2;
|
||||
|
||||
// Cek apakah user di-banned (Nonaktif Manual)
|
||||
$user['is_banned'] = $user['is_banned'] ?? false;
|
||||
$user['disabled'] = $user['kena_limit'] || $user['is_banned'];
|
||||
|
||||
if ($user['is_banned']) {
|
||||
$user['status_text'] = "(Akun Dibekukan)";
|
||||
} elseif ($user['kena_limit']) {
|
||||
$user['status_text'] = "(Limit Penuh: 2/2)";
|
||||
} else {
|
||||
$user['status_text'] = "";
|
||||
}
|
||||
|
||||
return $user;
|
||||
})
|
||||
->groupBy('role');
|
||||
|
||||
// Filter hanya buku offline
|
||||
$daftarBuku = DummyDataService::getAllBooks()
|
||||
->where('status', 'Tersedia')
|
||||
->filter(function ($buku) {
|
||||
|
|
@ -54,4 +96,96 @@ public function create()
|
|||
'daftarBuku' => $daftarBuku,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan halaman KHUSUS Manajemen Denda (Siswa & Guru Telat).
|
||||
*/
|
||||
public function dendaIndex()
|
||||
{
|
||||
$allData = DummyDataService::getAdminPeminjamanAktif();
|
||||
$allSiswaRaw = collect(DummyDataService::getAllSiswa());
|
||||
$now = \Carbon\Carbon::now();
|
||||
|
||||
// LOGIC AUTO-BAN
|
||||
$allSiswa = $allSiswaRaw->map(function ($siswa) use ($allData, $now) {
|
||||
|
||||
// Cek siswa punya pinjaman yang telat
|
||||
$pinjamanUser = $allData->firstWhere('user_id', $siswa['id']);
|
||||
|
||||
$isTelat = false;
|
||||
if ($pinjamanUser) {
|
||||
$isTelat = $pinjamanUser['tenggat_kembali']->startOfDay()->lt($now->startOfDay());
|
||||
}
|
||||
|
||||
// Jika Role SISWA dan TELAT -> Wajib AUTO BAN (Override true)
|
||||
// ika Role GURU -> Ikut status asli database (Manual Ban)
|
||||
if ($siswa['role'] === 'siswa' && $isTelat) {
|
||||
$siswa['is_banned'] = true;
|
||||
}
|
||||
|
||||
return $siswa;
|
||||
});
|
||||
|
||||
$siswaTelat = $allData->filter(function ($item) use ($now, $allSiswa) {
|
||||
$userData = $allSiswa->firstWhere('id', $item['user_id']);
|
||||
|
||||
if (!$userData)
|
||||
return false;
|
||||
|
||||
$isTelat = $item['tenggat_kembali']->startOfDay()->lt($now->startOfDay());
|
||||
$isBanned = $userData['is_banned'];
|
||||
$isTargetRole = in_array($userData['role'], ['siswa', 'guru']);
|
||||
|
||||
return ($isTelat || $isBanned) && $isTargetRole;
|
||||
})->map(function ($item) use ($now, $allSiswa) {
|
||||
|
||||
// Hitungan Denda
|
||||
$tenggat = $item['tenggat_kembali']->startOfDay();
|
||||
|
||||
// Jika belum telat (tapi banned/manual), hari telat 0
|
||||
$hariTelat = ($tenggat->lt($now->startOfDay()))
|
||||
? $tenggat->diffInDays($now->startOfDay())
|
||||
: 0;
|
||||
|
||||
$totalDenda = $hariTelat * $item['denda_per_hari'];
|
||||
|
||||
$item['hari_terlambat'] = $hariTelat;
|
||||
$item['total_denda'] = $totalDenda;
|
||||
|
||||
// Ambil status is_banned TERBARU dari $allSiswa
|
||||
$dataSiswa = $allSiswa->firstWhere('id', $item['user_id']);
|
||||
$item['is_banned'] = $dataSiswa['is_banned'] ?? false;
|
||||
$item['kelas'] = $dataSiswa['kelas'] ?? 'Guru';
|
||||
|
||||
// Link WA
|
||||
$hp = $item['nomor_hp'];
|
||||
if (substr($hp, 0, 1) == '0')
|
||||
$hp = '62' . substr($hp, 1);
|
||||
|
||||
if ($hariTelat > 0) {
|
||||
$pesan = "Halo {$item['peminjam']}, anda terlambat pengembalian buku. Total Denda: Rp " . number_format($totalDenda, 0, ',', '.');
|
||||
} else {
|
||||
$pesan = "Halo {$item['peminjam']}, akun anda sedang dinonaktifkan sementara. Mohon hubungi petugas.";
|
||||
}
|
||||
$item['wa_link'] = "https://wa.me/{$hp}?text=" . urlencode($pesan);
|
||||
|
||||
return $item;
|
||||
});
|
||||
|
||||
$listKelas = $siswaTelat->pluck('kelas')->unique()->values();
|
||||
|
||||
return view('admin.denda.index', [
|
||||
'pageTitle' => 'Manajemen Denda & Sanksi',
|
||||
'siswaTelat' => $siswaTelat,
|
||||
'listKelas' => $listKelas
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy function untuk tombol Sanksi
|
||||
*/
|
||||
public function berikanSanksi(Request $request)
|
||||
{
|
||||
return response()->json(['status' => 'success']);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,17 +11,31 @@ class DashboardController extends Controller
|
|||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
$bukuPinjam = DummyDataService::getBukuPinjamOffline($user);
|
||||
|
||||
$isTelat = collect($bukuPinjam)->contains(function ($buku) {
|
||||
return $buku['sisa_hari'] < 0;
|
||||
});
|
||||
|
||||
if ($isTelat && $user->role === 'siswa') {
|
||||
$user->is_banned = true;
|
||||
}
|
||||
|
||||
$stats = DummyDataService::getDashboardStats();
|
||||
$pengumuman = DummyDataService::getPengumuman();
|
||||
$pemberitahuan = DummyDataService::getPemberitahuan();
|
||||
$progressMembaca = DummyDataService::getProgressMembaca();
|
||||
$statistikBulanan = DummyDataService::getStatistikBulanan();
|
||||
$bukuPinjamOffline = DummyDataService::getBukuPinjamOffline($user);
|
||||
$bukuPinjamOffline = $bukuPinjam;
|
||||
$bacaBukuOnline = DummyDataService::getBacaBukuOnline($user);
|
||||
$rekomendasiPembelajaran = DummyDataService::getRekomendasiPembelajaran();
|
||||
$personalNotif = DummyDataService::getNotifikasiForUser($user);
|
||||
|
||||
// Cek apakah ada notifikasi denda aktif
|
||||
$dendaAlert = collect($personalNotif)->where('type', 'denda_active');
|
||||
|
||||
// Menambahkan thumbnail YouTube ke setiap rekomendasi
|
||||
$rekomendasiPembelajaran = DummyDataService::getRekomendasiPembelajaran()->map(function ($item) {
|
||||
$rekomendasiPembelajaran = $rekomendasiPembelajaran->map(function ($item) {
|
||||
$videoId = $this->extractYouTubeId($item['youtube_link']);
|
||||
if ($videoId) {
|
||||
$item['thumbnail'] = "https://img.youtube.com/vi/{$videoId}/hqdefault.jpg";
|
||||
|
|
@ -31,30 +45,32 @@ public function index()
|
|||
return $item;
|
||||
});
|
||||
|
||||
$dendaAlert = collect($personalNotif)->where('type', 'denda_active');
|
||||
|
||||
$hour = date('H');
|
||||
$greeting = "Selamat Pagi";
|
||||
if ($hour >= 12 && $hour < 15) {
|
||||
if ($hour >= 12 && $hour < 15)
|
||||
$greeting = "Selamat Siang";
|
||||
} elseif ($hour >= 15 && $hour < 18) {
|
||||
elseif ($hour >= 15 && $hour < 18)
|
||||
$greeting = "Selamat Sore";
|
||||
} elseif ($hour >= 18) {
|
||||
elseif ($hour >= 18)
|
||||
$greeting = "Selamat Malam";
|
||||
}
|
||||
|
||||
return view('dashboard', compact(
|
||||
'user',
|
||||
'stats',
|
||||
'pengumuman',
|
||||
'pemberitahuan',
|
||||
'dendaAlert',
|
||||
'progressMembaca',
|
||||
'statistikBulanan',
|
||||
'bukuPinjamOffline',
|
||||
'bacaBukuOnline',
|
||||
'greeting',
|
||||
'rekomendasiPembelajaran'
|
||||
));
|
||||
))->with('notifikasi', $personalNotif);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function untuk mengekstrak ID video dari URL YouTube.
|
||||
*/
|
||||
|
|
@ -63,4 +79,4 @@ private function extractYouTubeId(string $url): ?string
|
|||
preg_match('/(v=|vi=|youtu.be\/|embed\/|\/v\/|\?v=|\&v=)(.+?)\b/i', $url, $matches);
|
||||
return $matches[2] ?? null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,13 +4,23 @@
|
|||
|
||||
use App\Services\DummyDataService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class KatalogController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
||||
$user = Auth::user();
|
||||
|
||||
$isBanned = false;
|
||||
if ($user && $user->role === 'siswa') {
|
||||
$bukuPinjam = DummyDataService::getBukuPinjamOffline($user);
|
||||
$isTelat = collect($bukuPinjam)->contains(fn($b) => $b['sisa_hari'] < 0);
|
||||
$isBannedManual = $user->is_banned ?? false;
|
||||
$isBanned = $isTelat || $isBannedManual;
|
||||
}
|
||||
|
||||
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
||||
$filterOptions = DummyDataService::getFilterOptions();
|
||||
|
||||
|
|
@ -20,6 +30,7 @@ public function index(Request $request)
|
|||
'input' => $filters,
|
||||
'pageTitle' => 'Katalog Buku',
|
||||
'mode' => 'umum',
|
||||
'isBanned' => $isBanned,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,18 @@ class PeminjamanController extends Controller
|
|||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = \Illuminate\Support\Facades\Auth::user();
|
||||
$bukuPinjam = \App\Services\DummyDataService::getBukuPinjamOffline($user);
|
||||
$isTelat = collect($bukuPinjam)->contains(function ($buku) {
|
||||
return $buku['sisa_hari'] < 0;
|
||||
});
|
||||
|
||||
$isBannedManual = $user->is_banned ?? false;
|
||||
|
||||
if (($isTelat || $isBannedManual) && $user->role === 'siswa') {
|
||||
return redirect()->route('dashboard')->with('error', 'AKSES DITOLAK: Akun Anda sedang dibekukan karena ada buku terlambat!');
|
||||
}
|
||||
|
||||
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
||||
$filters['tipe_akses'] = 'offline';
|
||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@
|
|||
|
||||
use App\Services\DummyDataService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth; // Tambahkan ini
|
||||
|
||||
class RiwayatController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan halaman riwayat peminjaman offline.
|
||||
*/
|
||||
public function offlineIndex()
|
||||
{
|
||||
$riwayatOffline = DummyDataService::getRiwayatOffline();
|
||||
$user = \Illuminate\Support\Facades\Auth::user();
|
||||
if (!$user) $user = (object) ['id' => 1];
|
||||
|
||||
$riwayatOffline = DummyDataService::getRiwayatOffline($user);
|
||||
|
||||
return view('riwayat.offline', [
|
||||
'pageTitle' => 'Riwayat Peminjaman Offline',
|
||||
|
|
@ -20,9 +21,6 @@ public function offlineIndex()
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan halaman riwayat baca online.
|
||||
*/
|
||||
public function onlineIndex()
|
||||
{
|
||||
$riwayatOnline = DummyDataService::getRiwayatOnline();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Providers;
|
||||
|
||||
use App\Services\DummyDataService;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
|
@ -26,18 +27,18 @@ public function boot(): void
|
|||
URL::forceScheme('https');
|
||||
}
|
||||
|
||||
// View Composer untuk semua view (*)
|
||||
View::composer('*', function ($view) {
|
||||
$notifikasi = collect([]);
|
||||
$unreadNotificationsCount = 0;
|
||||
|
||||
// Hanya ambil notifikasi jika ada pengguna yang login
|
||||
if (auth()->check()) {
|
||||
$user = auth()->user();
|
||||
if (Auth::check()) {
|
||||
$user = Auth::user();
|
||||
$notifikasi = collect(DummyDataService::getNotifikasiForUser($user));
|
||||
$unreadNotificationsCount = $notifikasi->where('read', false)->count();
|
||||
$unreadCount = $notifikasi->where('read', false)->count();
|
||||
$view->with('notifikasi', $notifikasi);
|
||||
$view->with('unreadNotificationsCount', $unreadCount);
|
||||
} else {
|
||||
$view->with('notifikasi', collect([]));
|
||||
$view->with('unreadNotificationsCount', 0);
|
||||
}
|
||||
|
||||
$view->with(compact('notifikasi', 'unreadNotificationsCount'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public static function getAllSiswa(): array
|
|||
'role' => 'siswa',
|
||||
'kelas' => 'XII RPL',
|
||||
'golongan' => 'A',
|
||||
'is_banned' => false,
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
|
|
@ -41,6 +42,7 @@ public static function getAllSiswa(): array
|
|||
'role' => 'siswa',
|
||||
'kelas' => 'XII RPL A',
|
||||
'golongan' => 'A',
|
||||
'is_banned' => true,
|
||||
],
|
||||
|
||||
[
|
||||
|
|
@ -53,6 +55,7 @@ public static function getAllSiswa(): array
|
|||
'role' => 'siswa',
|
||||
'kelas' => 'XII RPL A',
|
||||
'golongan' => 'A',
|
||||
'is_banned' => false,
|
||||
],
|
||||
[
|
||||
'id' => 5,
|
||||
|
|
@ -61,6 +64,7 @@ public static function getAllSiswa(): array
|
|||
'email' => 'rina.marlina@smkn1perpus.sch.id',
|
||||
'password' => 'password',
|
||||
'role' => 'guru',
|
||||
'is_banned' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
|
@ -81,157 +85,39 @@ public static function getAktivitasMingguan(): array
|
|||
*/
|
||||
public static function getAdminPeminjamanAktif(): \Illuminate\Support\Collection
|
||||
{
|
||||
return collect([
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-001',
|
||||
'peminjam' => 'Silvi Rahmawati',
|
||||
'nomor_hp' => '08123456789',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(12),
|
||||
'tenggat_kembali' => Carbon::now()->subDays(5), // Terlambat 5 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 5, 'judul' => 'Si Anak Pintar'],
|
||||
['id' => 8, 'judul' => 'Ayah'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-002',
|
||||
'peminjam' => 'Andi Pratama',
|
||||
'nomor_hp' => '081556677889',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(4),
|
||||
'tenggat_kembali' => Carbon::now()->addDays(3), // Sisa 3 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 7, 'judul' => 'The Last Spell Breather'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-003',
|
||||
'peminjam' => 'Siti Nurhaliza',
|
||||
'nomor_hp' => '081998877665',
|
||||
$allBooks = self::getAllBooks();
|
||||
$allSiswa = collect(self::getAllSiswa());
|
||||
$bukuDipinjam = $allBooks->whereIn('status', ['Dipinjam', 'Terlambat']);
|
||||
$grouped = $bukuDipinjam->groupBy(function ($item) {
|
||||
return is_array($item['user_id']) ? $item['user_id'][0] : $item['user_id'];
|
||||
});
|
||||
|
||||
return $grouped->map(function ($books, $userId) use ($allSiswa) {
|
||||
$siswa = $allSiswa->firstWhere('id', $userId);
|
||||
$firstBook = $books->first();
|
||||
$tglKembali = Carbon::now()->addDays($firstBook['sisa_hari']);
|
||||
|
||||
return [
|
||||
'id_peminjaman' => 'PIN-ADM-' . sprintf('%03d', $userId),
|
||||
'user_id' => $userId,
|
||||
'role' => $siswa ? $siswa['role'] : 'siswa',
|
||||
'is_banned' => $siswa ? ($siswa['is_banned'] ?? false) : false,
|
||||
|
||||
'peminjam' => $siswa ? $siswa['nama_lengkap'] : 'User Tidak Dikenal',
|
||||
'nomor_hp' => $siswa ? ($siswa['nomor_hp'] ?? '-') : '-',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(7),
|
||||
'tenggat_kembali' => Carbon::now(), // Hari ini
|
||||
'tenggat_kembali' => $tglKembali,
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 1, 'judul' => 'Modul Ajar IPAS'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-004',
|
||||
'peminjam' => 'Dewi Lestari',
|
||||
'nomor_hp' => '082134567891',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(15),
|
||||
'tenggat_kembali' => Carbon::now()->subDays(8), // Terlambat 8 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 2, 'judul' => 'Modul Ajar Pendidikan Pancasila'],
|
||||
['id' => 6, 'judul' => 'Matematika Dasar'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-005',
|
||||
'peminjam' => 'Eko Prasetyo',
|
||||
'nomor_hp' => '085612345678',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(2),
|
||||
'tenggat_kembali' => Carbon::now()->addDays(5), // Sisa 5 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 9, 'judul' => 'Senja, Hujan, & Cerita yang Telah Usai'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-006',
|
||||
'peminjam' => 'Rina Marlina',
|
||||
'nomor_hp' => '081987654321',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(10),
|
||||
'tenggat_kembali' => Carbon::now()->subDays(3), // Terlambat 3 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 4, 'judul' => 'Modul Pembelajaran Seni Budaya'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-007',
|
||||
'peminjam' => 'Budi Santoso',
|
||||
'nomor_hp' => '082298765432',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(6),
|
||||
'tenggat_kembali' => Carbon::now()->addDays(1), // Sisa 1 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 3, 'judul' => 'Modul Belajar Sosiologi'],
|
||||
['id' => 10, 'judul' => 'Hijrah itu Cinta'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-008',
|
||||
'peminjam' => 'Putri Amelia',
|
||||
'nomor_hp' => '085743219876',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(18),
|
||||
'tenggat_kembali' => Carbon::now()->subDays(11), // Terlambat 11 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 1, 'judul' => 'Modul Ajar IPAS'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-009',
|
||||
'peminjam' => 'Ahmad Haziq',
|
||||
'nomor_hp' => '081345678902',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(3),
|
||||
'tenggat_kembali' => Carbon::now()->addDays(4), // Sisa 4 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 7, 'judul' => 'The Last Spell Breather'],
|
||||
['id' => 8, 'judul' => 'Ayah'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-010',
|
||||
'peminjam' => 'John Wick',
|
||||
'nomor_hp' => '082187654309',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(8),
|
||||
'tenggat_kembali' => Carbon::now()->subDays(1), // Terlambat 1 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 5, 'judul' => 'Si Anak Pintar'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-011',
|
||||
'peminjam' => 'Silvi Rahmawati',
|
||||
'nomor_hp' => '08123456789',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(5),
|
||||
'tenggat_kembali' => Carbon::now()->addDays(2), // Sisa 2 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 2, 'judul' => 'Modul Ajar Pendidikan Pancasila'],
|
||||
['id' => 4, 'judul' => 'Modul Pembelajaran Seni Budaya'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-012',
|
||||
'peminjam' => 'Andi Pratama',
|
||||
'nomor_hp' => '081556677889',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(20),
|
||||
'tenggat_kembali' => Carbon::now()->subDays(13), // Terlambat 13 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 6, 'judul' => 'Matematika Dasar'],
|
||||
]
|
||||
],
|
||||
[
|
||||
'id_peminjaman' => 'PIN-202510-013',
|
||||
'peminjam' => 'Siti Nurhaliza',
|
||||
'nomor_hp' => '081998877665',
|
||||
'tanggal_pinjam' => Carbon::now()->subDays(1),
|
||||
'tenggat_kembali' => Carbon::now()->addDays(6), // Sisa 6 hari
|
||||
'denda_per_hari' => 1000,
|
||||
'books' => [
|
||||
['id' => 9, 'judul' => 'Senja, Hujan, & Cerita yang Telah Usai'],
|
||||
['id' => 10, 'judul' => 'Hijrah itu Cinta'],
|
||||
]
|
||||
],
|
||||
]);
|
||||
|
||||
'books' => $books->map(function ($b) {
|
||||
return [
|
||||
'id' => $b['id'],
|
||||
'judul' => $b['judul'],
|
||||
'cover' => $b['cover']
|
||||
];
|
||||
})->toArray()
|
||||
];
|
||||
})->values();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -328,7 +214,7 @@ public static function getSiswaTeraktif(): array
|
|||
['nama' => 'Rina Marlina', 'total_buku' => 20, 'kelas' => 'XII TKJ A'],
|
||||
['nama' => 'Budi Santoso', 'total_buku' => 18, 'kelas' => 'X OTKP'],
|
||||
['nama' => 'Putri Amelia', 'total_buku' => 16, 'kelas' => 'XI RPL C'],
|
||||
['nama' => 'Ahmad Haziq', 'total_buku' => 12, 'kelas' => 'X TKJ B'],
|
||||
['nama' => 'Ahmad Jono', 'total_buku' => 12, 'kelas' => 'X TKJ B'],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -483,158 +369,157 @@ public static function getStatistikBulanan(): array
|
|||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public static function getAllBooks()
|
||||
{
|
||||
return collect([
|
||||
[
|
||||
'id' => 1,
|
||||
'judul' => 'Modul Ajar IPAS',
|
||||
'penulis' => 'Tim Kemdikbud Ristek',
|
||||
'cover' => 'images/covers/ipas.jpg',
|
||||
'kode_buku' => '510', // 500 (Sains)
|
||||
'kategori' => 'Sains',
|
||||
'tahun' => 2022,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => ['online', 'offline'],
|
||||
'file_pdf' => 'ipas.pdf',
|
||||
'progress' => 75,
|
||||
'sisa_hari' => 14,
|
||||
'user_id' => 1,
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'judul' => 'Modul Ajar Pendidikan Pancasila',
|
||||
'penulis' => 'Tim Guru Pancasila',
|
||||
'cover' => 'images/covers/pancasila.jpg',
|
||||
'kode_buku'=> '370', // 370 (Pendidikan, bagian dari 300-Sosial)
|
||||
'kategori' => 'Pendidikan',
|
||||
'tahun' => 2023,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => false,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => 3,
|
||||
'progress' => 100,
|
||||
'user_id' => [3, 1],
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'judul' => 'Modul Belajar Sosiologi',
|
||||
'penulis' => 'Tim Cendekia',
|
||||
'cover' => 'images/covers/sosiologi.jpg',
|
||||
'kode_buku' => '340', // 300 (Ilmu Sosial)
|
||||
'kategori' => 'Sosial',
|
||||
'tahun' => 2021,
|
||||
'status' => 'Dipinjam',
|
||||
'is_new' => false,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => 8,
|
||||
'user_id' => 3,
|
||||
],
|
||||
[
|
||||
'id' => 4,
|
||||
'judul' => 'Modul Pembelajaran Seni Budaya',
|
||||
'penulis' => 'Cahya Wulan, S.Pd.',
|
||||
'cover' => 'images/covers/senbud.jpg',
|
||||
'kode_buku' => '752', // 700 (Seni)
|
||||
'kategori' => 'Seni',
|
||||
'tahun' => 2022,
|
||||
'status' => 'Dipinjam',
|
||||
'is_new' => false,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => 14,
|
||||
'user_id' => [1, 3],
|
||||
],
|
||||
[
|
||||
'id' => 5,
|
||||
'judul' => 'Si Anak Pintar',
|
||||
'penulis' => 'Tere Liye',
|
||||
'cover' => 'images/covers/sianakpintar.jpg',
|
||||
'kode_buku' => '843', // 800 (Fiksi/Sastra)
|
||||
'kategori' => 'Fiksi',
|
||||
'tahun' => 2018,
|
||||
'status' => 'Dipinjam',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => 5,
|
||||
'user_id' => 1,
|
||||
],
|
||||
[
|
||||
'id' => 6,
|
||||
'judul' => 'Matematika Dasar',
|
||||
'penulis' => 'Prof. Dr. Matematikus',
|
||||
'cover' => 'images/covers/mtk.jpg',
|
||||
'kode_buku' => '374', // 370 (Pendidikan)
|
||||
'kategori' => 'Pendidikan',
|
||||
'tahun' => 2023,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => ['online', 'offline'],
|
||||
'file_pdf' => 'mtk.pdf',
|
||||
'sisa_hari' => 7,
|
||||
'progress' => 40,
|
||||
'user_id' => [1, 4, 5],
|
||||
],
|
||||
[
|
||||
'id' => 7,
|
||||
'judul' => 'The Last Spell Breather',
|
||||
'penulis' => 'Julie Pike',
|
||||
'cover' => 'images/covers/thelastspellbreather.jpg',
|
||||
'kode_buku' => '834', // 800 (Fantasi/Sastra)
|
||||
'kategori' => 'Fantasi',
|
||||
'tahun' => 2024,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => 4,
|
||||
'progress' => 0,
|
||||
'user_id' => [3, 1]
|
||||
],
|
||||
[
|
||||
'id' => 8,
|
||||
'judul' => 'Ayah',
|
||||
'penulis' => 'Andrea Hirata',
|
||||
'cover' => 'images/covers/ayah.png',
|
||||
'kategori' => 'Novel',
|
||||
'tahun' => 2015,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => 'online',
|
||||
'file_pdf' => 'ayah.pdf',
|
||||
'progress' => 0,
|
||||
'user_id' => [1, 2, 3],
|
||||
],
|
||||
[
|
||||
'id' => 9,
|
||||
'judul' => 'Senja, Hujan, & Cerita yang Telah Usai',
|
||||
'penulis' => 'Boy Candra',
|
||||
'cover' => 'images/covers/senja.png',
|
||||
'kode_buku' => '845', // 800 (Novel/Sastra)
|
||||
'kategori' => 'Novel',
|
||||
'tahun' => 2015,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => ['online', 'offline'],
|
||||
'file_pdf' => 'senja.pdf',
|
||||
'progress' => 0,
|
||||
'sisa_hari' => 14,
|
||||
'user_id' => [1, 3],
|
||||
],
|
||||
[
|
||||
'id' => 10,
|
||||
'judul' => 'Hijrah itu Cinta',
|
||||
'penulis' => 'Abay Adhitya',
|
||||
'cover' => 'images/covers/hijrah.png',
|
||||
'kategori' => 'Religi',
|
||||
'tahun' => 2018,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => 'online',
|
||||
'file_pdf' => 'hijrah.pdf',
|
||||
'progress' => 0,
|
||||
'user_id' => [2, 3],
|
||||
]
|
||||
]);
|
||||
}
|
||||
{
|
||||
return collect([
|
||||
// --- BUKU MILIK USER 1 (SILVI) ---
|
||||
[
|
||||
'id' => 1,
|
||||
'judul' => 'Modul Ajar IPAS',
|
||||
'penulis' => 'Tim Kemdikbud Ristek',
|
||||
'cover' => 'images/covers/ipas.jpg',
|
||||
'kode_buku' => '510',
|
||||
'kategori' => 'Sains',
|
||||
'tahun' => 2022,
|
||||
'status' => 'Dipinjam',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => ['online', 'offline'],
|
||||
'sisa_hari' => -5,
|
||||
'user_id' => 1,
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'judul' => 'Perahu Kertas',
|
||||
'penulis' => 'Dewi Lestari',
|
||||
'cover' => 'images/covers/ayah.png',
|
||||
'kode_buku' => '844',
|
||||
'kategori' => 'Fiksi',
|
||||
'tahun' => 2012,
|
||||
'status' => 'Dipinjam',
|
||||
'is_new' => false,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => 3,
|
||||
'user_id' => 1,
|
||||
],
|
||||
|
||||
// --- BUKU TERSEDIA (UNTUK CEK PEMINJAMAN BARU) ---
|
||||
[
|
||||
'id' => 3,
|
||||
'judul' => 'Si Anak Pintar',
|
||||
'penulis' => 'Tere Liye',
|
||||
'cover' => 'images/covers/sianakpintar.jpg',
|
||||
'kode_buku' => '843',
|
||||
'kategori' => 'Fiksi',
|
||||
'tahun' => 2018,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => 0,
|
||||
'user_id' => null,
|
||||
],
|
||||
[
|
||||
'id' => 4,
|
||||
'judul' => 'Laskar Pelangi',
|
||||
'penulis' => 'Andrea Hirata',
|
||||
'cover' => 'https://upload.wikimedia.org/wikipedia/id/8/8e/Laskar_pelangi_sampul.jpg',
|
||||
'kode_buku' => 'NOV-001',
|
||||
'kategori' => 'Novel',
|
||||
'tahun' => 2005,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => false,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => 0,
|
||||
'user_id' => null,
|
||||
],
|
||||
[
|
||||
'id' => 5,
|
||||
'judul' => 'Filosofi Teras',
|
||||
'penulis' => 'Henry Manampiring',
|
||||
'cover' => 'https://upload.wikimedia.org/wikipedia/id/3/36/Filosofi_Teras.jpg',
|
||||
'kode_buku' => 'PSI-002',
|
||||
'kategori' => 'Psikologi',
|
||||
'tahun' => 2018,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => 0,
|
||||
'user_id' => null,
|
||||
],
|
||||
[
|
||||
'id' => 6,
|
||||
'judul' => 'Atomic Habits',
|
||||
'penulis' => 'James Clear',
|
||||
'cover' => 'https://images-na.ssl-images-amazon.com/images/I/91bYsX41DVL.jpg',
|
||||
'kode_buku' => 'SELF-003',
|
||||
'kategori' => 'Refleksi Diri',
|
||||
'tahun' => 2018,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => ['offline', 'online'],
|
||||
'sisa_hari' => 0,
|
||||
'user_id' => null,
|
||||
],
|
||||
[
|
||||
'id' => 7,
|
||||
'judul' => 'Bumi Manusia',
|
||||
'penulis' => 'Pramoedya Ananta Toer',
|
||||
'cover' => 'https://upload.wikimedia.org/wikipedia/id/4/44/Bumi_Manusia.jpg',
|
||||
'kode_buku' => 'SAS-004',
|
||||
'kategori' => 'Sastra',
|
||||
'tahun' => 1980,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => false,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => 0,
|
||||
'user_id' => null,
|
||||
],
|
||||
[
|
||||
'id' => 8,
|
||||
'judul' => 'Laut Bercerita',
|
||||
'penulis' => 'Leila S. Chudori',
|
||||
'cover' => 'https://upload.wikimedia.org/wikipedia/id/6/6d/Laut_Bercerita.jpeg',
|
||||
'kode_buku' => 'NOV-005',
|
||||
'kategori' => 'Novel',
|
||||
'tahun' => 2017,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => true,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => 0,
|
||||
'user_id' => null,
|
||||
],
|
||||
// --- BUKU MILIK USER 3 (SITI) - TELAT ---
|
||||
[
|
||||
'id' => 99,
|
||||
'judul' => 'Seni Berbicara',
|
||||
'penulis' => 'Larry King',
|
||||
'cover' => 'https://via.placeholder.com/150',
|
||||
'kode_buku' => 'COM-001',
|
||||
'kategori' => 'Refleksi Diri',
|
||||
'tahun' => 2019,
|
||||
'status' => 'Dipinjam',
|
||||
'is_new' => false,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => -2,
|
||||
'user_id' => 3,
|
||||
],
|
||||
|
||||
// --- BUKU MILIK GURU (USER 5) - TESTING MANUAL BAN ---
|
||||
[
|
||||
'id' => 88,
|
||||
'judul' => 'Strategi Pembelajaran Abad 21',
|
||||
'penulis' => 'Prof. Dr. Pendidikan',
|
||||
'cover' => 'https://via.placeholder.com/150',
|
||||
'kode_buku' => 'GURU-001',
|
||||
'kategori' => 'Pendidikan',
|
||||
'tahun' => 2020,
|
||||
'status' => 'Dipinjam',
|
||||
'is_new' => false,
|
||||
'tipe_akses' => 'offline',
|
||||
'sisa_hari' => -3,
|
||||
'user_id' => 5,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Data untuk buku pinjam offline
|
||||
|
|
@ -642,9 +527,16 @@ public static function getAllBooks()
|
|||
public static function getBukuPinjamOffline($user): array
|
||||
{
|
||||
return self::getAllBooks()
|
||||
->where('tipe_akses', 'offline')
|
||||
->filter(function ($buku) {
|
||||
// Handle tipe akses string atau array
|
||||
if (is_array($buku['tipe_akses'])) {
|
||||
return in_array('offline', $buku['tipe_akses']);
|
||||
}
|
||||
return $buku['tipe_akses'] === 'offline';
|
||||
})
|
||||
->filter(function ($buku) use ($user) {
|
||||
if (!isset($buku['user_id'])) return false;
|
||||
if (!isset($buku['user_id']) || $buku['user_id'] === null)
|
||||
return false;
|
||||
|
||||
if (is_array($buku['user_id'])) {
|
||||
return in_array($user->id, $buku['user_id']);
|
||||
|
|
@ -669,7 +561,8 @@ public static function getBacaBukuOnline($user): array
|
|||
return self::getAllBooks()
|
||||
->where('tipe_akses', 'online')
|
||||
->filter(function ($buku) use ($user) {
|
||||
if (!isset($buku['user_id'])) return false;
|
||||
if (!isset($buku['user_id']))
|
||||
return false;
|
||||
|
||||
if (is_array($buku['user_id'])) {
|
||||
return in_array($user->id, $buku['user_id']);
|
||||
|
|
@ -738,52 +631,47 @@ public static function getFilterOptions(): array
|
|||
* Data untuk riwayat peminjaman offline.
|
||||
* Setiap item mewakili satu transaksi peminjaman.
|
||||
*/
|
||||
public static function getRiwayatOffline(): array
|
||||
public static function getRiwayatOffline($user): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'id' => 1,
|
||||
'id_peminjaman' => 'PIN-20240520-001',
|
||||
'kode_buku' => '510',
|
||||
'judul_utama' => 'Modul Ajar IPAS',
|
||||
'tanggal_pinjam' => '20/05/2024',
|
||||
'tanggal_kembali' => '27/05/2024',
|
||||
'status' => 'Dikembalikan',
|
||||
$allBooks = self::getAllBooks();
|
||||
$myBooks = $allBooks->filter(function ($buku) use ($user) {
|
||||
if (is_array($buku['user_id'])) {
|
||||
return in_array($user->id, $buku['user_id']);
|
||||
}
|
||||
return $buku['user_id'] == $user->id;
|
||||
})->whereIn('status', ['Dipinjam', 'Dikembalikan', 'Terlambat']);
|
||||
|
||||
// Mapping ke format tampilan View
|
||||
return $myBooks->map(function ($buku, $index) {
|
||||
|
||||
// Logika Tanggal Dummy
|
||||
$tglPinjam = Carbon::now()->subDays(7);
|
||||
|
||||
// Hitung tanggal kembali berdasarkan sisa hari di Master Data
|
||||
$tglKembali = Carbon::now()->addDays($buku['sisa_hari']);
|
||||
|
||||
return [
|
||||
'id' => $index + 1,
|
||||
'id_peminjaman' => 'PIN-' . date('Ym') . '-' . sprintf('%03d', $buku['id']),
|
||||
'kode_buku' => $buku['kode_buku'],
|
||||
'judul_utama' => $buku['judul'],
|
||||
'tanggal_pinjam' => $tglPinjam->format('d/m/Y'),
|
||||
'tanggal_kembali' => $tglKembali->format('d/m/Y'),
|
||||
'status' => $buku['status'],
|
||||
'books' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'judul' => 'Modul Ajar IPAS',
|
||||
'kode_buku' => '510',
|
||||
'cover' => 'images/covers/ipas.jpg',
|
||||
'deskripsi' => 'Buku ini berisi ajakan kepada anak-anak untuk semangat pergi ke sekolah dan menuntut ilmu.',
|
||||
'kategori' => 'Pendidikan',
|
||||
'tahun' => 2022,
|
||||
'keterangan' => 'Buku dikembalikan dalam kondisi baik. Terdapat denda keterlambatan 2 hari: Rp 2.000,-'
|
||||
'id' => $buku['id'],
|
||||
'judul' => $buku['judul'],
|
||||
'kode_buku' => $buku['kode_buku'],
|
||||
'cover' => $buku['cover'],
|
||||
'deskripsi' => 'Deskripsi buku ' . $buku['judul'],
|
||||
'kategori' => $buku['kategori'],
|
||||
'tahun' => $buku['tahun'],
|
||||
'keterangan' => ($buku['sisa_hari'] < 0) ? 'Buku Terlambat' : null,
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'id_peminjaman' => 'PIN-20240527-002',
|
||||
'kode_buku' => '844',
|
||||
'judul_utama' => 'Perahu Kertas',
|
||||
'tanggal_pinjam' => '27/05/2024',
|
||||
'tanggal_kembali' => '04/06/2024',
|
||||
'status' => 'Dipinjam',
|
||||
'books' => [
|
||||
[
|
||||
'id' => 8,
|
||||
'judul' => 'Perahu Kertas',
|
||||
'kode_buku' => '844',
|
||||
'cover' => 'images/covers/ayah.png',
|
||||
'deskripsi' => 'Cerita penggambaran pasang surut hubungan dua anak manusia, yaitu Kugy dan Keenan.',
|
||||
'kategori' => 'Fiksi',
|
||||
'tahun' => 2022,
|
||||
'keterangan' => null,
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
];
|
||||
})->values()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -821,79 +709,62 @@ public static function getRiwayatOnline(): array
|
|||
public static function getNotifikasiForUser($user): array
|
||||
{
|
||||
$notifikasi = [];
|
||||
$allBooks = self::getAllBooks();
|
||||
$myBooks = $allBooks->filter(function ($buku) use ($user) {
|
||||
if (!isset($buku['user_id']) || $buku['user_id'] === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$bukuPinjaman = self::getBukuPinjamOffline($user);
|
||||
if (is_array($buku['user_id'])) {
|
||||
return in_array($user->id, $buku['user_id']);
|
||||
}
|
||||
return $buku['user_id'] == $user->id;
|
||||
});
|
||||
|
||||
if (!empty($bukuPinjaman)) {
|
||||
$bukuTerbaru = $bukuPinjaman[0];
|
||||
$notifikasi[] = [
|
||||
'icon' => 'bi-check2-circle',
|
||||
'color' => 'success',
|
||||
'title' => 'Buku "' . $bukuTerbaru['judul'] . '" berhasil dipinjam.',
|
||||
'time' => '5 menit yang lalu',
|
||||
'read' => false,
|
||||
'type' => 'riwayat_peminjaman',
|
||||
'link_id' => null,
|
||||
];
|
||||
}
|
||||
foreach ($myBooks as $buku) {
|
||||
// LOGIC TELAT
|
||||
if ($buku['sisa_hari'] < 0 && $buku['status'] == 'Dipinjam') {
|
||||
$hariTelat = abs($buku['sisa_hari']);
|
||||
$denda = $hariTelat * 1000;
|
||||
|
||||
|
||||
foreach ($bukuPinjaman as $buku) {
|
||||
if ($buku['sisa_hari'] <= 3) {
|
||||
$notifikasi[] = [
|
||||
'icon' => 'bi-exclamation-triangle',
|
||||
'icon' => 'bi-exclamation-octagon-fill',
|
||||
'color' => 'danger',
|
||||
'title' => 'Buku "' . $buku['judul'] . '" akan jatuh tempo!',
|
||||
'time' => '1 jam yang lalu',
|
||||
'title' => 'TERLAMBAT: ' . $buku['judul'],
|
||||
'content' => "Telat {$hariTelat} hari. Denda: Rp " . number_format($denda, 0, ',', '.'),
|
||||
'time' => 'Sekarang',
|
||||
'read' => false,
|
||||
'type' => 'riwayat_peminjaman',
|
||||
'type' => 'denda_active',
|
||||
'link_id' => null,
|
||||
];
|
||||
}
|
||||
// LOGIC JATUH TEMPO
|
||||
elseif ($buku['sisa_hari'] >= 0 && $buku['sisa_hari'] <= 3 && $buku['status'] == 'Dipinjam') {
|
||||
$notifikasi[] = [
|
||||
'icon' => 'bi-exclamation-triangle-fill',
|
||||
'color' => 'warning',
|
||||
'title' => 'Jatuh Tempo: ' . $buku['judul'],
|
||||
'content' => "Sisa waktu tinggal " . $buku['sisa_hari'] . " hari lagi.",
|
||||
'time' => 'Segera',
|
||||
'read' => false,
|
||||
'type' => 'warning_jatuh_tempo',
|
||||
'link_id' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($user->role === 'guru') {
|
||||
$notifikasi[] = [
|
||||
'icon' => 'bi-lightbulb-fill',
|
||||
'color' => 'success',
|
||||
'title' => 'Rekomendasi pembelajaran baru telah ditambahkan.',
|
||||
'time' => 'Kemarin',
|
||||
'read' => true,
|
||||
'type' => 'rekomendasi',
|
||||
'link_id' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
$notifikasiUmum = [
|
||||
[
|
||||
'icon' => 'bi-book-half',
|
||||
'color' => 'info',
|
||||
'title' => 'Buku baru ditambahkan ke kategori Fiksi.',
|
||||
'time' => '3 jam yang lalu',
|
||||
'read' => false,
|
||||
'type' => 'katalog_kategori',
|
||||
'link_id' => 'Fiksi',
|
||||
],
|
||||
[
|
||||
'icon' => 'bi-megaphone-fill',
|
||||
'color' => 'warning',
|
||||
'title' => 'Perpustakaan akan tutup lebih awal pada hari Jumat.',
|
||||
'time' => '1 hari yang lalu',
|
||||
'read' => true,
|
||||
'type' => 'halaman_profil',
|
||||
'link_id' => null,
|
||||
],
|
||||
[
|
||||
'icon' => 'bi-calendar-event',
|
||||
'color' => 'primary',
|
||||
'title' => 'Acara "Bedah Buku" akan diadakan minggu depan.',
|
||||
'time' => '2 hari yang lalu',
|
||||
'read' => true,
|
||||
'type' => 'halaman_profil',
|
||||
'link_id' => null,
|
||||
],
|
||||
// Notif Default
|
||||
$notifikasi[] = [
|
||||
'icon' => 'bi-info-circle-fill',
|
||||
'color' => 'primary',
|
||||
'title' => 'Selamat Datang di DigiPus!',
|
||||
'content' => 'Silakan jelajahi koleksi buku terbaru kami.',
|
||||
'time' => 'Baru saja',
|
||||
'read' => true,
|
||||
'type' => 'info',
|
||||
'link_id' => null,
|
||||
];
|
||||
|
||||
return array_merge($notifikasi, $notifikasiUmum);
|
||||
return $notifikasi;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -825,7 +825,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
|
|
@ -2590,7 +2589,6 @@
|
|||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz",
|
||||
"integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.0",
|
||||
"immutable": "^5.0.2",
|
||||
|
|
@ -2677,8 +2675,7 @@
|
|||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz",
|
||||
"integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.0",
|
||||
|
|
@ -2735,7 +2732,6 @@
|
|||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
|
@ -2779,7 +2775,6 @@
|
|||
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
|
|
@ -2884,7 +2879,6 @@
|
|||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ if (formPeminjamanElement) {
|
|||
const bukuAwal = JSON.parse(formPeminjamanElement.dataset.bukuAwal);
|
||||
|
||||
let bukuTerpilih = [bukuAwal.id];
|
||||
const maxBuku = 3;
|
||||
const maxBuku = 2;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
updateCounter();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,248 @@
|
|||
<x-app-layout>
|
||||
@section('page-title', 'Manajemen Denda')
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h3 class="my-0 fw-bold">Manajemen Denda & Sanksi</h3>
|
||||
<p class="text-muted mb-0">Pantau siswa terlambat dan berikan sanksi jika diperlukan.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- FILTER SECTION --}}
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-body py-3">
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col-md-auto">
|
||||
<span class="fw-bold text-muted"><i class="bi bi-funnel-fill me-1"></i> Filter Data:</span>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select id="filterKelas" class="form-select form-select-sm">
|
||||
<option value="">Semua Kelas</option>
|
||||
@foreach ($listKelas as $kelas)
|
||||
<option value="{{ $kelas }}">{{ $kelas }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select id="filterBesaran" class="form-select form-select-sm">
|
||||
<option value="">Semua Nominal</option>
|
||||
<option value="ringan">Denda Ringan (< Rp 10.000)</option>
|
||||
<option value="berat">Denda Berat (> Rp 10.000)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-auto ms-auto">
|
||||
<button id="resetFilter" class="btn btn-sm btn-light border text-muted">
|
||||
<i class="bi bi-arrow-counterclockwise"></i> Reset
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="dendaTable" class="table table-striped table-hover align-middle" style="width:100%">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th class="text-center" width="5%">NO</th>
|
||||
<th width="25%">SISWA & KELAS</th>
|
||||
<th width="25%">BUKU TERLAMBAT</th>
|
||||
<th width="15%">STATUS</th>
|
||||
<th width="15%">DENDA</th>
|
||||
<th width="15%">AKSI</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($siswaTelat as $item)
|
||||
<tr>
|
||||
<td class="text-center">{{ $loop->iteration }}</td>
|
||||
<td>
|
||||
<div class="fw-bold text-dark">{{ $item['peminjam'] }}</div>
|
||||
<span
|
||||
class="badge bg-light text-secondary border mt-1 mb-1">{{ $item['kelas'] }}</span>
|
||||
<div class="small text-muted"><i
|
||||
class="bi bi-telephone me-1"></i>{{ $item['nomor_hp'] }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul class="mb-0 ps-3 small text-muted">
|
||||
@foreach ($item['books'] as $buku)
|
||||
<li>{{ $buku['judul'] }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</td>
|
||||
<td data-order="{{ $item['tenggat_kembali']->timestamp }}">
|
||||
<span
|
||||
class="badge bg-danger-subtle text-danger border border-danger-subtle rounded-pill px-3">
|
||||
Telat {{ $item['hari_terlambat'] }} Hari
|
||||
</span>
|
||||
<div class="small text-muted mt-1">
|
||||
Tenggat: {{ $item['tenggat_kembali']->format('d/m/Y') }}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td data-order="{{ $item['total_denda'] }}">
|
||||
<div class="fw-bold text-danger">Rp
|
||||
{{ number_format($item['total_denda'], 0, ',', '.') }}</div>
|
||||
<small class="text-muted" style="font-size: 0.75rem;">Rp 1.000/hari</small>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex gap-2">
|
||||
{{-- Tombol WA --}}
|
||||
<a href="{{ $item['wa_link'] }}" target="_blank"
|
||||
class="btn btn-sm btn-success text-white" title="Tagih via WhatsApp">
|
||||
<i class="bi bi-whatsapp"></i>
|
||||
</a>
|
||||
|
||||
{{-- LOGIC TOMBOL AKTIFKAN / SANKSI --}}
|
||||
@if ($item['is_banned'])
|
||||
{{-- Jika sudah dibekukan (Otomatis/Manual), muncul tombol AKTIFKAN --}}
|
||||
<button class="btn btn-sm btn-outline-success btn-aktifkan"
|
||||
data-nama="{{ $item['peminjam'] }}" title="Aktifkan Kembali Akun">
|
||||
<i class="bi bi-shield-check"></i> Aktifkan
|
||||
</button>
|
||||
@else
|
||||
{{-- Jika belum dibekukan, muncul tombol SANKSI (Manual) --}}
|
||||
<button class="btn btn-sm btn-outline-danger btn-sanksi"
|
||||
data-nama="{{ $item['peminjam'] }}" title="Berikan Sanksi">
|
||||
<i class="bi bi-slash-circle"></i> Sanksi
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="text-center py-5">
|
||||
<div class="text-muted opacity-50">
|
||||
<i class="bi bi-emoji-smile fs-1 mb-2 d-block"></i>
|
||||
<p class="mb-0">Tidak ada siswa yang terlambat hari ini.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var table = $('#dendaTable').DataTable({
|
||||
order: [
|
||||
[3, 'desc']
|
||||
],
|
||||
|
||||
columnDefs: [{
|
||||
"searchable": false,
|
||||
"orderable": false,
|
||||
"targets": 0
|
||||
}],
|
||||
|
||||
language: {
|
||||
search: "_INPUT_",
|
||||
searchPlaceholder: "Cari nama siswa..."
|
||||
},
|
||||
dom: 'rtip'
|
||||
});
|
||||
|
||||
table.on('order.dt search.dt', function() {
|
||||
table.column(0, {
|
||||
search: 'applied',
|
||||
order: 'applied'
|
||||
}).nodes().each(function(cell, i) {
|
||||
cell.innerHTML = i + 1;
|
||||
});
|
||||
}).draw();
|
||||
|
||||
$.fn.dataTable.ext.search.push(
|
||||
function(settings, data, dataIndex) {
|
||||
var filterKelas = $('#filterKelas').val();
|
||||
var filterBesaran = $('#filterBesaran').val();
|
||||
var dataSiswa = data[1] || "";
|
||||
var dataDendaRaw = data[4] || "0";
|
||||
var dataDenda = parseInt(dataDendaRaw.replace(/[^0-9]/g, ''), 10);
|
||||
|
||||
if (filterKelas !== "" && !dataSiswa.includes(filterKelas)) return false;
|
||||
if (filterBesaran === "ringan" && dataDenda >= 10000) return false;
|
||||
if (filterBesaran === "berat" && dataDenda < 10000) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
$('#filterKelas, #filterBesaran').change(function() {
|
||||
table.draw();
|
||||
});
|
||||
$('#resetFilter').click(function() {
|
||||
$('#filterKelas').val('');
|
||||
$('#filterBesaran').val('');
|
||||
table.draw();
|
||||
});
|
||||
});
|
||||
|
||||
// --- LOGIC TOMBOL SANKSI ---
|
||||
$(document).on('click', '.btn-sanksi', function() {
|
||||
const nama = $(this).data('nama');
|
||||
modernSwal.fire({
|
||||
title: 'Nonaktifkan Guru?',
|
||||
text: `Apakah Anda yakin ingin memberikan sanksi pembekuan akun kepada ${nama}?`,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Ya, Bekukan',
|
||||
confirmButtonColor: '#dc3545',
|
||||
cancelButtonText: 'Batal'
|
||||
}).then((result) => {
|
||||
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.`
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Logic Tombol Aktifkan (Unban)
|
||||
$(document).on('click', '.btn-aktifkan', function() {
|
||||
const nama = $(this).data('nama');
|
||||
|
||||
modernSwal.fire({
|
||||
title: 'Aktifkan Akun?',
|
||||
text: `Pastikan ${nama} sudah melunasi denda. Akun akan diaktifkan kembali.`,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Ya, Aktifkan',
|
||||
confirmButtonColor: '#198754',
|
||||
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('<span class="badge bg-success">Aktif</span>');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<form action="#" method="POST" id="formPeminjaman" autocomplete="off"> @csrf
|
||||
<div class="row g-4">
|
||||
|
||||
|
||||
<div class="col-lg-7">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
|
|
@ -18,20 +18,24 @@
|
|||
<h5 class="fw-bold mb-3">Data Peminjaman</h5>
|
||||
<div class="mb-3">
|
||||
<label for="peminjam_id" class="form-label">Pilih Peminjam (Siswa/Guru)</label>
|
||||
|
||||
|
||||
<select class="form-control" id="peminjam_id" name="peminjam_id"
|
||||
placeholder="Cari nama atau email...">
|
||||
<option value="">Pilih...</option>
|
||||
@foreach ($groupedUsers as $role => $users)
|
||||
<optgroup label="{{ ucfirst($role) }}">
|
||||
@foreach ($users as $user)
|
||||
<option value="{{ $user['id'] }}">{{ $user['nama_lengkap'] }}</option>
|
||||
{{-- Logic Disable User --}}
|
||||
<option value="{{ $user['id'] }}"
|
||||
{{ $user['disabled'] ? 'disabled' : '' }}>
|
||||
{{ $user['nama_lengkap'] }} {{ $user['status_text'] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</optgroup>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="tanggal_pinjam" class="form-label">Tanggal Pinjam</label>
|
||||
|
|
@ -40,8 +44,8 @@
|
|||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="tanggal_kembali" class="form-label">Tenggat Kembali</label>
|
||||
<input type="text" class="form-control" id="tanggal_kembali"
|
||||
name="tanggal_kembali" placeholder="Pilih tenggat kembali">
|
||||
<input type="text" class="form-control" id="tanggal_kembali" name="tanggal_kembali"
|
||||
placeholder="Pilih tenggat kembali">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -51,13 +55,13 @@
|
|||
<h5 class="fw-bold m-0">Buku Terpilih</h5>
|
||||
<span class="badge bg-primary-soft" id="counterBuku">0 Buku</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="daftarBukuPinjam" class="mb-3">
|
||||
<div id="emptyStateBuku" class="text-center text-muted py-4">
|
||||
<i class="bi bi-collection fs-1" style="opacity: 0.25;"></i>
|
||||
<p class="mb-0 mt-2">Belum ada buku yang dipilih.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="hiddenInputs"></div>
|
||||
|
||||
|
|
@ -66,7 +70,7 @@
|
|||
<i class="bi bi-save me-1"></i> Simpan Peminjaman
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -96,19 +100,16 @@
|
|||
<div class="d-flex align-items-center book-item-list"
|
||||
onclick="toggleBookSelection(this, {{ $buku['id'] }})"
|
||||
style="cursor: pointer;">
|
||||
<img src="{{ asset($buku['cover']) }}" alt="Cover"
|
||||
class="rounded me-3"
|
||||
<img src="{{ asset($buku['cover']) }}" alt="Cover" class="rounded me-3"
|
||||
style="width: 50px; height: 70px; object-fit: cover;">
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="fw-bold mb-1 line-clamp-2">{{ $buku['judul'] }}</h6>
|
||||
<p class="text-muted small mb-1">{{ $buku['penulis'] }}</p>
|
||||
<span
|
||||
class="badge bg-info-soft">{{ $buku['kategori'] }}</span>
|
||||
<span class="badge bg-info-soft">{{ $buku['kategori'] }}</span>
|
||||
</div>
|
||||
<div class="form-check ms-3">
|
||||
<input class="form-check-input book-checkbox" type="checkbox"
|
||||
value="{{ $buku['id'] }}"
|
||||
id="book-check-{{ $buku['id'] }}"
|
||||
value="{{ $buku['id'] }}" id="book-check-{{ $buku['id'] }}"
|
||||
style="pointer-events: none;">
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -125,10 +126,7 @@ class="badge bg-info-soft">{{ $buku['kategori'] }}</span>
|
|||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
// Inisialisasi Library & Simpan Instansinya
|
||||
|
||||
const tomSelect = new TomSelect("#peminjam_id", {
|
||||
const tomSelect = new TomSelect("#peminjam_id", {
|
||||
create: false,
|
||||
sortField: {
|
||||
field: "text",
|
||||
|
|
@ -136,12 +134,12 @@ class="badge bg-info-soft">{{ $buku['kategori'] }}</span>
|
|||
}
|
||||
});
|
||||
|
||||
const tglPinjam = flatpickr("#tanggal_pinjam", {
|
||||
dateFormat: "Y-m-d",
|
||||
const tglPinjam = flatpickr("#tanggal_pinjam", {
|
||||
dateFormat: "Y-m-d",
|
||||
altInput: true,
|
||||
altFormat: "d/m/Y",
|
||||
altFormat: "d/m/Y",
|
||||
defaultDate: "today",
|
||||
locale: "id",
|
||||
locale: "id",
|
||||
onChange: function(selectedDates, dateStr) {
|
||||
if (selectedDates.length > 0) {
|
||||
tglKembali.set("minDate", new Date(selectedDates[0]).fp_incr(1));
|
||||
|
|
@ -149,19 +147,18 @@ class="badge bg-info-soft">{{ $buku['kategori'] }}</span>
|
|||
}
|
||||
});
|
||||
|
||||
const tglKembali = flatpickr("#tanggal_kembali", {
|
||||
const tglKembali = flatpickr("#tanggal_kembali", {
|
||||
dateFormat: "Y-m-d",
|
||||
altInput: true,
|
||||
altFormat: "d/m/Y",
|
||||
defaultDate: new Date().fp_incr(7),
|
||||
altFormat: "d/m/Y",
|
||||
defaultDate: new Date().fp_incr(7),
|
||||
locale: "id",
|
||||
minDate: new Date().fp_incr(1)
|
||||
});
|
||||
|
||||
|
||||
// Logika Pemilihan Buku
|
||||
|
||||
const MAX_BOOKS = 3;
|
||||
const MAX_BOOKS = 2;
|
||||
let selectedBookIds = new Set();
|
||||
const allBooks = new Map();
|
||||
document.querySelectorAll('.book-option').forEach(el => {
|
||||
|
|
@ -238,26 +235,27 @@ function renderSelectedBooks() {
|
|||
if (selectedBookIds.has(stringId)) {
|
||||
selectedBookIds.delete(stringId);
|
||||
checkbox.checked = false;
|
||||
itemElement.style.background = 'transparent';
|
||||
itemElement.style.background = 'transparent';
|
||||
} else {
|
||||
if (selectedBookIds.size >= MAX_BOOKS) {
|
||||
alert(`Maaf, Anda hanya dapat memilih maksimal ${MAX_BOOKS} buku.`);
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
selectedBookIds.add(stringId);
|
||||
checkbox.checked = true;
|
||||
itemElement.style.background = 'rgba(var(--bs-primary-rgb), 0.05)';
|
||||
itemElement.style.background = 'rgba(var(--bs-primary-rgb), 0.05)';
|
||||
}
|
||||
renderSelectedBooks();
|
||||
}
|
||||
|
||||
// Global function untuk remove
|
||||
// Global function untuk remove
|
||||
window.removeBook = function(id) {
|
||||
const stringId = String(id);
|
||||
selectedBookIds.delete(stringId);
|
||||
|
||||
const itemElement = document.querySelector(`.book-option[data-book-id="${id}"] .book-item-list`);
|
||||
const itemElement = document.querySelector(
|
||||
`.book-option[data-book-id="${id}"] .book-item-list`);
|
||||
if (itemElement) {
|
||||
itemElement.querySelector('.book-checkbox').checked = false;
|
||||
itemElement.style.background = 'transparent';
|
||||
|
|
@ -265,27 +263,25 @@ function renderSelectedBooks() {
|
|||
|
||||
renderSelectedBooks();
|
||||
}
|
||||
|
||||
const form = document.getElementById('formPeminjaman');
|
||||
form.reset();
|
||||
|
||||
// Logika Reset Saat Reload Halaman
|
||||
|
||||
const form = document.getElementById('formPeminjaman');
|
||||
form.reset();
|
||||
|
||||
tomSelect.clear();
|
||||
|
||||
tglPinjam.setDate("today", false);
|
||||
tglKembali.setDate(new Date().fp_incr(7), false);
|
||||
|
||||
selectedBookIds.clear();
|
||||
|
||||
document.querySelectorAll('.book-option').forEach(el => {
|
||||
tomSelect.clear();
|
||||
|
||||
tglPinjam.setDate("today", false);
|
||||
tglKembali.setDate(new Date().fp_incr(7), false);
|
||||
|
||||
selectedBookIds.clear();
|
||||
|
||||
document.querySelectorAll('.book-option').forEach(el => {
|
||||
el.querySelector('.book-checkbox').checked = false;
|
||||
el.querySelector('.book-item-list').style.background = 'transparent';
|
||||
el.querySelector('.book-item-list').style.background = 'transparent';
|
||||
});
|
||||
|
||||
renderSelectedBooks();
|
||||
|
||||
renderSelectedBooks();
|
||||
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
</x-app-layout>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="peminjamanTable" class="table table-striped table-hover nowrap dt-responsive" style="width:100%">
|
||||
<table id="peminjamanTable" class="table table-striped table-hover nowrap dt-responsive"
|
||||
style="width:100%">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>NO</th>
|
||||
|
|
@ -32,6 +33,7 @@
|
|||
$now = \Carbon\Carbon::now();
|
||||
$counter = 1;
|
||||
@endphp
|
||||
|
||||
@forelse ($peminjamanAktif as $transaksi)
|
||||
@php
|
||||
$tenggat = $transaksi['tenggat_kembali'];
|
||||
|
|
@ -39,177 +41,178 @@
|
|||
$isHariIni = $now->startOfDay()->isSameDay($tenggat->startOfDay());
|
||||
$selisihHari = $now->startOfDay()->diffInDays($tenggat->startOfDay(), false);
|
||||
|
||||
$statusKeterlambatan = '';
|
||||
$dendaKeterlambatan = 0;
|
||||
$statusClass = '';
|
||||
|
||||
if ($isTerlambat) {
|
||||
$hariTerlambat = $tenggat->startOfDay()->diffInDays($now->startOfDay());
|
||||
$statusKeterlambatan = "Terlambat $hariTerlambat hari";
|
||||
$dendaKeterlambatan = $hariTerlambat * $transaksi['denda_per_hari'];
|
||||
$statusClass = 'badge rounded-pill bg-danger-subtle text-danger-emphasis';
|
||||
$statusText =
|
||||
'Terlambat ' . $tenggat->startOfDay()->diffInDays($now->startOfDay()) . ' hari';
|
||||
} elseif ($isHariIni) {
|
||||
$statusKeterlambatan = 'Hari ini';
|
||||
$statusClass = 'badge rounded-pill bg-warning-subtle text-warning-emphasis';
|
||||
$statusText = 'Jatuh Tempo Hari Ini';
|
||||
} else {
|
||||
$sisaHari = abs($selisihHari);
|
||||
$statusKeterlambatan = "Sisa $sisaHari hari";
|
||||
$statusClass = 'badge rounded-pill bg-success-subtle text-success-emphasis';
|
||||
$statusText = 'Sisa ' . abs($selisihHari) . ' hari';
|
||||
}
|
||||
@endphp
|
||||
|
||||
@foreach ($transaksi['books'] as $index => $buku)
|
||||
<tr>
|
||||
<td>{{ $counter++ }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="badge bg-primary-light text-primary fw-semibold">{{ $transaksi['id_peminjaman'] }}</span>
|
||||
</td>
|
||||
<td>{{ $transaksi['peminjam'] }}</td>
|
||||
<td>{{ $buku['judul'] }}</td>
|
||||
<td>{{ $transaksi['tanggal_pinjam']->format('d/m/Y') }}</td>
|
||||
<td>{{ $transaksi['tenggat_kembali']->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
<span class="badge {{ $statusClass }}">{{ $statusKeterlambatan }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal"
|
||||
data-bs-target="#modalPengembalian-{{ $transaksi['id_peminjaman'] }}-{{ $buku['id'] }}">
|
||||
Proses
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $counter++ }}</td>
|
||||
<td>
|
||||
<span
|
||||
class="badge bg-primary-subtle text-primary fw-bold">{{ $transaksi['id_peminjaman'] }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="fw-bold">{{ $transaksi['peminjam'] }}</div>
|
||||
<div class="small text-muted">{{ $transaksi['nomor_hp'] }}</div>
|
||||
</td>
|
||||
<td>
|
||||
<ul class="mb-0 ps-3 small">
|
||||
@foreach ($transaksi['books'] as $buku)
|
||||
<li>{{ $buku['judul'] }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</td>
|
||||
<td>{{ $transaksi['tanggal_pinjam']->format('d/m/Y') }}</td>
|
||||
<td>{{ $transaksi['tenggat_kembali']->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
<span class="{{ $statusClass }}">{{ $statusText }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal"
|
||||
data-bs-target="#modalPengembalian-{{ $transaksi['id_peminjaman'] }}">
|
||||
Proses Pengembalian
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<div class="modal fade"
|
||||
id="modalPengembalian-{{ $transaksi['id_peminjaman'] }}-{{ $buku['id'] }}"
|
||||
tabindex="-1" aria-labelledby="modalLabel-{{ $buku['id'] }}" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content border-0 shadow-lg rounded-3">
|
||||
<div class="modal-header p-4">
|
||||
<h1 class="modal-title fs-5" id="modalLabel-{{ $buku['id'] }}">
|
||||
Proses Pengembalian Buku</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"
|
||||
aria-label="Close"></button>
|
||||
{{-- MODAL PENGEMBALIAN --}}
|
||||
<div class="modal fade" id="modalPengembalian-{{ $transaksi['id_peminjaman'] }}"
|
||||
tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content border-0 shadow-lg rounded-3">
|
||||
<div class="modal-header p-4">
|
||||
<h5 class="modal-title fw-bold">Proses Pengembalian
|
||||
({{ count($transaksi['books']) }} Buku)
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<div class="alert alert-light border mb-4">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>Peminjam:
|
||||
<strong>{{ $transaksi['peminjam'] }}</strong></span>
|
||||
<span>ID: <strong>{{ $transaksi['id_peminjaman'] }}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<h6 class="fw-bold">Informasi Peminjaman</h6>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-muted">Nama
|
||||
Peminjam</label>
|
||||
<input type="text" class="form-control"
|
||||
value="{{ $transaksi['peminjam'] }}" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-muted">Judul
|
||||
Buku</label>
|
||||
<input type="text" class="form-control"
|
||||
value="{{ $buku['judul'] }}" readonly>
|
||||
</div>
|
||||
<hr>
|
||||
<h6 class="fw-bold">1. Kondisi Buku</h6>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input radio-kondisi" type="radio"
|
||||
name="kondisi_buku_{{ $buku['id'] }}"
|
||||
id="kondisi_baik_{{ $buku['id'] }}" value="baik"
|
||||
checked data-denda="0">
|
||||
<label class="form-check-label"
|
||||
for="kondisi_baik_{{ $buku['id'] }}">
|
||||
Kondisi Baik
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input radio-kondisi" type="radio"
|
||||
name="kondisi_buku_{{ $buku['id'] }}"
|
||||
id="kondisi_rusak_{{ $buku['id'] }}" value="rusak"
|
||||
data-denda="0">
|
||||
<label class="form-check-label"
|
||||
for="kondisi_rusak_{{ $buku['id'] }}">
|
||||
Buku Rusak
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input radio-kondisi" type="radio"
|
||||
name="kondisi_buku_{{ $buku['id'] }}"
|
||||
id="kondisi_hilang_{{ $buku['id'] }}" value="hilang"
|
||||
data-denda="0">
|
||||
<label class="form-check-label"
|
||||
for="kondisi_hilang_{{ $buku['id'] }}">
|
||||
Buku Hilang
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-3 d-none denda-rusak-input-wrapper">
|
||||
<label class="form-label text-danger">Denda
|
||||
Kerusakan/Kehilangan (Rp)</label>
|
||||
<input type="number" class="form-control denda-rusak-input"
|
||||
value="0" placeholder="Masukkan nominal denda">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h6 class="fw-bold">2. Status Keterlambatan</h6>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-muted">Status</label>
|
||||
<input type="text" class="form-control"
|
||||
value="{{ $statusKeterlambatan }}" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-muted">Denda
|
||||
Keterlambatan</label>
|
||||
<input type="text"
|
||||
class="form-control denda-keterlambatan-display"
|
||||
value="Rp {{ number_format($dendaKeterlambatan, 0, ',', '.') }}"
|
||||
readonly
|
||||
data-denda-keterlambatan="{{ $dendaKeterlambatan }}">
|
||||
</div>
|
||||
<hr>
|
||||
<h6 class="fw-bold">3. Rangkuman & Aksi</h6>
|
||||
<div class="mb-3">
|
||||
<label for="catatan_petugas_{{ $buku['id'] }}"
|
||||
class="form-label small text-muted">Catatan Petugas
|
||||
(Opsional)
|
||||
</label>
|
||||
<textarea class="form-control" id="catatan_petugas_{{ $buku['id'] }}" rows="2"></textarea>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
role="switch" id="notif_wa_{{ $buku['id'] }}"
|
||||
checked>
|
||||
<label class="form-check-label"
|
||||
for="notif_wa_{{ $buku['id'] }}">
|
||||
Kirim notifikasi ke WA ({{ $transaksi['nomor_hp'] }})
|
||||
</label>
|
||||
</div>
|
||||
<div class="p-3 bg-primary-light rounded-3 text-primary">
|
||||
<h5 class="fw-bold mb-0 text-center">Total Denda Diterima
|
||||
</h5>
|
||||
<h3 class="fw-bold mb-0 text-center total-denda-display">
|
||||
Rp
|
||||
{{ number_format($dendaKeterlambatan, 0, ',', '.') }}
|
||||
</h3>
|
||||
<h6 class="fw-bold mb-3">Daftar Buku yang Dikembalikan:</h6>
|
||||
|
||||
@foreach ($transaksi['books'] as $buku)
|
||||
<div class="card mb-3 border bg-light">
|
||||
<div class="card-body p-3">
|
||||
<h6 class="fw-bold text-primary mb-2">{{ $buku['judul'] }}</h6>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label small text-muted mb-1">Kondisi
|
||||
Buku</label>
|
||||
<div class="d-flex gap-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input radio-kondisi"
|
||||
type="radio"
|
||||
name="kondisi_{{ $buku['id'] }}_{{ $transaksi['id_peminjaman'] }}"
|
||||
value="baik" checked>
|
||||
<label
|
||||
class="form-check-label small">Baik</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input radio-kondisi"
|
||||
type="radio"
|
||||
name="kondisi_{{ $buku['id'] }}_{{ $transaksi['id_peminjaman'] }}"
|
||||
value="rusak">
|
||||
<label
|
||||
class="form-check-label small">Rusak/Hilang</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 denda-rusak-input-wrapper d-none">
|
||||
<label class="form-label small text-muted mb-1">Denda
|
||||
Kerusakan</label>
|
||||
<input type="number"
|
||||
class="form-control form-control-sm denda-rusak-input"
|
||||
placeholder="0">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label small text-muted mb-1">
|
||||
<i class="bi bi-pencil-square me-1"></i>Catatan
|
||||
Petugas
|
||||
</label>
|
||||
<textarea class="form-control form-control-sm" rows="2"
|
||||
placeholder="Contoh: Ada coretan di halaman 10, Cover sedikit terlipat, dll..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span>Denda Keterlambatan ({{ $statusText }})</span>
|
||||
@php
|
||||
$dendaTelat = 0;
|
||||
if ($isTerlambat) {
|
||||
$hari = $tenggat->startOfDay()->diffInDays($now->startOfDay());
|
||||
$dendaTelat = $hari * 1000;
|
||||
}
|
||||
@endphp
|
||||
{{-- Data Attribute untuk JS --}}
|
||||
<strong class="text-danger denda-keterlambatan-display"
|
||||
data-denda-keterlambatan="{{ $dendaTelat }}">
|
||||
Rp {{ number_format($dendaTelat, 0, ',', '.') }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="modal-footer p-4">
|
||||
<button type="button" class="btn btn-outline-primary"
|
||||
data-bs-dismiss="modal">Batal</button>
|
||||
<button type="button" class="btn btn-primary">Konfirmasi
|
||||
Pengembalian</button>
|
||||
|
||||
<div
|
||||
class="d-flex justify-content-between align-items-center mb-3 bg-danger bg-opacity-10 p-2 rounded">
|
||||
<span class="fw-bold text-danger">TOTAL YANG HARUS DIBAYAR</span>
|
||||
<strong class="text-danger fs-5 total-denda-display">
|
||||
Rp {{ number_format($dendaTelat, 0, ',', '.') }}
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input" type="checkbox" role="switch" checked
|
||||
id="waStrukToggle-{{ $transaksi['id_peminjaman'] }}">
|
||||
<label class="form-check-label small"
|
||||
for="waStrukToggle-{{ $transaksi['id_peminjaman'] }}">
|
||||
Kirim <strong>Bukti Pengembalian</strong> ke WhatsApp siswa
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input email-toggle" type="checkbox"
|
||||
role="switch" checked
|
||||
id="emailStrukToggle-{{ $transaksi['id_peminjaman'] }}">
|
||||
<label class="form-check-label small"
|
||||
for="emailStrukToggle-{{ $transaksi['id_peminjaman'] }}">
|
||||
Kirim Bukti Pengembalian</strong> ke Email
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer p-3 bg-light">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
data-bs-dismiss="modal">Batal</button>
|
||||
<button type="button" class="btn btn-primary btn-konfirmasi-kembali"
|
||||
data-nama-peminjam="{{ $transaksi['peminjam'] }}"
|
||||
data-nomor-hp="{{ $transaksi['nomor_hp'] }}">
|
||||
Konfirmasi & Selesai
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="8" class="text-center py-5">
|
||||
<div class="empty-state">
|
||||
<i class="bi bi-journal-x"></i>
|
||||
<p>Tidak ada data peminjaman yang sedang aktif.</p>
|
||||
</div>
|
||||
</td>
|
||||
<td colspan="8" class="text-center py-5">Tidak ada data.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
|
|
@ -220,44 +223,53 @@ class="form-label small text-muted">Catatan Petugas
|
|||
|
||||
@push('scripts')
|
||||
<script>
|
||||
// Inisialisasi DataTables
|
||||
$(document).ready(function() {
|
||||
$('#peminjamanTable').DataTable({
|
||||
pageLength: 10,
|
||||
order: [
|
||||
[0, 'asc']
|
||||
],
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// Logika Modal
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
function formatRupiah(angka) {
|
||||
return new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 0
|
||||
}).format(angka).replace(/\s/g, '');
|
||||
}).format(angka).replace(/\s/g, '');
|
||||
}
|
||||
|
||||
function hitungTotalDenda(modal) {
|
||||
const dendaKeterlambatanEl = modal.querySelector('.denda-keterlambatan-display');
|
||||
const dendaRusakEl = modal.querySelector('.denda-rusak-input');
|
||||
const dendaRusakInputs = modal.querySelectorAll('.denda-rusak-input');
|
||||
const totalDendaEl = modal.querySelector('.total-denda-display');
|
||||
const dendaKeterlambatan = parseInt(dendaKeterlambatanEl.dataset.dendaKeterlambatan) || 0;
|
||||
const dendaRusak = parseInt(dendaRusakEl.value) || 0;
|
||||
const totalDenda = dendaKeterlambatan + dendaRusak;
|
||||
|
||||
let dendaKeterlambatan = parseInt(dendaKeterlambatanEl.dataset.dendaKeterlambatan) || 0;
|
||||
let totalDendaRusak = 0;
|
||||
|
||||
dendaRusakInputs.forEach(input => {
|
||||
if (!input.closest('.denda-rusak-input-wrapper').classList.contains('d-none')) {
|
||||
totalDendaRusak += parseInt(input.value) || 0;
|
||||
}
|
||||
});
|
||||
|
||||
const totalDenda = dendaKeterlambatan + totalDendaRusak;
|
||||
totalDendaEl.textContent = formatRupiah(totalDenda);
|
||||
}
|
||||
|
||||
// Logic Radio Button Kondisi Buku
|
||||
const radioKondisi = document.querySelectorAll('.radio-kondisi');
|
||||
radioKondisi.forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
const modal = this.closest('.modal-content');
|
||||
const dendaRusakWrapper = modal.querySelector('.denda-rusak-input-wrapper');
|
||||
const dendaRusakInput = modal.querySelector('.denda-rusak-input');
|
||||
if (this.value === 'rusak' || this.value === 'hilang') {
|
||||
const cardBody = this.closest('.card-body');
|
||||
const dendaRusakWrapper = cardBody.querySelector('.denda-rusak-input-wrapper');
|
||||
const dendaRusakInput = cardBody.querySelector('.denda-rusak-input');
|
||||
|
||||
const modal = this.closest('.modal');
|
||||
|
||||
if (this.value === 'rusak') {
|
||||
dendaRusakWrapper.classList.remove('d-none');
|
||||
} else {
|
||||
dendaRusakWrapper.classList.add('d-none');
|
||||
|
|
@ -267,15 +279,71 @@ function hitungTotalDenda(modal) {
|
|||
});
|
||||
});
|
||||
|
||||
// Logic Input Denda Rusak
|
||||
const dendaRusakInputs = document.querySelectorAll('.denda-rusak-input');
|
||||
dendaRusakInputs.forEach(input => {
|
||||
input.addEventListener('input', function() {
|
||||
const modal = this.closest('.modal-content');
|
||||
const modal = this.closest('.modal');
|
||||
hitungTotalDenda(modal);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('click', '.btn-konfirmasi-kembali', function() {
|
||||
const nama = $(this).data('nama-peminjam');
|
||||
const modalEl = $(this).closest('.modal');
|
||||
const modalInstance = bootstrap.Modal.getInstance(modalEl[0]);
|
||||
const isEmailChecked = modalEl.find('.email-toggle').is(':checked');
|
||||
|
||||
modalInstance.hide();
|
||||
|
||||
modernSwal.fire({
|
||||
title: 'Selesaikan Transaksi?',
|
||||
text: `Buku dari ${nama} akan ditandai sudah kembali.`,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Ya, Selesaikan',
|
||||
cancelButtonText: 'Batal'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
// Loading Simpan DB
|
||||
modernSwal.fire({
|
||||
title: 'Menyimpan Data...',
|
||||
timer: 800,
|
||||
timerProgressBar: true,
|
||||
didOpen: () => Swal.showLoading()
|
||||
}).then(() => {
|
||||
// Loading Kirim Email (Jika dicentang)
|
||||
if (isEmailChecked) {
|
||||
const dummyEmail = nama.replace(/\s+/g, '.').toLowerCase() +
|
||||
'@sekolah.sch.id';
|
||||
modernSwal.fire({
|
||||
title: 'Mengirim Email...',
|
||||
html: `Mengirim nota ke: <b>${dummyEmail}</b>`,
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
didOpen: () => Swal.showLoading()
|
||||
}).then(() => {
|
||||
finishTransaction(true);
|
||||
});
|
||||
} else {
|
||||
finishTransaction(false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
modalInstance.show();
|
||||
}
|
||||
});
|
||||
|
||||
function finishTransaction(withEmail) {
|
||||
Toast.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: withEmail ? 'Buku kembali & Email terkirim.' : 'Buku berhasil dikembalikan.'
|
||||
});
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
</x-app-layout>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,28 @@
|
|||
Pinjam Buku Baru
|
||||
</button> --}}
|
||||
|
||||
{{-- CEK STATUS BANNED --}}
|
||||
@if (Auth::user()->is_banned)
|
||||
<div class="alert alert-danger border-0 shadow-sm d-flex align-items-center mb-4" role="alert">
|
||||
<div class="display-1 text-danger me-4">
|
||||
<i class="bi bi-shield-lock-fill"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="alert-heading fw-bold">AKUN ANDA DIBEKUKAN SEMENTARA!</h4>
|
||||
<p class="mb-1">
|
||||
Sistem mendeteksi adanya <strong>keterlambatan pengembalian buku</strong> atau <strong>tunggakan
|
||||
denda</strong> yang belum diselesaikan.
|
||||
</p>
|
||||
<hr>
|
||||
<p class="mb-0 small">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
Fitur peminjaman buku dinonaktifkan. Silakan segera kembalikan buku ke perpustakaan atau hubungi
|
||||
petugas untuk mengaktifkan kembali akun Anda.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card border-0 shadow-sm rounded-4 my-4 p-4 position-relative" style="background-color: #CEDEFF;">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-8 p-4 p-md-5" style="z-index: 2;">
|
||||
|
|
@ -192,7 +214,9 @@ class="badge bg-{{ $item['type'] }}-soft text-{{ $item['type'] }} rounded-pill p
|
|||
@forelse($bukuPinjamOffline as $buku)
|
||||
<div class="col-xl-4 col-md-6" x-show="expanded || {{ $loop->index }} < 3" x-transition>
|
||||
<x-book-card :buku="$buku">
|
||||
<div class="d-flex align-items-center text-danger bg-white bg-opacity-75 rounded-2 px-2 py-1"> <i class="bi bi-clock-fill me-2 shadow-md"></i>
|
||||
<div
|
||||
class="d-flex align-items-center text-danger bg-white bg-opacity-75 rounded-2 px-2 py-1">
|
||||
<i class="bi bi-clock-fill me-2 shadow-md"></i>
|
||||
<span class="fw-bold small">Sisa: {{ $buku['sisa_hari'] }} hari</span>
|
||||
</div>
|
||||
</x-book-card>
|
||||
|
|
|
|||
|
|
@ -68,48 +68,44 @@ class="badge fw-normal {{ $buku['status'] == 'Tersedia' ? 'bg-success-subtle tex
|
|||
</div>
|
||||
|
||||
<div class="d-flex flex-column gap-2 mt-auto pt-2 border-top">
|
||||
@if ($mode === 'offline')
|
||||
@if ($buku['status'] == 'Dipinjam')
|
||||
<button class="btn btn-sm btn-secondary w-100" disabled>
|
||||
<i class="bi bi-x-circle me-1"></i> Tidak Tersedia
|
||||
@php
|
||||
$userBanned = $isBanned ?? false;
|
||||
$isOfflineAccess =
|
||||
$buku['tipe_akses'] === 'offline' ||
|
||||
(is_array($buku['tipe_akses']) && in_array('offline', $buku['tipe_akses']));
|
||||
$isOnlineAccess =
|
||||
$buku['tipe_akses'] === 'online' ||
|
||||
(is_array($buku['tipe_akses']) && in_array('online', $buku['tipe_akses']));
|
||||
$stokHabis = $buku['status'] == 'Dipinjam';
|
||||
@endphp
|
||||
|
||||
{{-- TOMBOL PINJAM (OFFLINE) --}}
|
||||
@if ($isOfflineAccess && $mode !== 'online')
|
||||
@if ($stokHabis)
|
||||
@if (!$isOnlineAccess)
|
||||
<button class="btn btn-sm btn-light border w-100 text-muted" disabled><i
|
||||
class="bi bi-x-circle me-1"></i> Tidak Tersedia</button>
|
||||
@endif
|
||||
@elseif($userBanned)
|
||||
{{-- LOGIC : Jika Banned, tombol Pinjam MATI --}}
|
||||
<button class="btn btn-sm btn-secondary w-100" disabled
|
||||
title="Akun Anda bermasalah">
|
||||
<i class="bi bi-lock-fill me-1"></i> Akun Dikunci
|
||||
</button>
|
||||
@else
|
||||
<a href="{{ route('peminjaman.ringkasan', $buku['id']) }}"
|
||||
class="btn btn-sm btn-outline-primary w-100">
|
||||
<i class="bi bi-arrow-left-right me-1"></i> Pinjam
|
||||
<i class="bi bi-arrow-left-right me-1"></i> Pinjam
|
||||
</a>
|
||||
@endif
|
||||
@elseif($mode === 'online')
|
||||
@endif
|
||||
|
||||
{{-- TOMBOL BACA (ONLINE) - Selalu Nyala --}}
|
||||
@if ($isOnlineAccess && $mode !== 'offline')
|
||||
<a href="{{ route('baca.ringkasan', $buku['id']) }}"
|
||||
class="btn btn-sm btn-primary w-100">
|
||||
<i class="bi bi-search me-1"></i> Baca
|
||||
<i class="bi bi-book-half me-1"></i> Baca
|
||||
</a>
|
||||
@else
|
||||
{{-- Mode 'umum' --}}
|
||||
@if ($buku['status'] == 'Dipinjam')
|
||||
<button class="btn btn-sm btn-secondary w-100" disabled>
|
||||
<i class="bi bi-x-circle me-1"></i> Tidak Tersedia
|
||||
</button>
|
||||
@else
|
||||
@if (is_array($buku['tipe_akses']))
|
||||
<a href="{{ route('peminjaman.ringkasan', $buku['id']) }}"
|
||||
class="btn btn-sm btn-outline-primary flex-fill"><i
|
||||
class="bi bi-arrow-left-right me-1"></i> Pinjam</a>
|
||||
<a href="{{ route('baca.ringkasan', $buku['id']) }}"
|
||||
class="btn btn-sm btn-primary flex-fill"><i class="bi bi-book-half me-1"></i>
|
||||
Baca</a>
|
||||
@elseif ($buku['tipe_akses'] === 'offline')
|
||||
<a href="{{ route('peminjaman.ringkasan', $buku['id']) }}"
|
||||
class="btn btn-sm btn-outline-primary w-100">
|
||||
<i class="bi bi-arrow-left-right me-1"></i> Pinjam
|
||||
</a>
|
||||
@elseif ($buku['tipe_akses'] === 'online')
|
||||
<a href="{{ route('baca.ringkasan', $buku['id']) }}"
|
||||
class="btn btn-sm btn-primary w-100">
|
||||
<i class="bi bi-book-half me-1"></i> Baca
|
||||
</a>
|
||||
@endif
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,13 @@
|
|||
<div class="main-wrapper flex-grow-1">
|
||||
@include('layouts.navigation')
|
||||
<main class="container-fluid py-4 px-4">
|
||||
@if (session('error'))
|
||||
<div class="alert alert-danger alert-dismissible fade show m-3" role="alert">
|
||||
<i class="bi bi-exclamation-octagon-fill me-2"></i>
|
||||
<strong>DITOLAK!</strong> {{ session('error') }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
@endif
|
||||
{{ $slot }}
|
||||
</main>
|
||||
<footer class="footer text-center py-3">
|
||||
|
|
@ -60,6 +67,8 @@
|
|||
if (isset($item['type'])) {
|
||||
if ($item['type'] === 'riwayat_peminjaman') {
|
||||
$url = route('riwayat.offline');
|
||||
} elseif ($item['type'] === 'denda_active') {
|
||||
$url = route('riwayat.offline');
|
||||
} elseif ($item['type'] === 'rekomendasi' && isset($item['link_id'])) {
|
||||
$url = route('rekomendasi.show', $item['link_id']);
|
||||
} elseif ($item['type'] === 'katalog_kategori' && isset($item['link_id'])) {
|
||||
|
|
@ -103,6 +112,8 @@ class="notification-item d-flex my-1 rounded-3 text-body text-decoration-none @i
|
|||
<!-- Tom Select -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/tom-select/dist/js/tom-select.complete.min.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
|
|
@ -150,6 +161,34 @@ function toggleDesktopSidebar() {
|
|||
});
|
||||
});
|
||||
});
|
||||
const modernSwal = Swal.mixin({
|
||||
customClass: {
|
||||
popup: 'rounded-4 border-0 shadow-lg',
|
||||
title: 'fw-bold text-dark fs-4',
|
||||
html: 'text-muted',
|
||||
confirmButton: 'btn btn-primary rounded-pill px-4 py-2 fw-bold ms-2',
|
||||
cancelButton: 'btn btn-light text-muted rounded-pill px-4 py-2 fw-bold'
|
||||
},
|
||||
buttonsStyling: false,
|
||||
reverseButtons: true,
|
||||
padding: '2rem',
|
||||
});
|
||||
|
||||
// Preset untuk Notifikasi Sukses
|
||||
const Toast = Swal.mixin({
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
showConfirmButton: false,
|
||||
timer: 3000,
|
||||
timerProgressBar: true,
|
||||
customClass: {
|
||||
popup: 'rounded-3 shadow-sm border-0 bg-white'
|
||||
},
|
||||
didOpen: (toast) => {
|
||||
toast.addEventListener('mouseenter', Swal.stopTimer)
|
||||
toast.addEventListener('mouseleave', Swal.resumeTimer)
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@stack('scripts')
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ class="badge bg-primary-subtle text-primary-emphasis rounded-pill">{{ $unreadNot
|
|||
if (isset($item['type'])) {
|
||||
if ($item['type'] === 'riwayat_peminjaman') {
|
||||
$url = route('riwayat.offline');
|
||||
} elseif ($item['type'] === 'denda_active') {
|
||||
$url = route('riwayat.offline');
|
||||
} elseif ($item['type'] === 'rekomendasi' && isset($item['link_id'])) {
|
||||
$url = route('rekomendasi.show', $item['link_id']);
|
||||
} elseif ($item['type'] === 'katalog_kategori' && isset($item['link_id'])) {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@ class="bi bi-book-fill"></i><span class="nav-text ms-2">Manajemen Buku</span></a
|
|||
class="nav-link {{ request()->routeIs('admin.peminjaman.*') ? 'active' : '' }}"> <i
|
||||
class="bi bi-arrow-left-right"></i><span class="nav-text ms-2">Manajemen Pinjaman</span> </a>
|
||||
</li>
|
||||
<li class="nav-item"> <a href="{{ route('admin.denda.index') }}"
|
||||
class="nav-link {{ request()->routeIs('admin.denda.*') ? 'active' : '' }}"> <i
|
||||
class="bi-exclamation-octagon"></i><span class="nav-text ms-2">Manajemen Denda</span> </a>
|
||||
</li>
|
||||
<li class="nav-item"><a href="{{ route('admin.pengumuman.index') }}"
|
||||
class="nav-link {{ request()->routeIs('admin.pengumuman.*') ? 'active' : '' }}"><i
|
||||
class="bi bi-megaphone-fill"></i><span class="nav-text ms-2">Pengumuman</span></a>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
|
||||
<h5 class="fw-bold m-0">Informasi Peminjam</h5>
|
||||
<a href="{{ route('profile.edit') }}" class="btn btn-sm btn-outline-secondary ">Edit Profil</a>
|
||||
<a href="{{ route('profile.edit') }}" class="btn btn-sm btn-outline-secondary ">Edit
|
||||
Profil</a>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<div class="row g-3">
|
||||
|
|
@ -95,8 +96,6 @@ class="rounded me-3 form-book-cover">
|
|||
value="{{ \Carbon\Carbon::now()->addDays(7)->format('d F Y') }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Peraturan Peminjaman --}}
|
||||
|
|
@ -105,7 +104,7 @@ class="rounded me-3 form-book-cover">
|
|||
<div class="alert alert-info border-0 bg-info-subtle">
|
||||
<ol class="mb-0 ps-3">
|
||||
<li>Perpustakaan buka pada jam operasional sekolah.</li>
|
||||
<li>Setiap siswa hanya dapat meminjam sebanyak 2-3 buku dalam satu waktu.</li>
|
||||
<li>Setiap siswa hanya dapat meminjam sebanyak 1-2 buku dalam satu waktu.</li>
|
||||
<li>Buku yang dipinjam harus dikembalikan dalam jangka waktu yang telah ditentukan.
|
||||
</li>
|
||||
<li>Jika buku tidak dikembalikan tepat waktu, akan dikenakan denda atau sanksi
|
||||
|
|
@ -147,8 +146,8 @@ class="rounded me-3 form-book-cover">
|
|||
<div class="modal-body">
|
||||
<div class="alert alert-info border-0 bg-info-subtle mb-4 d-flex align-items-center">
|
||||
<i class="bi bi-info-circle-fill me-2"></i>
|
||||
<span>Anda dapat memilih maksimal <strong><span id="sisaSlot">2</span> buku</strong> lagi.
|
||||
Total maksimal 3 buku.</span>
|
||||
<span>Anda dapat memilih maksimal <strong><span id="sisaSlot">1</span> buku</strong> lagi.
|
||||
Total maksimal 2 buku.</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
|
|
@ -206,20 +205,20 @@ class="bi bi-star-fill me-1"></i>Buku Utama</span>
|
|||
@endforeach
|
||||
</div>
|
||||
|
||||
<!-- Counter Buku Terpilih -->
|
||||
<div class="mt-4 p-3 bg-light rounded shadow-sm">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="fw-semibold">
|
||||
<i class="bi bi-collection me-2 text-primary"></i>Buku Terpilih:
|
||||
<span id="counterBuku" class="text-dark">1</span>/3
|
||||
</span>
|
||||
<ul>
|
||||
<li id="selectedBooks" class="text-muted small"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Counter Buku Terpilih -->
|
||||
<div class="p-3 bg-light">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="fw-semibold">
|
||||
<i class="bi bi-collection me-2 text-primary"></i>Buku Terpilih:
|
||||
<span id="counterBuku" class="text-dark">1</span>/2
|
||||
</span>
|
||||
<ul>
|
||||
<li id="selectedBooks" class="text-muted small"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="modal-footer bg-light">
|
||||
|
|
@ -252,6 +251,23 @@ class="bi bi-star-fill me-1"></i>Buku Utama</span>
|
|||
<div id="ringkasanBuku" class="mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-warning border-0 border-start border-4 border-warning shadow-sm mb-4"
|
||||
role="alert">
|
||||
<div class="d-flex">
|
||||
<div class="fs-1 me-3 text-warning">
|
||||
<i class="bi bi-exclamation-circle-fill"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="fw-bold text-dark mb-1">PENTING: Aturan Peminjaman!</h6>
|
||||
<p class="mb-0 text-muted small">
|
||||
Sesuai peraturan perpustakaan, durasi peminjaman buku maksimal adalah
|
||||
<strong class="text-dark bg-warning-subtle px-2 py-1 rounded">2 HARI</strong>.
|
||||
<br>
|
||||
Mohon kembalikan tepat waktu untuk menghindari denda (Rp 1.000/hari).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer border-0 justify-content-center">
|
||||
<button type="button" class="btn btn-secondary px-4" data-bs-dismiss="modal">Batal</button>
|
||||
|
||||
|
|
@ -268,30 +284,41 @@ class="bi bi-star-fill me-1"></i>Buku Utama</span>
|
|||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const tglPinjam = flatpickr("#tanggalPinjam", {
|
||||
|
||||
// (Default Hari Ini + 2 Hari)
|
||||
// Simpan instance-nya ke variabel
|
||||
const fpKembali = flatpickr("#tanggalKembali", {
|
||||
dateFormat: "d F Y",
|
||||
altInput: true,
|
||||
altFormat: "d F Y",
|
||||
defaultDate: "{{ \Carbon\Carbon::now()->format('Y-m-d') }}",
|
||||
defaultDate: new Date().fp_incr(2),
|
||||
locale: "id",
|
||||
minDate: "today",
|
||||
onChange: function(selectedDates, dateStr) {
|
||||
if (tglKembali.selectedDates[0] <= selectedDates[0]) {
|
||||
const newDate = new Date(selectedDates[0]);
|
||||
newDate.setDate(newDate.getDate() + 1);
|
||||
tglKembali.setDate(newDate);
|
||||
}
|
||||
tglKembali.set("minDate", new Date(selectedDates[0]).fp_incr(1));
|
||||
}
|
||||
minDate: new Date().fp_incr(1),
|
||||
maxDate: new Date().fp_incr(2)
|
||||
});
|
||||
|
||||
const tglKembali = flatpickr("#tanggalKembali", {
|
||||
// Inisialisasi Tanggal Pinjam
|
||||
flatpickr("#tanggalPinjam", {
|
||||
dateFormat: "d F Y",
|
||||
altInput: true,
|
||||
altFormat: "d F Y",
|
||||
defaultDate: "{{ \Carbon\Carbon::now()->addDays(7)->format('Y-m-d') }}",
|
||||
defaultDate: "today",
|
||||
locale: "id",
|
||||
minDate: new Date().fp_incr(1)
|
||||
minDate: "today",
|
||||
|
||||
// LOGIC : Saat Tanggal Pinjam Berubah
|
||||
onChange: function(selectedDates, dateStr) {
|
||||
if (selectedDates.length > 0) {
|
||||
const tglMulai = selectedDates[0];
|
||||
const maxDateBaru = new Date(tglMulai).fp_incr(2);
|
||||
const minDateBaru = new Date(tglMulai).fp_incr(1);
|
||||
|
||||
fpKembali.set("minDate", minDateBaru);
|
||||
fpKembali.set("maxDate", maxDateBaru);
|
||||
|
||||
fpKembali.setDate(maxDateBaru);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -21,58 +21,81 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
@php $counter = 1; @endphp
|
||||
@forelse ($riwayatOffline as $transaksi)
|
||||
@foreach ($transaksi['books'] as $buku)
|
||||
<tr>
|
||||
<td>{{ $counter++ }}</td>
|
||||
<td>{{ $transaksi['id_peminjaman'] }}</td>
|
||||
<td>{{ $buku['kode_buku'] }}</td>
|
||||
<td>{{ $buku['judul'] }}</td>
|
||||
<td>{{ $transaksi['tanggal_pinjam'] }}</td>
|
||||
<td>{{ $transaksi['tanggal_kembali'] }}</td>
|
||||
<td>
|
||||
@if ($transaksi['status'] == 'Dikembalikan')
|
||||
<span
|
||||
class="badge rounded-pill bg-success-subtle text-success-emphasis">{{ $transaksi['status'] }}</span>
|
||||
@else
|
||||
<span
|
||||
class="badge rounded-pill bg-warning-subtle text-warning-emphasis">{{ $transaksi['status'] }}</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-primary btn-sm text-white" data-bs-toggle="modal"
|
||||
data-bs-target="#detailModal" data-transaksi-id="{{ $transaksi['id'] }}"
|
||||
data-buku-id="{{ $buku['id'] }}">
|
||||
Detail
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@if (count($riwayatOffline) > 0)
|
||||
@foreach ($riwayatOffline as $transaksi)
|
||||
@foreach ($transaksi['books'] as $buku)
|
||||
@php
|
||||
try {
|
||||
$tglKembali = \Carbon\Carbon::createFromFormat(
|
||||
'd/m/Y',
|
||||
$transaksi['tanggal_kembali'],
|
||||
)->startOfDay();
|
||||
} catch (\Exception $e) {
|
||||
$tglKembali = \Carbon\Carbon::parse(
|
||||
$transaksi['tanggal_kembali'],
|
||||
)->startOfDay();
|
||||
}
|
||||
|
||||
$today = now()->startOfDay();
|
||||
$isTelat = $today->gt($tglKembali) && $transaksi['status'] == 'Dipinjam';
|
||||
|
||||
$hariTelat = 0;
|
||||
$denda = 0;
|
||||
|
||||
if ($isTelat) {
|
||||
$hariTelat = $tglKembali->diffInDays($today);
|
||||
$denda = $hariTelat * 1000;
|
||||
}
|
||||
@endphp
|
||||
|
||||
<tr>
|
||||
<td>{{ $counter++ }}</td>
|
||||
<td>{{ $transaksi['id_peminjaman'] }}</td>
|
||||
<td>{{ $buku['kode_buku'] }}</td>
|
||||
<td>{{ $buku['judul'] }}</td>
|
||||
<td>{{ $transaksi['tanggal_pinjam'] }}</td>
|
||||
<td>{{ $transaksi['tanggal_kembali'] }}</td>
|
||||
<td>
|
||||
@if ($transaksi['status'] == 'Dikembalikan')
|
||||
<span
|
||||
class="badge rounded-pill bg-success-subtle text-success-emphasis">Dikembalikan</span>
|
||||
@elseif($isTelat)
|
||||
<span class="badge rounded-pill bg-danger text-white">Terlambat</span>
|
||||
@else
|
||||
<span
|
||||
class="badge rounded-pill bg-warning-subtle text-warning-emphasis">Dipinjam</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-primary btn-sm text-white" data-bs-toggle="modal"
|
||||
data-bs-target="#detailModal" data-transaksi-id="{{ $transaksi['id'] }}"
|
||||
data-buku-id="{{ $buku['id'] }}"
|
||||
data-is-telat="{{ $isTelat ? 'true' : 'false' }}"
|
||||
data-hari-telat="{{ $hariTelat }}"
|
||||
data-denda="{{ number_format($denda, 0, ',', '.') }}">
|
||||
Detail
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@endforeach
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="text-center">Tidak ada riwayat peminjaman.</td> </tr>
|
||||
@endforelse
|
||||
@endif
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- MODAL DETAIL RIWAYAT --}}
|
||||
<div class="modal fade" id="detailModal" tabindex="-1" aria-labelledby="detailModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
|
||||
{{-- MODAL DETAIL --}}
|
||||
<div class="modal fade" id="detailModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-content border-0 shadow-lg">
|
||||
<div class="modal-header bg-light">
|
||||
<h5 class="modal-title fw-bold" id="detailModalLabel"><i
|
||||
class="bi bi-book-half me-2 text-primary"></i>Detail Riwayat</h5>
|
||||
<h5 class="modal-title fw-bold"><i class="bi bi-book-half me-2 text-primary"></i>Detail Riwayat</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<div id="modal-content-placeholder" class="text-center">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-content-placeholder" class="text-center">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -90,9 +113,20 @@ class="bi bi-book-half me-2 text-primary"></i>Detail Riwayat</h5>
|
|||
order: [
|
||||
[0, 'asc']
|
||||
],
|
||||
|
||||
language: {
|
||||
emptyTable: `
|
||||
<div class="text-center py-5 text-muted">
|
||||
<i class="bi bi-inbox fs-1 d-block mb-2 opacity-50"></i>
|
||||
Belum ada riwayat peminjaman.
|
||||
</div>
|
||||
`,
|
||||
zeroRecords: "Tidak ada data yang cocok"
|
||||
},
|
||||
|
||||
columnDefs: [{
|
||||
responsivePriority: 1,
|
||||
targets: 2
|
||||
targets: 3
|
||||
},
|
||||
{
|
||||
responsivePriority: 2,
|
||||
|
|
@ -106,75 +140,107 @@ class="bi bi-book-half me-2 text-primary"></i>Detail Riwayat</h5>
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
// Modal Detail
|
||||
// Modal Logic
|
||||
const detailModal = document.getElementById('detailModal');
|
||||
|
||||
detailModal.addEventListener('show.bs.modal', event => {
|
||||
const button = event.relatedTarget;
|
||||
const transaksiId = button.getAttribute('data-transaksi-id');
|
||||
const bukuId = button.getAttribute('data-buku-id');
|
||||
const modalBody = detailModal.querySelector('#modal-content-placeholder');
|
||||
const modalTitle = detailModal.querySelector('#detailModalLabel');
|
||||
const isTelat = button.getAttribute('data-is-telat') === 'true';
|
||||
const hariTelat = button.getAttribute('data-hari-telat');
|
||||
const denda = button.getAttribute('data-denda');
|
||||
|
||||
const modalBody = detailModal.querySelector('#modal-content-placeholder');
|
||||
const transaksiItem = riwayatOfflineData.find(item => item.id == transaksiId);
|
||||
if (!transaksiItem) return;
|
||||
|
||||
const buku = transaksiItem.books.find(b => b.id == bukuId);
|
||||
if (!buku) return;
|
||||
|
||||
modalTitle.textContent = `Detail Buku`;
|
||||
|
||||
const keteranganHtml = buku.keterangan ?
|
||||
`
|
||||
<hr class="my-3">
|
||||
<div class="alert alert-warning border-0 bg-warning-subtle mb-0">
|
||||
<h6 class="fw-bold mb-1 text-warning-emphasis"><i class="bi bi-info-circle-fill me-2"></i>Catatan Petugas</h6>
|
||||
<p class="mb-0">${buku.keterangan}</p>
|
||||
const dendaHtml = isTelat ? `
|
||||
<div class="card border-0 border-start border-4 border-danger bg-danger-subtle mb-3 shadow-sm">
|
||||
<div class="card-body p-3 d-flex align-items-center">
|
||||
<div class="flex-shrink-0 me-3">
|
||||
<i class="bi bi-exclamation-octagon-fill text-danger fs-1"></i>
|
||||
</div>
|
||||
<div class="text-start">
|
||||
<h6 class="fw-bold text-danger mb-1">Terlambat Pengembalian!</h6>
|
||||
<p class="mb-0 small text-danger-emphasis">
|
||||
Anda terlambat <span class="badge bg-danger text-white">${hariTelat} Hari</span>.
|
||||
Denda yang harus dibayar:
|
||||
<span class="fw-bold fs-6 d-block mt-1">Rp ${denda}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
` :
|
||||
'';
|
||||
</div>` : '';
|
||||
|
||||
const keteranganHtml = buku.keterangan ? `
|
||||
<div class="card border-0 border-start border-4 border-warning bg-warning-subtle mb-4 shadow-sm">
|
||||
<div class="card-body p-3 d-flex align-items-start">
|
||||
<div class="flex-shrink-0 me-3">
|
||||
<i class="bi bi-journal-bookmark-fill text-warning-emphasis fs-3"></i>
|
||||
</div>
|
||||
<div class="text-start">
|
||||
<h6 class="fw-bold text-warning-emphasis mb-1">Catatan Petugas</h6>
|
||||
<p class="mb-0 small text-dark opacity-75 fst-italic">
|
||||
"${buku.keterangan}"
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>` : '';
|
||||
|
||||
const contentHTML = `
|
||||
// RENDER HTML
|
||||
modalBody.innerHTML = `
|
||||
<div class="text-center mb-4">
|
||||
<img src="{{ asset('${buku.cover}') }}" alt="Cover ${buku.judul}" class="img-fluid rounded shadow-sm" style="max-width: 150px;">
|
||||
<img src="{{ asset('') }}${buku.cover}" class="img-fluid rounded shadow-sm" style="max-width: 140px;">
|
||||
</div>
|
||||
|
||||
<h4 class="fw-bold text-center">${buku.judul}</h4>
|
||||
<p class="text-muted text-center">${buku.deskripsi}</p>
|
||||
<h5 class="fw-bold text-center mb-1">${buku.judul}</h5>
|
||||
<p class="text-muted text-center small mb-4">${buku.deskripsi}</p>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<table class="table table-borderless table-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="fw-bold text-start" style="width: 35%;">ID Peminjaman</td>
|
||||
<td style="width: 5%;">:</td>
|
||||
<td class="text-start">${transaksiItem.id_peminjaman}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-bold text-start">Kode Buku</td>
|
||||
<td>:</td>
|
||||
<td class="text-start">${buku.kode_buku}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-bold text-start">Kategori Buku</td>
|
||||
<td>:</td>
|
||||
<td class="text-start"><span class="badge bg-primary-subtle text-primary-emphasis rounded-pill px-3 py-2">${buku.kategori}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-bold text-start">Tahun Terbit</td>
|
||||
<td>:</td>
|
||||
<td class="text-start">${buku.tahun}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
${dendaHtml}
|
||||
${keteranganHtml}
|
||||
|
||||
${keteranganHtml} `;
|
||||
|
||||
modalBody.innerHTML = contentHTML;
|
||||
<div class="card bg-light border-0 rounded-3 p-3">
|
||||
<table class="table table-borderless table-sm mb-0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="fw-bold text-start" style="width: 35%;">ID Peminjaman</td>
|
||||
<td style="width: 1%;">:</td>
|
||||
<td class="text-start">${transaksiItem.id_peminjaman}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-bold text-start">Kode Buku</td>
|
||||
<td>:</td>
|
||||
<td class="text-start fw-medium">${buku.kode_buku}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-bold text-start align-middle">Kategori</td>
|
||||
<td class="align-middle">:</td>
|
||||
<td class="text-start">
|
||||
<span class="badge bg-primary-subtle text-primary-emphasis rounded-pill px-3 py-2">
|
||||
${buku.kategori}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-bold text-start">Tahun Terbit</td>
|
||||
<td>:</td>
|
||||
<td class="text-start">${buku.tahun}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-bold text-start">Tgl Pinjam</td>
|
||||
<td>:</td>
|
||||
<td class="text-start">${transaksiItem.tanggal_pinjam}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="fw-bold text-start">Tenggat</td>
|
||||
<td>:</td>
|
||||
<td class="text-start">${transaksiItem.tanggal_kembali}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>`;
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
</x-app-layout>
|
||||
|
|
|
|||
|
|
@ -100,6 +100,9 @@
|
|||
|
||||
Route::get('/peminjaman', [AdminPeminjamanController::class, 'index'])->name('peminjaman.index');
|
||||
Route::get('/peminjaman/tambah', [AdminPeminjamanController::class, 'create'])->name('peminjaman.create');
|
||||
|
||||
Route::get('/denda', [AdminPeminjamanController::class, 'dendaIndex'])->name('denda.index');
|
||||
Route::post('/denda/sanksi', [AdminPeminjamanController::class, 'berikanSanksi'])->name('denda.sanksi');
|
||||
});
|
||||
|
||||
// --- RUTE LOGIN KHUSUS ADMIN ---
|
||||
|
|
|
|||
Loading…
Reference in New Issue