TKK_E32230908/resources/views/admin/koleksi/index.blade.php

408 lines
21 KiB
PHP

@extends('layouts.koleksi')
@section('title', 'Manajemen Koleksi')
@section('content')
@php
use Picqer\Barcode\BarcodeGeneratorPNG;
@endphp
<!-- TOOLBAR PENCARIAN -->
<div class="card border-0 shadow-sm mb-4 search-toolbar">
<div class="card-body p-3">
<form method="GET" action="{{ route('admin.koleksi.index') }}" class="row g-3 align-items-center">
<div class="col-md-5">
<div class="search-box">
<i class="fas fa-search search-icon"></i>
<input type="text" name="search" class="form-control form-control-lg border-0 bg-light"
placeholder="Cari judul, klasifikasi, atau kode eksemplar..." value="{{ request('search') }}">
</div>
</div>
<div class="col-auto d-flex gap-2">
<button class="btn btn-primary-custom px-4 h-100" type="submit">
<i class="fas fa-filter me-2"></i>Cari
</button>
@if(request('search'))
<a href="{{ route('admin.koleksi.index') }}" class="btn btn-outline-danger px-4 h-100 d-inline-flex align-items-center justify-content-center fw-semibold text-nowrap rounded-3" title="Batal Cari">
<i class="fas fa-undo me-2"></i>Batal
</a>
@endif
</div>
</form>
</div>
</div>
<!-- NOTIFIKASI SUKSES / GAGAL -->
@if (session('success'))
<div class="alert alert-custom-success alert-dismissible fade show mb-4 border-0 shadow-sm" role="alert">
<div class="d-flex align-items-center">
<div class="alert-icon-circle me-3"><i class="fas fa-check"></i></div>
<div>
<strong class="d-block">Berhasil!</strong>
<span class="small">{{ session('success') }}</span>
</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
@if (session('error'))
<div class="alert alert-danger alert-dismissible fade show mb-4 border-0 shadow-sm" role="alert">
<div class="d-flex align-items-center">
<div class="alert-icon-circle bg-danger me-3"><i class="fas fa-exclamation-triangle text-white"></i></div>
<div>
<strong class="d-block">Gagal!</strong>
<span class="small">{{ session('error') }}</span>
</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
<!-- FORM UTAMA DENGAN TABEL SEJAJAR -->
<form id="bulkActionForm" method="POST">
@csrf
<div class="card border-0 shadow-sm overflow-hidden main-table-card">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th class="text-center" width="60">NO</th>
<th>DETAIL KOLEKSI</th>
<th class="text-center" width="140">KLASIFIKASI</th>
<th class="text-center" width="160">BARCODE & KODE</th>
<th class="text-center" width="160">AKTIVITAS</th>
<th class="text-center" width="150">PILIH CETAK/HAPUS</th>
<th class="text-center pe-4" width="120">OPSI</th>
</tr>
</thead>
<tbody>
@php $no = $results->firstItem(); @endphp
@forelse($results as $koleksi)
<tr>
<td class="text-center fw-bold text-muted small">{{ $no++ }}</td>
<td>
<div class="d-flex align-items-center">
@if($koleksi->image && Storage::disk('public')->exists($koleksi->image))
<div class="img-wrapper me-3">
<img src="{{ Storage::url($koleksi->image) }}" class="book-cover shadow-sm">
</div>
@else
<div class="book-placeholder me-3 shadow-sm">
<i class="fas fa-book text-secondary"></i>
</div>
@endif
<div class="info-content">
<div class="fw-bold text-dark text-truncate mb-0" style="max-width: 250px;" title="{{ $koleksi->title }}">
{{ $koleksi->title ?? 'Judul Tidak Tersedia' }}
</div>
<span class="text-muted small fw-medium">ISBN: {{ $koleksi->isbn_issn ?? 'N/A' }}</span>
</div>
</div>
</td>
<td class="text-center">
<span class="badge-custom-primary">{{ $koleksi->classification ?? '-' }}</span>
</td>
<td class="text-center">
<div class="barcode-wrapper mb-1">
@if($koleksi->kode_eksemplar)
@php
$barcodeGen = new Picqer\Barcode\BarcodeGeneratorPNG();
$barcodeImg = base64_encode($barcodeGen->getBarcode($koleksi->kode_eksemplar, $barcodeGen::TYPE_CODE_128));
@endphp
<img src="data:image/png;base64,{{ $barcodeImg }}" class="barcode-img">
@else
<span class="text-muted smaller">N/A</span>
@endif
</div>
<code class="text-primary-dark small">{{ $koleksi->kode_eksemplar }}</code>
</td>
<td class="text-center">
<div class="activity-log">
<div class="small text-muted"><i class="far fa-calendar-alt me-1"></i> {{ $koleksi->input_date ? \Carbon\Carbon::parse($koleksi->input_date)->format('d/m/Y') : '-' }}</div>
<div class="small fw-bold text-primary-custom mt-1"><i class="fas fa-sync-alt me-1"></i> {{ $koleksi->last_update ? \Carbon\Carbon::parse($koleksi->last_update)->diffForHumans() : '-' }}</div>
</div>
</td>
<!-- KOLOM CHECKBOX (DIJAMIN LURUS TEPAT DI TENGAH) -->
<td class="text-center">
@if($koleksi->kode_eksemplar)
<div class="d-flex justify-content-center align-items-center" style="min-height: 40px;">
<input type="checkbox" name="kode_eksemplar[]" value="{{ $koleksi->kode_eksemplar }}" class="form-check-input row-checkbox custom-checkbox m-0">
</div>
@else
<div class="d-flex justify-content-center align-items-center" style="min-height: 40px;">
<span class="text-muted small">-</span>
</div>
@endif
</td>
<!-- KOLOM OPSI EDIT (SEJAJAR DAN TIDAK AKAN TURUN KE BAWAH) -->
<td class="text-center pe-4">
<div class="d-flex justify-content-center align-items-center" style="min-height: 40px;">
<a href="{{ route('admin.koleksi.edit', ['kode_eksemplar' => $koleksi->kode_eksemplar]) }}"
class="btn btn-action-edit text-nowrap m-0" title="Ubah Data">
<i class="fas fa-pen-nib me-1"></i><span>Edit Koleksi</span>
</a>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="text-center py-5">
<div class="empty-state py-4">
<i class="fas fa-folder-open fa-3x text-light mb-3"></i>
<h5 class="text-muted">Data tidak ditemukan</h5>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<!-- PAGINASI -->
<div class="card-footer bg-white border-0 py-4">
<div class="d-flex justify-content-center">
{{ $results->links('pagination::bootstrap-5') }}
</div>
</div>
</div>
<!-- FLOATING BAR UNTUK MULTIPLE ACTION -->
<div id="floatingActionBar" class="floating-action-bar d-none">
<div class="d-flex align-items-center justify-content-between px-4 py-3 bg-white shadow-lg rounded-4 border">
<div>
<i class="fas fa-info-circle text-primary me-2"></i>
<span class="fw-bold"><span id="selectedCount">0</span> terpilih</span>
</div>
<div class="d-flex gap-2">
<button type="button" id="selectAllPage" class="btn btn-outline-primary rounded-3 btn-sm fw-semibold">Pilih Semua di Halaman Ini</button>
<button type="button" id="clearSelection" class="btn btn-light rounded-3 btn-sm fw-semibold">Batal</button>
<button type="button" id="submitBulkDeleteBtn" class="btn btn-action-delete rounded-3 btn-sm px-4 fw-semibold" disabled>
<i class="fas fa-trash me-2"></i>Hapus Terpilih
</button>
<button type="button" id="submitBulkPrintBtn" class="btn btn-primary-custom rounded-3 btn-sm px-4" disabled>
<i class="fas fa-print me-2"></i>Cetak Label Multiple
</button>
</div>
</div>
</div>
</form>
<style>
/* Desain Dasar */
html, body { -ms-overflow-style: none; scrollbar-width: none; }
:root {
--primary: #4361ee;
--primary-light: #eef2ff;
--primary-dark: #3730a3;
--success-bg: #ecfdf5;
--success-text: #065f46;
--bg-light: #f8fafc;
--text-main: #1e293b;
--text-muted: #64748b;
--btn-edit-bg: #e0e7ff;
--btn-edit-text: #4338ca;
--btn-delete-bg: #fee2e2;
--btn-delete-text: #b91c1c;
}
body { background-color: var(--bg-light); font-family: 'Inter', sans-serif; color: var(--text-main); }
/* Desain Header Tabel Presisi */
table thead th {
background: #f8fafc;
color: var(--text-muted);
font-size: 0.70rem !important;
font-weight: 800 !important;
text-transform: uppercase;
letter-spacing: 0.05em;
padding: 1rem 0.5rem !important;
border-bottom: 2px solid #e2e8f0 !important;
vertical-align: middle !important;
}
.search-toolbar { border-radius: 16px; }
.search-box { position: relative; }
.search-icon { position: absolute; left: 1.25rem; top: 50%; transform: translateY(-50%); color: var(--text-muted); z-index: 5; }
.search-box .form-control { padding-left: 3.2rem; border-radius: 12px; font-size: 0.95rem; }
.btn-primary-custom { background: var(--primary); color: white; border-radius: 12px; font-weight: 600; border: none; }
.btn-primary-custom:disabled { background: #cbd5e1; color: #94a3b8; cursor: not-allowed; }
/* Ukuran Custom Checkbox */
.custom-checkbox { width: 1.25rem; height: 1.25rem; border-radius: 6px !important; cursor: pointer; }
.main-table-card { border-radius: 16px; }
table tbody tr:hover { background-color: #f1f5f9; }
.book-cover { width: 48px; height: 68px; border-radius: 8px; object-fit: cover; }
.book-placeholder { width: 48px; height: 68px; border-radius: 8px; background: #f1f5f9; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; }
.badge-custom-primary { background: var(--primary-light); color: var(--primary); padding: 6px 14px; border-radius: 8px; font-size: 0.85rem; font-weight: 700; display: inline-block; }
.barcode-img { width: 110px; height: 35px; border-radius: 4px; filter: contrast(1.1); mix-blend-mode: multiply; }
/* Tombol Aksi */
.btn-action-edit, .btn-action-delete { padding: 0.5rem 1rem; font-size: 0.825rem; font-weight: 600; border-radius: 8px !important; border: none; transition: 0.2s ease; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; }
.btn-action-edit { background-color: var(--btn-edit-bg); color: var(--btn-edit-text); }
.btn-action-delete { background-color: var(--btn-delete-bg); color: var(--btn-delete-text); }
.btn-action-delete:disabled { background-color: #cbd5e1; color: #94a3b8; cursor: not-allowed; }
/* Floating Action Bar */
.floating-action-bar { position: fixed; bottom: 25px; left: 50%; transform: translateX(-50%); width: 90%; max-width: 850px; z-index: 1050; }
.pagination .page-link { border: none; color: var(--text-muted); border-radius: 10px; font-weight: 600; padding: 10px 16px; }
.pagination .page-item.active .page-link { background: var(--primary); color: white; }
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
const checkboxes = document.querySelectorAll('.row-checkbox');
const floatingBar = document.getElementById('floatingActionBar');
const selectedCountSpan = document.getElementById('selectedCount');
const selectAllPageBtn = document.getElementById('selectAllPage');
const clearSelectionBtn = document.getElementById('clearSelection');
const submitBulkPrintBtn = document.getElementById('submitBulkPrintBtn');
const submitBulkDeleteBtn = document.getElementById('submitBulkDeleteBtn');
const actionForm = document.getElementById('bulkActionForm');
let isStandBy = false;
function getStored() { return JSON.parse(localStorage.getItem('selected_koleksi') || '[]'); }
function setStored(arr) { localStorage.setItem('selected_koleksi', JSON.stringify(arr)); }
let selected = getStored();
if (selected.length > 0) {
isStandBy = true;
}
checkboxes.forEach(cb => {
if (selected.includes(cb.value)) cb.checked = true;
cb.addEventListener('change', function() {
let s = getStored();
if(this.checked) {
if(!s.includes(this.value)) s.push(this.value);
isStandBy = true;
} else {
s = s.filter(id => id !== this.value);
}
setStored(s);
updateFloatingBar();
});
});
function injectHiddenInputs(formElement, storedItems) {
document.querySelectorAll('.hidden-persist').forEach(el => el.remove());
storedItems.forEach(val => {
let input = document.createElement('input');
input.type = 'hidden';
input.name = 'kode_eksemplar[]';
input.value = val;
input.className = 'hidden-persist';
formElement.appendChild(input);
});
}
submitBulkPrintBtn.addEventListener('click', function() {
const s = getStored();
if(s.length === 0) { alert('Pilih minimal 1 item.'); return; }
if(s.length > 20) { alert('Maksimal 20 item terpilih untuk cetak.'); return; }
actionForm.action = "{{ route('admin.koleksi.barcode_multiple') }}";
actionForm.target = "_blank";
const methodInput = actionForm.querySelector('input[name="_method"]');
if(methodInput) methodInput.remove();
injectHiddenInputs(actionForm, s);
actionForm.submit();
});
submitBulkDeleteBtn.addEventListener('click', function() {
const s = getStored();
if(s.length === 0) { alert('Pilih minimal 1 item untuk dihapus.'); return; }
if (confirm(`Apakah Anda yakin ingin menghapus permanen ${s.length} data koleksi terpilih?`)) {
actionForm.action = "{{ route('admin.koleksi.destroy_multiple') }}";
actionForm.target = "_self";
let methodInput = actionForm.querySelector('input[name="_method"]');
if(!methodInput) {
methodInput = document.createElement('input');
methodInput.type = 'hidden';
methodInput.name = '_method';
methodInput.value = 'DELETE';
actionForm.appendChild(methodInput);
}
injectHiddenInputs(actionForm, s);
localStorage.removeItem('selected_koleksi');
actionForm.submit();
}
});
function isAllPageChecked() {
if (checkboxes.length === 0) return false;
let s = getStored();
return Array.from(checkboxes).every(cb => s.includes(cb.value));
}
function updateFloatingBar() {
const checkedCount = getStored().length;
selectedCountSpan.textContent = checkedCount;
submitBulkPrintBtn.disabled = (checkedCount === 0);
submitBulkDeleteBtn.disabled = (checkedCount === 0);
if (isStandBy || checkedCount > 0) {
floatingBar.classList.remove('d-none');
} else {
floatingBar.classList.add('d-none');
}
if (isAllPageChecked()) {
selectAllPageBtn.textContent = 'Hapus Pilihan';
selectAllPageBtn.classList.replace('btn-outline-primary', 'btn-outline-danger');
} else {
selectAllPageBtn.textContent = 'Pilih Semua di Halaman Ini';
selectAllPageBtn.classList.replace('btn-outline-danger', 'btn-outline-primary');
}
}
updateFloatingBar();
clearSelectionBtn.addEventListener('click', function () {
localStorage.removeItem('selected_koleksi');
checkboxes.forEach(cb => cb.checked = false);
isStandBy = false;
updateFloatingBar();
});
selectAllPageBtn.addEventListener('click', function () {
let s = getStored();
if (isAllPageChecked()) {
checkboxes.forEach(checkbox => {
checkbox.checked = false;
s = s.filter(id => id !== checkbox.value);
});
isStandBy = true;
setStored(s);
updateFloatingBar();
} else {
for (let checkbox of checkboxes) {
if (!s.includes(checkbox.value)) {
checkbox.checked = true;
s.push(checkbox.value);
}
}
isStandBy = true;
setStored(s);
updateFloatingBar();
}
});
});
</script>
@endsection