feat: Feature peminjaman offline
This commit is contained in:
parent
88490ce78b
commit
172ac9e3b7
|
|
@ -19,6 +19,16 @@ public function index()
|
|||
$bukuPinjamOffline = DummyDataService::getBukuPinjamOffline($user);
|
||||
$bacaBukuOnline = DummyDataService::getBacaBukuOnline($user);
|
||||
|
||||
$hour = date('H');
|
||||
$greeting = "Selamat Pagi";
|
||||
if ($hour >= 12 && $hour < 15) {
|
||||
$greeting = "Selamat Siang";
|
||||
} elseif ($hour >= 15 && $hour < 18) {
|
||||
$greeting = "Selamat Sore";
|
||||
} elseif ($hour >= 18) {
|
||||
$greeting = "Selamat Malam";
|
||||
}
|
||||
|
||||
return view('dashboard', compact(
|
||||
'user',
|
||||
'stats',
|
||||
|
|
@ -27,7 +37,8 @@ public function index()
|
|||
'progressMembaca',
|
||||
'statistikBulanan',
|
||||
'bukuPinjamOffline',
|
||||
'bacaBukuOnline'
|
||||
'bacaBukuOnline',
|
||||
'greeting',
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ public function index(Request $request, $tipe = null)
|
|||
'semuaBuku' => $semuaBuku,
|
||||
'filterOptions' => $filterOptions,
|
||||
'input' => $filters,
|
||||
'tipe' => $tipe,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\DummyDataService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PeminjamanController extends Controller
|
||||
{
|
||||
/**
|
||||
* Menampilkan daftar buku yang bisa dipinjam offline,
|
||||
* dengan memanfaatkan filter dari getKatalogBuku.
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
||||
$filters['tipe_akses'] = 'offline';
|
||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
||||
$filterOptions = DummyDataService::getFilterOptions();
|
||||
|
||||
return view('peminjaman.index', [
|
||||
'semuaBuku' => $semuaBuku,
|
||||
'filterOptions' => $filterOptions,
|
||||
'input' => $request->query(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan halaman ringkasan buku sebelum meminjam.
|
||||
*/
|
||||
public function ringkasan($id)
|
||||
{
|
||||
// Untuk simulasi, kita anggap user sudah login
|
||||
// $user = Auth::user();
|
||||
$user = DummyDataService::getAllSiswa()[0];
|
||||
|
||||
$buku = DummyDataService::getKatalogBuku()->firstWhere('id', $id);
|
||||
|
||||
return view('peminjaman.ringkasan', compact('user', 'buku'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan form peminjaman dengan semua buku offline untuk pilihan tambahan.
|
||||
*/
|
||||
public function form($id)
|
||||
{
|
||||
// $user = Auth::user();
|
||||
$user = DummyDataService::getAllSiswa()[0];
|
||||
|
||||
// Buku yang dipilih pertama kali
|
||||
$buku = DummyDataService::getKatalogBuku()->firstWhere('id', $id);
|
||||
|
||||
// Semua buku offline untuk pilihan tambahan
|
||||
$filters = ['tipe_akses' => 'offline'];
|
||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
||||
|
||||
return view('peminjaman.form', compact('user', 'buku', 'semuaBuku'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Proses pengiriman form peminjaman multi buku
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
// Validasi input
|
||||
$request->validate([
|
||||
'buku_ids' => 'required|array|min:1|max:3',
|
||||
'buku_ids.*' => 'integer|exists:books,id' // Sesuaikan dengan tabel yang ada
|
||||
]);
|
||||
|
||||
// Logic untuk menyimpan peminjaman multiple books
|
||||
$bukuIds = $request->input('buku_ids');
|
||||
|
||||
// Contoh logic penyimpanan:
|
||||
foreach ($bukuIds as $bukuId) {
|
||||
// Simpan ke database peminjaman
|
||||
// Peminjaman::create([
|
||||
// 'user_id' => auth()->id(),
|
||||
// 'book_id' => $bukuId,
|
||||
// 'tanggal_pinjam' => now(),
|
||||
// 'tanggal_kembali' => now()->addDays(7),
|
||||
// 'status' => 'dipinjam'
|
||||
// ]);
|
||||
}
|
||||
|
||||
return redirect()->route('dashboard')
|
||||
->with('success', 'Berhasil meminjam ' . count($bukuIds) . ' buku!');
|
||||
}
|
||||
}
|
||||
|
|
@ -10,11 +10,13 @@ public static function getAllSiswa(): array
|
|||
[
|
||||
'id' => 1,
|
||||
'nisn' => '1234567890',
|
||||
'nama_lengkap' => 'John Doe',
|
||||
'email' => 'johnskuy@smkn1perpus.sch.id',
|
||||
'nomor_hp' => '081234567890',
|
||||
'nama_lengkap' => 'Silvi Rahmawati',
|
||||
'email' => 'silvi.rahmawati@smkn1perpus.sch.id',
|
||||
'nomor_hp' => '08123456789',
|
||||
'password' => 'password',
|
||||
'role' => 'siswa',
|
||||
'kelas' => 'XII RPL',
|
||||
'golongan' => 'A',
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
|
|
@ -33,6 +35,8 @@ public static function getAllSiswa(): array
|
|||
'nomor_hp' => '081998877665',
|
||||
'password' => 'password',
|
||||
'role' => 'siswa',
|
||||
'kelas' => 'XII RPL A',
|
||||
'golongan' => 'A',
|
||||
],
|
||||
[
|
||||
'id' => 4,
|
||||
|
|
@ -42,6 +46,8 @@ public static function getAllSiswa(): array
|
|||
'nomor_hp' => '081556677889',
|
||||
'password' => 'password',
|
||||
'role' => 'siswa',
|
||||
'kelas' => 'XII RPL A',
|
||||
'golongan' => 'A',
|
||||
],
|
||||
[
|
||||
'id' => 5,
|
||||
|
|
@ -152,7 +158,7 @@ private static function getAllBooks()
|
|||
'tahun' => 2023,
|
||||
'status' => 'Tersedia',
|
||||
'is_new' => false,
|
||||
'tipe_akses' => 'online',
|
||||
'tipe_akses' => ['online', 'offline'],
|
||||
'progress' => 100,
|
||||
'user_id' => [3, 1],
|
||||
],
|
||||
|
|
@ -206,7 +212,7 @@ private static function getAllBooks()
|
|||
'is_new' => true,
|
||||
'tipe_akses' => 'online',
|
||||
'progress' => 40,
|
||||
'user_id' => [1,4,5],
|
||||
'user_id' => [1, 4, 5],
|
||||
],
|
||||
[
|
||||
'id' => 7,
|
||||
|
|
@ -219,9 +225,9 @@ private static function getAllBooks()
|
|||
'is_new' => true,
|
||||
'tipe_akses' => 'online',
|
||||
'progress' => 0,
|
||||
'user_id' => [3,1]
|
||||
'user_id' => [3, 1]
|
||||
],
|
||||
[
|
||||
[
|
||||
'id' => 8,
|
||||
'judul' => 'Buku Offline Tanpa Peminjam',
|
||||
'penulis' => 'Penulis Misteri',
|
||||
|
|
@ -289,24 +295,38 @@ public static function getBacaBukuOnline($user): array
|
|||
/**
|
||||
* Mengambil daftar buku untuk katalog dengan filter.
|
||||
*/
|
||||
public static function getKatalogBuku(array $filters = []): \Illuminate\Support\Collection
|
||||
{
|
||||
$buku = self::getAllBooks();
|
||||
// app/Services/DummyDataService.php
|
||||
|
||||
$buku = $buku->when($filters['search'] ?? null, function ($query, $search) {
|
||||
return $query->filter(fn($item) => str_contains(strtolower($item['judul']), strtolower($search)));
|
||||
})->when($filters['kategori'] ?? null, function ($query, $kategori) {
|
||||
return $query->where('kategori', $kategori);
|
||||
})->when($filters['tahun'] ?? null, function ($query, $tahun) {
|
||||
return $query->where('tahun', $tahun);
|
||||
})->when($filters['penulis'] ?? null, function ($query, $penulis) {
|
||||
return $query->where('penulis', $penulis);
|
||||
})->when($filters['tipe'] ?? null, function ($query, $tipe) {
|
||||
return $query->where('tipe_akses', $tipe);
|
||||
});
|
||||
public static function getKatalogBuku(array $filters = []): \Illuminate\Support\Collection
|
||||
{
|
||||
$buku = self::getAllBooks();
|
||||
|
||||
return $buku;
|
||||
}
|
||||
$buku = $buku->when($filters['search'] ?? null, function ($query, $search) {
|
||||
return $query->filter(fn($item) => str_contains(strtolower($item['judul']), strtolower($search)));
|
||||
})->when($filters['kategori'] ?? null, function ($query, $kategori) {
|
||||
return $query->where('kategori', $kategori);
|
||||
})->when($filters['tahun'] ?? null, function ($query, $tahun) {
|
||||
return $query->where('tahun', $tahun);
|
||||
})->when($filters['penulis'] ?? null, function ($query, $penulis) {
|
||||
return $query->where('penulis', $penulis);
|
||||
})
|
||||
->when($filters['tipe_akses'] ?? null, function ($query, $tipe) {
|
||||
if ($tipe === 'offline') {
|
||||
return $query->filter(function ($buku) {
|
||||
return $buku['tipe_akses'] === 'offline' || (is_array($buku['tipe_akses']) && in_array('offline', $buku['tipe_akses']));
|
||||
});
|
||||
}
|
||||
if ($tipe === 'online') {
|
||||
return $query->filter(function ($buku) {
|
||||
return $buku['tipe_akses'] === 'online' || (is_array($buku['tipe_akses']) && in_array('online', $buku['tipe_akses']));
|
||||
});
|
||||
}
|
||||
return $query;
|
||||
})
|
||||
->sortByDesc('status');
|
||||
|
||||
return $buku;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method baru untuk mengambil daftar unik untuk dropdown filter
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
// public/js/dashboard-charts.js
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Ambil elemen canvas Bar Chart
|
||||
const barChartEl = document.getElementById('barChart');
|
||||
if (barChartEl) {
|
||||
const statistikData = JSON.parse(barChartEl.dataset.stats);
|
||||
|
||||
new Chart(barChartEl.getContext('2d'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: statistikData.labels,
|
||||
datasets: [{
|
||||
label: 'Buku Dibaca',
|
||||
backgroundColor: '#435ebe',
|
||||
data: statistikData.data,
|
||||
borderRadius: 8
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: 'rgba(0,0,0,0.05)',
|
||||
},
|
||||
ticks: {
|
||||
color: '#6c757d',
|
||||
font: {
|
||||
size: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
x: {
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
ticks: {
|
||||
color: '#6c757d',
|
||||
font: {
|
||||
size: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const donutChartEl = document.getElementById('donutChart');
|
||||
if (donutChartEl) {
|
||||
const progressData = JSON.parse(donutChartEl.dataset.progress);
|
||||
|
||||
new Chart(donutChartEl.getContext('2d'), {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Telah Dibaca', 'Belum Dibaca'],
|
||||
datasets: [{
|
||||
data: [progressData.selesai, progressData.sisa],
|
||||
backgroundColor: ['#435ebe', '#e9ecef'],
|
||||
borderColor: ['#ffffff'],
|
||||
borderWidth: 3,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
cutout: '75%'
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
import './bootstrap';
|
||||
import 'bootstrap';
|
||||
import './peminjaman-form.js';
|
||||
import * as bootstrap from 'bootstrap';
|
||||
|
||||
window.bootstrap = bootstrap;
|
||||
|
||||
import Alpine from 'alpinejs';
|
||||
|
||||
window.Alpine = Alpine;
|
||||
|
||||
Alpine.start();
|
||||
Alpine.start();
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
const formPeminjamanElement = document.getElementById('formPeminjaman');
|
||||
|
||||
if (formPeminjamanElement) {
|
||||
// Ambil data yang sudah disimpan di elemen HTML
|
||||
const semuaBuku = JSON.parse(formPeminjamanElement.dataset.semuaBuku);
|
||||
const bukuAwal = JSON.parse(formPeminjamanElement.dataset.bukuAwal);
|
||||
|
||||
let bukuTerpilih = [bukuAwal.id];
|
||||
const maxBuku = 3;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
updateCounter();
|
||||
updateSelectedBooks();
|
||||
updateCheckboxes();
|
||||
updateBtnTambahBuku();
|
||||
updateDaftarBuku();
|
||||
});
|
||||
|
||||
function updateCounter() {
|
||||
document.getElementById('counterBuku').textContent = bukuTerpilih.length;
|
||||
document.getElementById('sisaSlot').textContent = maxBuku - bukuTerpilih.length;
|
||||
}
|
||||
|
||||
function updateBtnTambahBuku() {
|
||||
const btnTambah = document.getElementById('btnTambahBuku');
|
||||
if (bukuTerpilih.length >= maxBuku) {
|
||||
btnTambah.classList.add('disabled');
|
||||
btnTambah.innerHTML = '<i class="bi bi-check-circle me-1"></i>Maksimal Buku Tercapai';
|
||||
} else {
|
||||
btnTambah.classList.remove('disabled');
|
||||
btnTambah.innerHTML = '<i class="bi bi-plus-circle me-1"></i>Tambah Buku';
|
||||
}
|
||||
}
|
||||
|
||||
function updateSelectedBooks() {
|
||||
const container = document.getElementById('selectedBooks');
|
||||
const selected = semuaBuku.filter(book => bukuTerpilih.includes(book.id));
|
||||
container.innerHTML = selected.map(book => `<li>${book.judul}</li>`).join('');
|
||||
}
|
||||
|
||||
function updateCheckboxes() {
|
||||
document.querySelectorAll('.book-checkbox').forEach(checkbox => {
|
||||
const bookId = parseInt(checkbox.value);
|
||||
checkbox.checked = bukuTerpilih.includes(bookId);
|
||||
|
||||
if (bukuTerpilih.length >= maxBuku && !bukuTerpilih.includes(bookId)) {
|
||||
checkbox.disabled = true;
|
||||
} else if (bookId !== bukuAwal.id) {
|
||||
checkbox.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.toggleBookSelection = function (bookId) {
|
||||
if (bookId === bukuAwal.id) return;
|
||||
|
||||
const checkbox = document.getElementById('book' + bookId);
|
||||
if (!checkbox.disabled) {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
handleCheckboxChange(checkbox);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCheckboxChange(checkbox) {
|
||||
const bookId = parseInt(checkbox.value);
|
||||
if (checkbox.checked) {
|
||||
if (bukuTerpilih.length < maxBuku && !bukuTerpilih.includes(bookId)) {
|
||||
bukuTerpilih.push(bookId);
|
||||
} else {
|
||||
checkbox.checked = false;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
bukuTerpilih = bukuTerpilih.filter(id => id !== bookId);
|
||||
}
|
||||
|
||||
updateCounter();
|
||||
updateSelectedBooks();
|
||||
updateCheckboxes();
|
||||
updateBtnTambahBuku();
|
||||
}
|
||||
|
||||
document.addEventListener('change', function (e) {
|
||||
if (e.target.classList.contains('book-checkbox')) {
|
||||
if (e.target.value != bukuAwal.id) {
|
||||
handleCheckboxChange(e.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('searchBuku')?.addEventListener('input', function (e) {
|
||||
const searchTerm = e.target.value.toLowerCase();
|
||||
document.querySelectorAll('.book-option').forEach(option => {
|
||||
const title = option.getAttribute('data-book-title');
|
||||
const author = option.getAttribute('data-book-author');
|
||||
|
||||
if (title.includes(searchTerm) || author.includes(searchTerm)) {
|
||||
option.style.display = 'block';
|
||||
} else {
|
||||
option.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function updateDaftarBuku() {
|
||||
const container = document.getElementById('daftarBukuPinjam');
|
||||
const hiddenInputs = document.getElementById('hiddenInputs');
|
||||
const selected = semuaBuku.filter(book => bukuTerpilih.includes(book.id));
|
||||
|
||||
container.innerHTML = selected.map((book) => `
|
||||
<div class="book-item border rounded p-3 mb-3 shadow-sm" data-book-id="${book.id}">
|
||||
<div class="d-flex align-items-start">
|
||||
|
||||
<img src="/${book.cover}" alt="Cover"
|
||||
class="rounded me-3 form-book-cover"
|
||||
style="width: 60px; height: 80px; object-fit: cover;">
|
||||
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="fw-bold mb-1">${book.judul}</h6>
|
||||
<p class="text-muted small mb-1">${book.penulis}</p>
|
||||
<span class="badge bg-info">${book.kategori}</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column align-items-end gap-2">
|
||||
${book.id === bukuAwal.id
|
||||
? '<span class="badge bg-success">Buku Utama</span>'
|
||||
: '<span class="badge bg-primary">Tambahan</span>'}
|
||||
|
||||
${book.id !== bukuAwal.id
|
||||
? `<button type="button" class="btn btn-sm btn-outline-danger remove-book"
|
||||
onclick="removeBuku(${book.id})"
|
||||
title="Hapus Buku">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>`
|
||||
: ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
|
||||
hiddenInputs.innerHTML = bukuTerpilih.map(id =>
|
||||
`<input type="hidden" name="buku_ids[]" value="${id}">`
|
||||
).join('');
|
||||
}
|
||||
|
||||
window.removeBuku = function (bookId) {
|
||||
if (bookId !== bukuAwal.id) {
|
||||
bukuTerpilih = bukuTerpilih.filter(id => id !== bookId);
|
||||
updateDaftarBuku();
|
||||
updateCounter();
|
||||
updateSelectedBooks();
|
||||
updateCheckboxes();
|
||||
updateBtnTambahBuku();
|
||||
}
|
||||
}
|
||||
|
||||
window.konfirmasiPilihanBuku = function () {
|
||||
updateDaftarBuku();
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('pilihBukuModal'));
|
||||
modal.hide();
|
||||
}
|
||||
|
||||
document.getElementById('konfirmasiModal')?.addEventListener('show.bs.modal', function () {
|
||||
const selected = semuaBuku.filter(book => bukuTerpilih.includes(book.id));
|
||||
const ringkasan = selected.map((book, index) =>
|
||||
`<div class="d-flex justify-content-between">
|
||||
<span>${index + 1}. ${book.judul}</span>
|
||||
<small class="text-muted">${book.penulis}</small>
|
||||
</div>`
|
||||
).join('');
|
||||
document.getElementById('ringkasanBuku').innerHTML = ringkasan;
|
||||
});
|
||||
|
||||
window.kirimForm = function () {
|
||||
document.getElementById('formPeminjaman').submit();
|
||||
}
|
||||
|
||||
document.getElementById('pilihBukuModal')?.addEventListener('hidden.bs.modal', function () {
|
||||
const searchInput = document.getElementById('searchBuku');
|
||||
if (searchInput) {
|
||||
searchInput.value = '';
|
||||
document.querySelectorAll('.book-option').forEach(option => {
|
||||
option.style.display = 'block';
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1,128 +1,139 @@
|
|||
$primary: #435ebe;
|
||||
$secondary: #6c757d;
|
||||
$success: #198754;
|
||||
$info: #0dcaf0;
|
||||
$warning: #ffc107;
|
||||
$danger: #dc3545;
|
||||
$light: #f4f7f8;
|
||||
$dark: #212529;
|
||||
// ===================================
|
||||
// VARIABLES & MAPS
|
||||
// ===================================
|
||||
|
||||
// Derived colors untuk background light
|
||||
$primary-light: rgba($primary, 0.1);
|
||||
$success-light: rgba($success, 0.1);
|
||||
$warning-light: rgba($warning, 0.1);
|
||||
$danger-light: rgba($danger, 0.1);
|
||||
$info-light: rgba($info, 0.1);
|
||||
$secondary-light: rgba($secondary, 0.1);
|
||||
// Theme Colors Map
|
||||
$theme-colors: (
|
||||
"primary": #435ebe,
|
||||
"secondary": mix(#6c757d, #ffffff, 80%),
|
||||
"success": mix(#198754, #ffffff, 85%),
|
||||
"info": mix(#0dcaf0, #ffffff, 80%),
|
||||
"warning": mix(#ffc107, #ffffff, 80%),
|
||||
"danger": mix(#dc3545, #ffffff, 80%),
|
||||
);
|
||||
|
||||
// Border radius
|
||||
// Gray Colors Map
|
||||
$grays: (
|
||||
"light": #f4f7f8,
|
||||
"dark": #4c5053
|
||||
);
|
||||
|
||||
// Spacing & Sizing
|
||||
$border-radius: 0.5rem;
|
||||
$card-box-shadow: 0 0 1.25rem rgba(33,37,41,.05);
|
||||
|
||||
$border-radius-sm: 0.25rem;
|
||||
$border-radius-lg: 1rem;
|
||||
$shadow-sm: 0 2px 4px rgba(0,0,0,0.08);
|
||||
$shadow-md: 0 4px 8px rgba(0,0,0,0.12);
|
||||
$shadow-lg: 0 8px 20px rgba(0,0,0,0.15);
|
||||
|
||||
// Shadows & Transitions
|
||||
$card-box-shadow: 0 0 1.25rem rgba(33, 37, 41, 0.05);
|
||||
$shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||
$shadow-md: 0 4px 8px rgba(0, 0, 0, 0.12);
|
||||
$shadow-lg: 0 8px 20px rgba(0, 0, 0, 0.15);
|
||||
$transition: all 0.3s ease;
|
||||
|
||||
// ===================================
|
||||
// CARD COMPONENTS
|
||||
// UTILITIES (GENERATED FROM MAPS)
|
||||
// ===================================
|
||||
|
||||
.card {
|
||||
box-shadow: $card-box-shadow;
|
||||
|
||||
.card-header {
|
||||
&.bg-white {
|
||||
background-color: #fff !important;
|
||||
border-bottom: 1px solid rgba($secondary, 0.125);
|
||||
}
|
||||
@each $color, $value in $theme-colors {
|
||||
.bg-#{$color}-light {
|
||||
background-color: rgba($value, 0.25) !important;
|
||||
}
|
||||
.bg-#{$color}-soft {
|
||||
background-color: rgba($value, 0.25);
|
||||
color: $value;
|
||||
}
|
||||
.alert-#{$color} {
|
||||
background-color: rgba($value, 0.2);
|
||||
color: darken($value, 25%);
|
||||
border-color: rgba($value, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// ICON COMPONENTS
|
||||
// BASE COMPONENTS
|
||||
// ===================================
|
||||
|
||||
.icon-circle {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
.card {
|
||||
box-shadow: $card-box-shadow;
|
||||
.card-header {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid rgba(map-get($grays, "dark"), 0.1); // DIPERBAIKI
|
||||
}
|
||||
}
|
||||
|
||||
.alert {
|
||||
border-radius: $border-radius;
|
||||
box-shadow: $shadow-sm;
|
||||
.alert-icon {
|
||||
width: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.modal {
|
||||
.modal-content {
|
||||
border: none;
|
||||
border-radius: $border-radius-lg;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
|
||||
.modal-header,
|
||||
.modal-footer {
|
||||
border-color: rgba(map-get($grays, "dark"), 0.1);
|
||||
padding: 1.5rem;
|
||||
}
|
||||
.modal-title {
|
||||
color: map-get($grays, "dark"); // DIPERBAIKI
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn{
|
||||
padding: 0.5rem 1.5rem;
|
||||
font-weight: 500;
|
||||
&.btn-sm {
|
||||
padding: 0.375rem 1.25rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.badge.rounded-pill {
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// CUSTOM COMPONENTS
|
||||
// ===================================
|
||||
|
||||
.icon-circle, .icon-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: $transition;
|
||||
}
|
||||
|
||||
.section-icon,
|
||||
.chart-icon,
|
||||
.notification-icon,
|
||||
.activity-icon {
|
||||
.icon-circle {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.icon-box {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
background-color: $primary-light;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: $transition;
|
||||
|
||||
i {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// BACKGROUND COLORS
|
||||
// ===================================
|
||||
|
||||
// Light backgrounds for icon circles
|
||||
.bg-primary-light { background-color: $primary-light; }
|
||||
.bg-success-light { background-color: $success-light; }
|
||||
.bg-warning-light { background-color: $warning-light; }
|
||||
.bg-danger-light { background-color: $danger-light; }
|
||||
.bg-info-light { background-color: $info-light; }
|
||||
.bg-secondary-light { background-color: $secondary-light; }
|
||||
|
||||
// Soft badge colors
|
||||
.bg-primary-soft {
|
||||
background-color: $primary-light;
|
||||
color: $primary;
|
||||
}
|
||||
.bg-success-soft {
|
||||
background-color: $success-light;
|
||||
color: $success;
|
||||
}
|
||||
.bg-warning-soft {
|
||||
background-color: $warning-light;
|
||||
color: $warning;
|
||||
}
|
||||
.bg-danger-soft {
|
||||
background-color: $danger-light;
|
||||
color: $danger;
|
||||
}
|
||||
.bg-info-soft {
|
||||
background-color: $info-light;
|
||||
color: $info;
|
||||
}
|
||||
.bg-secondary-soft {
|
||||
background-color: $secondary-light;
|
||||
color: $secondary;
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// BOOK COMPONENTS
|
||||
// ===================================
|
||||
|
||||
.book-card {
|
||||
transition: $transition;
|
||||
box-shadow: $card-box-shadow;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: $shadow-md !important;
|
||||
|
||||
box-shadow: $shadow-md;
|
||||
.book-cover {
|
||||
transform: scale(1.03);
|
||||
}
|
||||
|
|
@ -132,180 +143,169 @@ $transition: all 0.3s ease;
|
|||
.book-cover {
|
||||
transition: $transition;
|
||||
border-radius: $border-radius-sm;
|
||||
|
||||
&-container {
|
||||
background: linear-gradient(135deg, rgba($light, 0.5) 0%, rgba($light, 0.8) 100%);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(map-get($grays, "light"), 0.5) 0%, // DIPERBAIKI
|
||||
rgba(map-get($grays, "light"), 0.8) 100% // DIPERBAIKI
|
||||
);
|
||||
border-radius: $border-radius-sm 0 0 $border-radius-sm;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===================================
|
||||
// ALERT COMPONENTS
|
||||
// ===================================
|
||||
|
||||
.alert {
|
||||
border-radius: $border-radius;
|
||||
box-shadow: $shadow-sm;
|
||||
|
||||
.alert-icon {
|
||||
width: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&.alert-primary {
|
||||
background-color: rgba($primary, 0.1);
|
||||
color: darken($primary, 10%);
|
||||
}
|
||||
|
||||
&.alert-success {
|
||||
background-color: rgba($success, 0.1);
|
||||
color: darken($success, 10%);
|
||||
}
|
||||
|
||||
&.alert-warning {
|
||||
background-color: rgba($warning, 0.1);
|
||||
color: darken($warning, 20%);
|
||||
}
|
||||
|
||||
&.alert-danger {
|
||||
background-color: rgba($danger, 0.1);
|
||||
color: darken($danger, 10%);
|
||||
}
|
||||
|
||||
&.alert-info {
|
||||
background-color: rgba($info, 0.1);
|
||||
color: darken($info, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// SECTION COMPONENTS
|
||||
// ===================================
|
||||
|
||||
.section-header {
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 2px solid $light;
|
||||
|
||||
border-bottom: 2px solid map-get($grays, "light"); // DIPERBAIKI
|
||||
h5 {
|
||||
color: $dark;
|
||||
color: map-get($grays, "dark"); // DIPERBAIKI
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// MODAL COMPONENTS
|
||||
// ===================================
|
||||
|
||||
.modal {
|
||||
.modal-content {
|
||||
border: none;
|
||||
border-radius: $border-radius-lg;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.15);
|
||||
|
||||
.modal-header {
|
||||
border-bottom: 1px solid rgba($secondary, 0.125);
|
||||
padding: 1.5rem;
|
||||
|
||||
.modal-title {
|
||||
color: $dark;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-top: 1px solid rgba($secondary, 0.125);
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// UTILITY CLASSES
|
||||
// ===================================
|
||||
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.line-clamp-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
// Rounded buttons
|
||||
.btn {
|
||||
&.rounded-pill {
|
||||
border-radius: 50px;
|
||||
padding: 0.5rem 1.5rem;
|
||||
font-weight: 500;
|
||||
|
||||
&.btn-sm {
|
||||
padding: 0.375rem 1.25rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Badge
|
||||
.badge {
|
||||
&.rounded-pill {
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// SCROLLBAR CUSTOMIZATION
|
||||
// ===================================
|
||||
|
||||
.modal-dialog-scrollable .modal-body {
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// EMPTY STATES
|
||||
// ===================================
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 3rem 1rem;
|
||||
|
||||
i {
|
||||
font-size: 4rem;
|
||||
color: $secondary;
|
||||
color: map-get($grays, "dark"); // DIPERBAIKI
|
||||
margin-bottom: 1rem;
|
||||
opacity: 0.5;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $secondary;
|
||||
color: map-get($grays, "dark"); // DIPERBAIKI
|
||||
opacity: 0.7;
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary-soft {
|
||||
--bs-btn-color: #{map-get($grays, "dark")};
|
||||
--bs-btn-bg: #{map-get($theme-colors, "primary")};
|
||||
--bs-btn-border-color: #{map-get($theme-colors, "primary")};
|
||||
--bs-btn-hover-color: #{map-get($grays, "dark")};
|
||||
--bs-btn-hover-bg: #{darken(map-get($theme-colors, "primary"), 5%)};
|
||||
--bs-btn-hover-border-color: #{darken(map-get($theme-colors, "primary"), 7.5%)};
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// UTILITIES & CUSTOMIZATIONS
|
||||
// ===================================
|
||||
|
||||
.line-clamp-2,
|
||||
.line-clamp-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.line-clamp-2 { -webkit-line-clamp: 2; }
|
||||
.line-clamp-3 { -webkit-line-clamp: 3; }
|
||||
|
||||
// Custom Scrollbar
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: map-get($grays, "light"); // DIPERBAIKI
|
||||
border-radius: 3px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(map-get($grays, "dark"), 0.25); // DIPERBAIKI
|
||||
border-radius: 3px;
|
||||
&:hover {
|
||||
background: rgba(map-get($grays, "dark"), 0.4); // DIPERBAIKI
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// Style untuk kartu pemilihan buku di dalam modal
|
||||
// ===================================
|
||||
|
||||
.card-book-select {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: $transition;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: $shadow-md;
|
||||
}
|
||||
|
||||
.card-select-overlay {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background-color: rgba(#435ebe, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: $transition;
|
||||
border-radius: $border-radius-sm;
|
||||
|
||||
i {
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border: 2px solid map-get($theme-colors, "primary");
|
||||
|
||||
.card-select-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===================================
|
||||
// Styling untuk Form Peminjaman
|
||||
// ===================================
|
||||
|
||||
.form-book-cover {
|
||||
width: 60px;
|
||||
height: 80px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.book-item {
|
||||
background-color: map-get($grays, "light");
|
||||
border-left: 4px solid map-get($theme-colors, "primary");
|
||||
}
|
||||
|
||||
// Styling untuk kartu buku di dalam modal pemilihan
|
||||
.book-card {
|
||||
cursor: pointer;
|
||||
transition: $transition;
|
||||
border: 2px solid transparent;
|
||||
|
||||
&:hover {
|
||||
border-color: map-get($theme-colors, "primary");
|
||||
box-shadow: $shadow-md;
|
||||
}
|
||||
|
||||
// Style khusus ketika checkbox di dalamnya terpilih
|
||||
&:has(.book-checkbox:checked) {
|
||||
border-color: map-get($theme-colors, "success");
|
||||
background-color: rgba(map-get($theme-colors, "success"), 0.05);
|
||||
}
|
||||
}
|
||||
.remove-book {
|
||||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.btn.disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
|
||||
.book-option[style*="display: none"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
@import 'variables';
|
||||
@import 'bootstrap/scss/bootstrap';
|
||||
@import "bootstrap/scss/functions";
|
||||
@import "variables";
|
||||
@import "bootstrap/scss/bootstrap";
|
||||
|
||||
body {
|
||||
background-color: $light;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
@props(['buku'])
|
||||
|
||||
<div class="card border-0 shadow-sm h-100 book-card">
|
||||
<div class="row g-0 h-100">
|
||||
<div class="col-4">
|
||||
<div class="book-cover-container d-flex align-items-center justify-content-center p-3 h-100">
|
||||
<img src="{{ asset($buku['cover']) }}" class="img-fluid rounded shadow-sm book-cover" alt="Cover {{ $buku['judul'] }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="card-body d-flex flex-column h-100 p-3">
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="card-title fw-bold text-dark mb-2 line-clamp-2">
|
||||
{{ $buku['judul'] }}
|
||||
</h6>
|
||||
<p class="card-subtitle text-muted small mb-3">
|
||||
<i class="bi bi-person-fill me-1"></i>{{ $buku['penulis'] }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-auto">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -7,15 +7,15 @@
|
|||
</button> --}}
|
||||
|
||||
<div class="mb-4">
|
||||
<h3>Selamat Pagi, {{ $user->nama_lengkap }} !</h3>
|
||||
<h3>{{ $greeting }}, {{ $user->nama_lengkap }} !</h3>
|
||||
<p class="text-muted">Apa yang ingin kamu baca hari ini?</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4 mb-4">
|
||||
@foreach ($stats as $stat)
|
||||
<div class="col-xl-3 col-lg-6 col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body p-4">
|
||||
<div class="card rounded-2 bg-{{ $stat['color'] }}-light border-0 h-100">
|
||||
<div class="card-body p-4 ">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="text-muted fw-normal mb-2 text-uppercase small">{{ $stat['label'] }}</h6>
|
||||
|
|
@ -46,7 +46,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<canvas id="barChart" style="max-height: 300px;"></canvas>
|
||||
<canvas id="barChart" style="max-height: 300px;" data-stats='@json($statistikBulanan ?? ['labels' => [], 'data' => []])'>
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -62,7 +63,8 @@
|
|||
</div>
|
||||
<div class="card-body text-center d-flex justify-content-center align-items-center p-4">
|
||||
<div class="chart-container" style="position: relative; height: 200px; width: 200px;">
|
||||
<canvas id="donutChart"></canvas>
|
||||
<canvas id="donutChart" data-progress='@json($progressMembaca ?? ['selesai' => 0, 'sisa' => 0])'>
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center py-3">
|
||||
|
|
@ -174,45 +176,22 @@ class="badge bg-{{ $item['type'] }}-soft text-{{ $item['type'] }} rounded-pill p
|
|||
</div>
|
||||
<h6 class="mb-0 fw-bold text-dark">Buku Pinjam Offline</h6>
|
||||
</div>
|
||||
<button type-="button" :class="expanded ? 'btn btn-sm btn-primary rounded-pill px-3' : 'btn btn-sm btn-outline-primary rounded-pill px-3'"
|
||||
<button type="button"
|
||||
:class="expanded ? 'btn btn-sm btn-primary rounded-pill px-3' :
|
||||
'btn btn-sm btn-outline-primary rounded-pill px-3'"
|
||||
@click="expanded = !expanded" x-show="{{ count($bukuPinjamOffline) > 2 }}">
|
||||
<span x-text="expanded ? 'Tampilkan Lebih Sedikit' : 'Lihat Semua'">Lihat Semua</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
@forelse($bukuPinjamOffline as $buku)
|
||||
<div class="col-xl-4 col-md-6" x-show="{{ $loop->index }} < 3 || expanded" x-transition>
|
||||
<div class="card border-0 shadow-sm h-100 book-card">
|
||||
<div class="row g-0 h-100">
|
||||
<div class="col-4">
|
||||
<div
|
||||
class="book-cover-container d-flex align-items-center justify-content-center p-3 h-100">
|
||||
<img src="{{ asset($buku['cover']) }}"
|
||||
class="img-fluid rounded shadow-sm book-cover"
|
||||
alt="Cover {{ $buku['judul'] }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="card-body d-flex flex-column h-100 p-3">
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="card-title fw-bold text-dark mb-2 line-clamp-2">
|
||||
{{ $buku['judul'] }}</h6>
|
||||
<p class="card-subtitle text-muted small mb-2">
|
||||
<i class="bi bi-person-fill me-1"></i>{{ $buku['penulis'] }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-auto">
|
||||
<div
|
||||
class="alert alert-danger border-0 py-2 px-3 mb-0 d-flex align-items-center">
|
||||
<i class="bi bi-clock-fill me-2"></i>
|
||||
<span class="fw-bold small">Sisa: {{ $buku['sisa_hari'] }}
|
||||
hari</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-4 col-md-6" x-show="{{ $loop->index }} < 2 || expanded" x-transition>
|
||||
<x-book-card :buku="$buku">
|
||||
<div class="alert alert-danger border-0 py-2 px-3 mb-0 d-flex align-items-center">
|
||||
<i class="bi bi-clock-fill me-2"></i>
|
||||
<span class="fw-bold small">Sisa: {{ $buku['sisa_hari'] }} hari</span>
|
||||
</div>
|
||||
</div>
|
||||
</x-book-card>
|
||||
</div>
|
||||
@empty
|
||||
<div class="col-12">
|
||||
|
|
@ -233,7 +212,9 @@ class="alert alert-danger border-0 py-2 px-3 mb-0 d-flex align-items-center">
|
|||
</div>
|
||||
<h6 class="mb-0 fw-bold text-dark">Baca Buku Online</h6>
|
||||
</div>
|
||||
<button type="button" :class="expanded ? 'btn btn-sm btn-primary rounded-pill px-3' : 'btn btn-sm btn-outline-primary rounded-pill px-3'"
|
||||
<button type="button"
|
||||
:class="expanded ? 'btn btn-sm btn-primary rounded-pill px-3' :
|
||||
'btn btn-sm btn-outline-primary rounded-pill px-3'"
|
||||
@click="expanded = !expanded" x-show="{{ count($bacaBukuOnline) > 3 }}">
|
||||
<span x-text="expanded ? 'Tampilkan Lebih Sedikit' : 'Lihat Semua'">Lihat Semua</span>
|
||||
</button>
|
||||
|
|
@ -241,45 +222,23 @@ class="alert alert-danger border-0 py-2 px-3 mb-0 d-flex align-items-center">
|
|||
<div class="row g-4">
|
||||
@forelse($bacaBukuOnline as $buku)
|
||||
<div class="col-xl-4 col-md-6" x-show="{{ $loop->index }} < 3 || expanded" x-transition>
|
||||
<div class="card border-0 shadow-sm h-100 book-card">
|
||||
<div class="row g-0 h-100">
|
||||
<div class="col-4">
|
||||
<div
|
||||
class="book-cover-container d-flex align-items-center justify-content-center p-3 h-100">
|
||||
<img src="{{ asset($buku['cover']) }}"
|
||||
class="img-fluid rounded shadow-sm book-cover"
|
||||
alt="Cover {{ $buku['judul'] }}">
|
||||
</div>
|
||||
|
||||
<x-book-card :buku="$buku">
|
||||
<div class="progress-wrapper">
|
||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||||
<span class="small text-muted">Progress</span>
|
||||
<span class="small fw-bold text-primary">{{ $buku['progress'] }}%</span>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="card-body d-flex flex-column h-100 p-3">
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="card-title fw-bold text-dark mb-2 line-clamp-2">
|
||||
{{ $buku['judul'] }}</h6>
|
||||
<p class="card-subtitle text-muted small mb-3">
|
||||
<i class="bi bi-person-fill me-1"></i>{{ $buku['penulis'] }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-auto">
|
||||
<div class="progress-wrapper">
|
||||
<div
|
||||
class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="small text-muted">Progress</span>
|
||||
<span
|
||||
class="small fw-bold text-primary">{{ $buku['progress'] }}%</span>
|
||||
</div>
|
||||
<div class="progress" style="height: 8px;">
|
||||
<div class="progress-bar bg-gradient"
|
||||
style="width: {{ $buku['progress'] }}%"
|
||||
role="progressbar">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress" style="height: 8px;">
|
||||
<div class="progress-bar bg-primary rounded-pill"
|
||||
style="width: {{ $buku['progress'] }}%" role="progressbar"
|
||||
aria-valuenow="{{ $buku['progress'] }}" aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-book-card>
|
||||
|
||||
</div>
|
||||
@empty
|
||||
<div class="col-12">
|
||||
|
|
@ -368,87 +327,13 @@ class="badge bg-{{ $item['type'] }}-soft text-{{ $item['type'] }} rounded-pill p
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const statistikData = @json($statistikBulanan ?? ['labels' => [], 'data' => []]);
|
||||
const progressData = @json($progressMembaca ?? ['selesai' => 0, 'sisa' => 0]);
|
||||
|
||||
// Bar Chart with improved styling
|
||||
const ctxBar = document.getElementById('barChart').getContext('2d');
|
||||
new Chart(ctxBar, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: statistikData.labels,
|
||||
datasets: [{
|
||||
label: 'Buku Dibaca',
|
||||
data: statistikData.data,
|
||||
backgroundColor: '#435ebe',
|
||||
borderRadius: 8,
|
||||
borderSkipped: false,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: 'rgba(0,0,0,0.05)',
|
||||
},
|
||||
ticks: {
|
||||
color: '#6c757d',
|
||||
font: {
|
||||
size: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
x: {
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
ticks: {
|
||||
color: '#6c757d',
|
||||
font: {
|
||||
size: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Donut Chart
|
||||
const ctxDonut = document.getElementById('donutChart').getContext('2d');
|
||||
new Chart(ctxDonut, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Telah Dibaca', 'Belum Dibaca'],
|
||||
datasets: [{
|
||||
data: [progressData.selesai, progressData.sisa],
|
||||
backgroundColor: ['#435ebe', '#e9ecef'],
|
||||
borderColor: ['#435ebe', '#dee2e6'],
|
||||
borderWidth: 3,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
cutout: '75%'
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
|
||||
<script src="{{ asset('js/dashboard-charts.js') }}"></script>
|
||||
</x-app-layout>
|
||||
|
|
|
|||
|
|
@ -61,27 +61,48 @@
|
|||
|
||||
<div class="card-body d-flex flex-column p-3">
|
||||
<div class="flex-grow-1">
|
||||
{{-- Badge diletakkan di atas judul --}}
|
||||
<div class="mb-2">
|
||||
<span class="badge fw-normal bg-light text-dark border me-1">{{ $buku['kategori'] }}</span>
|
||||
<span class="badge fw-normal text-primary border me-1">{{ $buku['kategori'] }}</span>
|
||||
<span
|
||||
class="badge fw-normal {{ $buku['status'] == 'Tersedia' ? 'bg-success-subtle text-success-emphasis' : 'bg-warning-subtle text-warning-emphasis' }} border">{{ $buku['status'] }}</span>
|
||||
</div>
|
||||
<h5 class="card-title fw-bold ">{{ $buku['judul'] }}</h5>
|
||||
<h6 class="card-title fw-bold line-clamp-2">{{ $buku['judul'] }}</h6>
|
||||
<p class="card-text small text-muted mb-2">{{ $buku['penulis'] }}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="d-grid mt-auto">
|
||||
@if ($buku['status'] == 'Dipinjam')
|
||||
<button class="btn btn-secondary btn-sm" disabled><i class="bi bi-x-circle me-1"></i>
|
||||
Tidak Tersedia</button>
|
||||
@else
|
||||
@if ($buku['tipe_akses'] == 'online')
|
||||
@if ($buku['status'] == 'Tersedia')
|
||||
@if (is_array($buku['tipe_akses']))
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{{ route('peminjaman.ringkasan', $buku['id']) }}"
|
||||
class="btn btn-outline-primary btn-sm w-100">
|
||||
<i class="bi bi-arrow-down-up me-1"></i> Pinjam
|
||||
</a>
|
||||
|
||||
<button class="btn btn-primary btn-sm w-100"><i class="bi bi-book me-1"></i>
|
||||
Baca</button>
|
||||
</div>
|
||||
@elseif ($buku['tipe_akses'] == 'online')
|
||||
<button class="btn btn-primary btn-sm"><i class="bi bi-book me-1"></i> Baca
|
||||
Online</button>
|
||||
@else
|
||||
<button class="btn btn-outline-primary btn-sm"><i
|
||||
class="bi bi-arrow-down-up me-1"></i> Pinjam Offline</button>
|
||||
<a href="{{ route('peminjaman.ringkasan', $buku['id']) }}"
|
||||
class="btn btn-outline-primary btn-sm">
|
||||
<i class="bi bi-arrow-down-up me-1"></i> Pinjam Offline
|
||||
</a>
|
||||
@endif
|
||||
@else
|
||||
@if (is_array($buku['tipe_akses']))
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-secondary btn-sm w-100" disabled><i
|
||||
class="bi bi-x-circle me-1"></i> Dipinjam</button>
|
||||
<button class="btn btn-primary btn-sm w-100"><i class="bi bi-book me-1"></i>
|
||||
Baca</button>
|
||||
</div>
|
||||
@else
|
||||
<button class="btn btn-secondary btn-sm" disabled><i
|
||||
class="bi bi-x-circle me-1"></i> Tidak Tersedia</button>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,9 +10,15 @@
|
|||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
@vite(['resources/scss/app.scss', 'resources/js/app.js'])
|
||||
<!-- Flatpickr CSS & JS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/themes/material_blue.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
|
||||
<script src="https://npmcdn.com/flatpickr/dist/l10n/id.js"></script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #f4f7f8;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
|
|
@ -156,6 +162,7 @@ function toggleDesktopSidebar() {
|
|||
});
|
||||
});
|
||||
</script>
|
||||
@stack('scripts')
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@
|
|||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link">
|
||||
<a href="{{ route('peminjaman.index') }}"
|
||||
class="nav-link {{ request()->routeIs('peminjaman.*') ? 'active' : '' }}">
|
||||
<i class="bi bi-arrow-left-right"></i>
|
||||
<span class="nav-text ms-2">Peminjaman Offline</span>
|
||||
</a>
|
||||
|
|
@ -58,4 +59,4 @@
|
|||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
</aside>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,304 @@
|
|||
<x-app-layout>
|
||||
<div class="d-flex align-items-center mb-4">
|
||||
<a href="{{ route('peminjaman.ringkasan', $buku['id']) }}" class="btn btn-outline-secondary me-3">
|
||||
<i class="bi bi-arrow-left"></i>
|
||||
</a>
|
||||
<h1 class="h2 mb-0">Form Peminjaman Offline</h1>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-4 p-md-5">
|
||||
|
||||
{{-- Informasi Peminjam --}}
|
||||
<div class="mb-5">
|
||||
<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="#" class="btn btn-sm btn-outline-secondary ">Edit Profil</a>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-muted small">Nama Lengkap</label>
|
||||
<p class="fw-semibold">{{ $user['nama_lengkap'] }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-muted small">Nomor Handphone</label>
|
||||
<p class="fw-semibold">{{ $user['nomor_hp'] }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-muted small">Kelas</label>
|
||||
<p class="fw-semibold">{{ $user['kelas'] }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-muted small">Golongan</label>
|
||||
<p class="fw-semibold">{{ $user['golongan'] }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Form untuk submit data --}}
|
||||
<form action="{{ route('peminjaman.store') }}" method="POST" id="formPeminjaman"
|
||||
data-semua-buku="{{ json_encode($semuaBuku->values()) }}"
|
||||
data-buku-awal="{{ json_encode($buku) }}">
|
||||
@csrf
|
||||
|
||||
{{-- Informasi Buku & Tanggal --}}
|
||||
<div class="mb-5">
|
||||
<div class="d-flex justify-content-between align-items-center border-bottom pb-2 mb-3">
|
||||
<h5 class="fw-bold m-0">Buku yang Dipinjam</h5>
|
||||
<button type="button" class="btn btn-sm btn-primary " data-bs-toggle="modal"
|
||||
data-bs-target="#pilihBukuModal" id="btnTambahBuku">
|
||||
<i class="bi bi-plus-circle me-1"></i>Tambah Buku
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- Container untuk daftar buku yang dipinjam --}}
|
||||
<div id="daftarBukuPinjam" class="mb-4">
|
||||
<div class="book-item border rounded p-3 mb-3" data-book-id="{{ $buku['id'] }}">
|
||||
<div class="d-flex align-items-start">
|
||||
<img src="{{ asset($buku['cover']) }}" alt="Cover"
|
||||
class="rounded me-3 form-book-cover">
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="fw-bold mb-1">{{ $buku['judul'] }}</h6>
|
||||
<p class="text-muted small mb-1">{{ $buku['penulis'] }}</p>
|
||||
<span class="badge bg-info">{{ $buku['kategori'] }}</span>
|
||||
</div>
|
||||
<span class="badge bg-success">Buku Utama</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Hidden inputs untuk buku yang dipilih --}}
|
||||
<div id="hiddenInputs">
|
||||
<input type="hidden" name="buku_ids[]" value="{{ $buku['id'] }}">
|
||||
</div>
|
||||
|
||||
{{-- Tanggal Peminjaman --}}
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="tanggalPinjam" class="form-label text-muted">Tanggal Pinjam</label>
|
||||
<input type="text" id="tanggalPinjam" name="tanggal_pinjam" class="form-control"
|
||||
placeholder="Pilih tanggal pinjam"
|
||||
value="{{ \Carbon\Carbon::now()->format('d F Y') }}">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="tanggalKembali" class="form-label text-muted">Tanggal Kembali</label>
|
||||
<input type="text" id="tanggalKembali" name="tanggal_kembali"
|
||||
placeholder="Pilih tanggal kembali" class="form-control"
|
||||
value="{{ \Carbon\Carbon::now()->addDays(7)->format('d F Y') }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Peraturan Peminjaman --}}
|
||||
<div class="mb-4">
|
||||
<h5 class="fw-bold border-bottom pb-2 mb-3">Peraturan Peminjaman</h5>
|
||||
<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>Buku yang dipinjam harus dikembalikan dalam jangka waktu yang telah ditentukan.
|
||||
</li>
|
||||
<li>Jika buku tidak dikembalikan tepat waktu, akan dikenakan denda atau sanksi
|
||||
lainnya.</li>
|
||||
<li>Buku yang dikembalikan harus dalam kondisi baik, tanpa kerusakan atau hilang.
|
||||
</li>
|
||||
<li>Dilarang merusak atau mencoret-coret buku.</li>
|
||||
<li>Setiap siswa bertanggung jawab penuh terhadap buku yang dipinjam.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Tombol Kirim Form --}}
|
||||
<div class="d-grid">
|
||||
<button type="button" class="btn btn-primary btn-lg " data-bs-toggle="modal"
|
||||
data-bs-target="#konfirmasiModal">
|
||||
Kirim Form
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Modal Pilih Buku --}}
|
||||
<div class="modal fade" id="pilihBukuModal" tabindex="-1" aria-labelledby="pilihBukuModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-content border-0 shadow-lg rounded-3">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="modal-header bg-light">
|
||||
<h5 class="modal-title fw-bold" id="pilihBukuModalLabel">
|
||||
<i class="bi bi-book-half me-2 text-primary"></i>Pilih Buku Tambahan
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div class="modal-body">
|
||||
|
||||
<!-- Alert Info -->
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<!-- Search Box -->
|
||||
<div class="mb-3">
|
||||
<div class="input-group shadow-sm">
|
||||
<span class="input-group-text bg-white"><i class="bi bi-search"></i></span>
|
||||
<input type="text" class="form-control border-start-0" id="searchBuku"
|
||||
placeholder="Cari buku...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Daftar Buku -->
|
||||
<div id="daftarBukuModal" class="row g-3">
|
||||
@foreach ($semuaBuku as $bukuItem)
|
||||
@if ($bukuItem['status'] == 'Tersedia' || $bukuItem['id'] == $buku['id'])
|
||||
<div class="col-12 book-option" data-book-id="{{ $bukuItem['id'] }}"
|
||||
data-book-title="{{ strtolower($bukuItem['judul']) }}"
|
||||
data-book-author="{{ strtolower($bukuItem['penulis']) }}">
|
||||
|
||||
<div class="card book-card h-100 border-0 shadow-sm hover-shadow transition"
|
||||
onclick="toggleBookSelection({{ $bukuItem['id'] }})">
|
||||
|
||||
<div class="card-body p-3">
|
||||
<div class="d-flex align-items-start">
|
||||
|
||||
<!-- Cover Buku -->
|
||||
<img src="{{ asset($bukuItem['cover']) }}" alt="Cover"
|
||||
class="rounded me-3 form-book-cover"
|
||||
style="width: 60px; height: 80px; object-fit: cover;">
|
||||
|
||||
<!-- Detail Buku -->
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="fw-bold mb-1">{{ $bukuItem['judul'] }}</h6>
|
||||
<p class="text-muted small mb-1">{{ $bukuItem['penulis'] }}</p>
|
||||
<div class="d-flex align-items-center flex-wrap gap-2">
|
||||
<span class="badge bg-info">{{ $bukuItem['kategori'] }}</span>
|
||||
<span class="badge bg-success"><i
|
||||
class="bi bi-check-circle me-1"></i>Tersedia</span>
|
||||
@if ($bukuItem['id'] == $buku['id'])
|
||||
<span class="badge bg-primary"><i
|
||||
class="bi bi-star-fill me-1"></i>Buku Utama</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Checkbox -->
|
||||
<div class="form-check ms-2">
|
||||
<input class="form-check-input book-checkbox" type="checkbox"
|
||||
value="{{ $bukuItem['id'] }}" id="book{{ $bukuItem['id'] }}"
|
||||
@if ($bukuItem['id'] == $buku['id']) checked disabled @endif
|
||||
onclick="event.stopPropagation()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@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>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">
|
||||
<i class="bi bi-x-circle me-1"></i> Batal
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" id="konfirmasiBuku"
|
||||
onclick="konfirmasiPilihanBuku()">
|
||||
<i class="bi bi-check2-circle me-1"></i> Konfirmasi Pilihan
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{-- Modal Konfirmasi Kirim --}}
|
||||
<div class="modal fade" id="konfirmasiModal" tabindex="-1" aria-labelledby="konfirmasiModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content border-0 shadow-lg">
|
||||
<div class="modal-header border-0 text-center d-block">
|
||||
<h5 class="modal-title fw-bold" id="konfirmasiModalLabel">Apakah Anda Yakin Ingin Mengirim Form?
|
||||
</h5>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<div class="text-muted mb-3">Pastikan data yang Anda kirim sudah benar.</div>
|
||||
<div class="alert alert-info border-0 bg-info-subtle text-start">
|
||||
<strong>Ringkasan Peminjaman:</strong>
|
||||
<div id="ringkasanBuku" class="mt-2"></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>
|
||||
|
||||
{{-- Button Setuju jika sudah ada backend --}}
|
||||
{{-- <button type="button" class="btn btn-primary px-4" onclick="kirimForm()">Setuju</button> --}}
|
||||
|
||||
{{-- Button Setuju jika belum ada backend, akan ter direct ke peminjaman.index --}}
|
||||
<a href="{{ route('peminjaman.index') }}" class="btn btn-primary px-4">
|
||||
Setuju
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const tglPinjam = flatpickr("#tanggalPinjam", {
|
||||
dateFormat: "d F Y",
|
||||
altInput: true,
|
||||
altFormat: "d F Y",
|
||||
defaultDate: "{{ \Carbon\Carbon::now()->format('Y-m-d') }}",
|
||||
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));
|
||||
}
|
||||
});
|
||||
|
||||
const tglKembali = flatpickr("#tanggalKembali", {
|
||||
dateFormat: "d F Y",
|
||||
altInput: true,
|
||||
altFormat: "d F Y",
|
||||
defaultDate: "{{ \Carbon\Carbon::now()->addDays(7)->format('Y-m-d') }}",
|
||||
locale: "id",
|
||||
minDate: new Date().fp_incr(1)
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</x-app-layout>
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
<x-app-layout>
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">Daftar Buku Peminjaman Offline</h1>
|
||||
</div>
|
||||
|
||||
<!-- Filter & Pencarian -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<form action="{{ route('peminjaman.index') }}" method="GET">
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col-md-4">
|
||||
<input type="text" name="search" class="form-control"
|
||||
placeholder="Cari buku berdasarkan judul..." value="{{ $input['search'] ?? '' }}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select name="kategori" class="form-select">
|
||||
<option value="">Semua Kategori</option>
|
||||
@foreach ($filterOptions['kategori'] as $kategori)
|
||||
<option value="{{ $kategori }}"
|
||||
{{ ($input['kategori'] ?? '') == $kategori ? 'selected' : '' }}>{{ $kategori }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select name="tahun" class="form-select">
|
||||
<option value="">Semua Tahun</option>
|
||||
@foreach ($filterOptions['tahun'] as $tahun)
|
||||
<option value="{{ $tahun }}"
|
||||
{{ ($input['tahun'] ?? '') == $tahun ? 'selected' : '' }}>{{ $tahun }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select name="penulis" class="form-select">
|
||||
<option value="">Semua Penulis</option>
|
||||
@foreach ($filterOptions['penulis'] as $penulis)
|
||||
<option value="{{ $penulis }}"
|
||||
{{ ($input['penulis'] ?? '') == $penulis ? 'selected' : '' }}>{{ $penulis }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 d-grid">
|
||||
<button type="submit" class="btn btn-primary">Filter</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="fw-bold">Daftar buku yang tersedia secara offline dan dapat diambil di perpustakaan dengan mengisi form
|
||||
yang tersedia.</p>
|
||||
|
||||
<!-- Daftar Buku -->
|
||||
<div class="row row-cols-2 row-cols-md-3 row-cols-lg-5 g-4">
|
||||
@forelse ($semuaBuku as $buku)
|
||||
<div class="col">
|
||||
<div class="card h-100 shadow-sm border-0">
|
||||
<img src="{{ asset($buku['cover']) }}" class="card-img-top rounded-2 object-fit-cover"
|
||||
style="height: 310px;" alt="Cover {{ $buku['judul'] }}">
|
||||
|
||||
<div class="card-body d-flex flex-column p-3">
|
||||
<div class="flex-grow-1">
|
||||
<div class="mb-2">
|
||||
<span class="badge fw-normal text-primary border me-1">{{ $buku['kategori'] }}</span>
|
||||
<span
|
||||
class="badge fw-normal {{ $buku['status'] == 'Tersedia' ? 'bg-success-subtle text-success-emphasis' : 'bg-warning-subtle text-warning-emphasis' }} border">{{ $buku['status'] }}</span>
|
||||
</div>
|
||||
<h6 class="card-title fw-bold line-clamp-2">{{ $buku['judul'] }}</h6>
|
||||
<p class="card-text small text-muted mb-2">{{ $buku['penulis'] }}</p>
|
||||
</div>
|
||||
|
||||
<div class="d-grid mt-auto">
|
||||
@if ($buku['status'] == 'Tersedia')
|
||||
<a href="{{ route('peminjaman.ringkasan', $buku['id']) }}"
|
||||
class="btn btn-outline-primary btn-sm">
|
||||
<i class="bi bi-arrow-down-up me-1"></i> Pinjam Offline
|
||||
</a>
|
||||
@else
|
||||
<button class="btn btn-secondary btn-sm" disabled><i class="bi bi-x-circle me-1"></i>
|
||||
Tidak Tersedia</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="col-12">
|
||||
<div class="alert alert-warning text-center">
|
||||
<h4 class="alert-heading">Tidak Ada Hasil</h4>
|
||||
<p>Tidak ada buku yang cocok dengan kriteria filter Anda. Coba reset atau ubah filter.</p>
|
||||
<hr>
|
||||
<a href="{{ route('peminjaman') }}" class="btn btn-primary">Reset Filter</a>
|
||||
</div>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<x-app-layout>
|
||||
<div class="d-flex flex-column" style="min-height: calc(100vh - 110px);">
|
||||
|
||||
{{-- Header --}}
|
||||
<div class="d-flex align-items-center mb-4">
|
||||
<a href="{{ route('peminjaman.index')}}" class="btn btn-outline-secondary me-3">
|
||||
<i class="bi bi-arrow-left"></i>
|
||||
</a>
|
||||
<h1 class="h2 mb-0">Ringkasan Buku</h1>
|
||||
</div>
|
||||
|
||||
{{-- Wrapper main content --}}
|
||||
<main class="flex-grow-1 d-flex align-items-center justify-content-center">
|
||||
<div class="container-fluid">
|
||||
<div class="row g-5 align-items-center">
|
||||
|
||||
{{-- Kolom Kiri untuk Cover Buku --}}
|
||||
<div class="col-lg-4 text-center">
|
||||
<img src="{{ asset($buku['cover']) }}" class="img-fluid rounded-3 shadow-lg"
|
||||
style="max-height: 450px; max-width: 300px; object-fit: cover;" alt="Cover {{ $buku['judul'] }}">
|
||||
</div>
|
||||
|
||||
{{-- Kolom Kanan untuk Detail & Button --}}
|
||||
<div class="col-lg-8">
|
||||
<h1 class="display-5 fw-bold mb-2">{{ $buku['judul'] }}</h1>
|
||||
<p class="h5 text-muted mb-4">oleh {{ $buku['penulis'] }}</p>
|
||||
|
||||
<p class="lead" style="text-align: justify;">
|
||||
Ini adalah ringkasan atau sinopsis dari buku "{{ $buku['judul'] }}". Lorem ipsum dolor sit
|
||||
amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore
|
||||
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
|
||||
aliquip ex ea commodo consequat.
|
||||
</p>
|
||||
|
||||
<div class="mt-5">
|
||||
<a href="{{ route('peminjaman.form', $buku['id']) }}" class="btn btn-primary btn-lg rounded-pill px-5 py-3">
|
||||
<i class="bi bi-file-earmark-text-fill me-2"></i>
|
||||
Lanjutkan ke Form Peminjaman
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="mt-auto text-center text-muted py-3">
|
||||
<small>Copyright © {{ date('Y') }} Perpus.</small>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use App\Http\Controllers\DashboardController;
|
||||
use App\Http\Controllers\KatalogController;
|
||||
use App\Http\Controllers\PeminjamanController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
|
|
@ -15,6 +16,11 @@
|
|||
Route::get('/katalog', [KatalogController::class, 'index'])
|
||||
->middleware('auth')->name('katalog');
|
||||
|
||||
// Route untuk peminjaman offline
|
||||
Route::get('/peminjaman-offline', [PeminjamanController::class, 'index'])->name('peminjaman.index');
|
||||
Route::get('/peminjaman-offline/{id}/ringkasan', [PeminjamanController::class, 'ringkasan'])->name('peminjaman.ringkasan');
|
||||
Route::get('/peminjaman-offline/{id}/form', [PeminjamanController::class, 'form'])->name('peminjaman.form');
|
||||
Route::post('/peminjaman/store', [PeminjamanController::class, 'store'])->name('peminjaman.store');
|
||||
|
||||
// Route untuk user profile dari laravel breeze
|
||||
Route::middleware('auth')->group(function () {
|
||||
|
|
|
|||
Loading…
Reference in New Issue