153 lines
6.3 KiB
PHP
153 lines
6.3 KiB
PHP
@php($showRecapTitle = $showRecapTitle ?? true)
|
||
<div class="d-flex flex-column flex-md-row align-items-md-center gap-2 mb-3">
|
||
@if ($showRecapTitle)
|
||
<div>
|
||
<h4 class="mb-0">Rekap Dokumen</h4>
|
||
<small class="text-muted">Ringkasan jumlah file per Unit dan Kategori</small>
|
||
</div>
|
||
@endif
|
||
<div class="{{ $showRecapTitle ? 'ms-md-auto' : '' }} d-flex gap-2 align-items-center">
|
||
<div class="input-group input-group-sm" style="max-width:320px;">
|
||
<span class="input-group-text bg-white border-end-0">
|
||
<i class="fa fa-search text-muted"></i>
|
||
</span>
|
||
<input type="search" id="recapSearch" class="form-control border-start-0" placeholder="Cari unit atau folder" oninput="debouncedRecapSearch(this.value)">
|
||
</div>
|
||
<select id="recapPerPage" class="form-select form-select-sm" style="width:auto;" onchange="changePerPage(this.value)">
|
||
<option value="5">5</option>
|
||
<option value="10" selected>10</option>
|
||
<option value="20">20</option>
|
||
<option value="50">50</option>
|
||
</select>
|
||
<button class="btn btn-outline-secondary btn-sm d-flex align-items-center gap-1" onclick="fetchRecap()">
|
||
<i class="fa fa-rotate"></i>
|
||
<span>Refresh</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="table-responsive" style="max-height: 55vh; overflow-y:auto;">
|
||
<table class="table table-sm table-hover align-middle">
|
||
<thead class="table-light shadow-sm">
|
||
<tr>
|
||
<th style="width:5%;" class="text-center">#</th>
|
||
<th style="width:30%;">Unit / Akreditasi</th>
|
||
<th style="width:20%;">Kategori</th>
|
||
<th style="width:15%;" class="text-center">Jumlah File</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="recapBody">
|
||
<tr>
|
||
<td colspan="4" class="text-center text-muted py-4">Memuat data...</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="d-flex flex-column flex-md-row align-items-center justify-content-between gap-2 mt-3" id="recapPagination"></div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', () => fetchRecap());
|
||
|
||
let recapDebounce;
|
||
const recapState = { page:1, perPage:10, keyword:'', lastPage:1 };
|
||
|
||
function debouncedRecapSearch(val){
|
||
clearTimeout(recapDebounce);
|
||
recapDebounce = setTimeout(() => {
|
||
recapState.keyword = val;
|
||
recapState.page = 1;
|
||
fetchRecap();
|
||
}, 250);
|
||
}
|
||
|
||
function changePerPage(val){
|
||
recapState.perPage = parseInt(val) || 10;
|
||
recapState.page = 1;
|
||
fetchRecap();
|
||
}
|
||
|
||
function fetchRecap(){
|
||
const tbody = document.getElementById('recapBody');
|
||
const pager = document.getElementById('recapPagination');
|
||
if(!tbody) return;
|
||
tbody.innerHTML = `<tr><td colspan="4" class="text-center text-muted py-4">Memuat data...</td></tr>`;
|
||
if(pager) pager.innerHTML = '';
|
||
|
||
const params = new URLSearchParams({
|
||
page: recapState.page,
|
||
per_page: recapState.perPage,
|
||
keyword: recapState.keyword || ''
|
||
});
|
||
fetch('/data/recap?' + params.toString())
|
||
.then(res => res.json())
|
||
.then(json => {
|
||
const rows = json?.data || [];
|
||
recapState.lastPage = json?.pagination?.last_page || 1;
|
||
if(!rows.length){
|
||
tbody.innerHTML = `<tr><td colspan="4" class="text-center text-muted py-4">Tidak ada data</td></tr>`;
|
||
return;
|
||
}
|
||
let grandTotal = 0;
|
||
const html = rows.map((row, idx) => {
|
||
const folderRows = (row.data || []).map((f, i) => `
|
||
<tr>
|
||
${i === 0 ? `<td rowspan="${row.data.length}" class="text-center align-middle fw-semibold">${idx+1}</td>` : ''}
|
||
${i === 0 ? `<td rowspan="${row.data.length}" class="fw-semibold">${row.unit || '-'}</td>` : ''}
|
||
<td>${f.folder || '-'}</td>
|
||
<td class="text-center fw-bold">${f.count || 0}</td>
|
||
</tr>
|
||
`).join('');
|
||
(row.data || []).forEach(f => { grandTotal += (parseInt(f.count, 10) || 0); });
|
||
return folderRows;
|
||
}).join('');
|
||
tbody.innerHTML = html + `
|
||
<tr class="table-light">
|
||
<td colspan="3" class="text-end fw-semibold">Total File</td>
|
||
<td class="text-center fw-bold">${grandTotal}</td>
|
||
</tr>
|
||
`;
|
||
renderRecapPagination();
|
||
})
|
||
.catch(err => {
|
||
console.error(err);
|
||
tbody.innerHTML = `<tr><td colspan="4" class="text-center text-danger py-4">Gagal memuat data</td></tr>`;
|
||
});
|
||
}
|
||
|
||
function renderRecapPagination(){
|
||
const pager = document.getElementById('recapPagination');
|
||
if(!pager) return;
|
||
if(recapState.lastPage <= 1){
|
||
pager.innerHTML = '';
|
||
return;
|
||
}
|
||
const maxButtons = 5;
|
||
let start = Math.max(1, recapState.page - Math.floor(maxButtons/2));
|
||
let end = Math.min(recapState.lastPage, start + maxButtons - 1);
|
||
start = Math.max(1, end - maxButtons + 1);
|
||
|
||
let buttons = '';
|
||
buttons += `<button class="btn btn-outline-secondary btn-sm" data-page="prev" ${recapState.page === 1 ? 'disabled' : ''}>‹</button>`;
|
||
for(let i=start; i<=end; i++){
|
||
buttons += `<button class="btn btn-sm ${i === recapState.page ? 'btn-primary' : 'btn-outline-secondary'}" data-page="${i}">${i}</button>`;
|
||
}
|
||
buttons += `<button class="btn btn-outline-secondary btn-sm" data-page="next" ${recapState.page === recapState.lastPage ? 'disabled' : ''}>›</button>`;
|
||
|
||
pager.innerHTML = `
|
||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||
<div class="btn-group" role="group">${buttons}</div>
|
||
<span class="small text-muted">Halaman ${recapState.page} dari ${recapState.lastPage}</span>
|
||
</div>
|
||
`;
|
||
|
||
pager.querySelectorAll('button[data-page]').forEach(btn => {
|
||
btn.addEventListener('click', () => {
|
||
const page = btn.getAttribute('data-page');
|
||
if(page === 'prev' && recapState.page > 1) recapState.page--;
|
||
else if(page === 'next' && recapState.page < recapState.lastPage) recapState.page++;
|
||
else if(!isNaN(parseInt(page))) recapState.page = parseInt(page);
|
||
fetchRecap();
|
||
});
|
||
});
|
||
}
|
||
</script>
|