362 lines
16 KiB
PHP
362 lines
16 KiB
PHP
@extends('layout.main')
|
||
@section('body_main')
|
||
<div class="row">
|
||
<div class="col-md-12">
|
||
<div class="card">
|
||
<div class="card-header d-flex align-items-center justify-content-between">
|
||
<h4 class="mb-0">Log Aktivitas</h4>
|
||
</div>
|
||
<div class="card-body p-3">
|
||
<div class="d-flex flex-column flex-md-row align-items-md-center gap-2 mb-3 flex-wrap">
|
||
<div class="input-group input-group-sm flex-grow-1" 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="tableSearch"
|
||
class="form-control border-start-0"
|
||
placeholder="Cari nama dokumen / nomor dokumen / aksi"
|
||
oninput="debouncedTableSearch(this.value)">
|
||
</div>
|
||
<div class="d-flex align-items-center gap-2">
|
||
<label class="small mb-0 text-muted">Mulai</label>
|
||
<input type="date" id="startDate" class="form-control form-control-sm" onchange="applyDateFilter()">
|
||
</div>
|
||
<div class="d-flex align-items-center gap-2">
|
||
<label class="small mb-0 text-muted">Selesai</label>
|
||
<input type="date" id="endDate" class="form-control form-control-sm" onchange="applyDateFilter()">
|
||
</div>
|
||
<div class="d-flex align-items-center gap-2">
|
||
<select id="tablePageSize" class="form-select form-select-sm" style="width: auto;">
|
||
<option value="5">5</option>
|
||
<option value="10"selected>10</option>
|
||
<option value="20">20</option>
|
||
<option value="50">50</option>
|
||
<option value="100">100</option>
|
||
</select>
|
||
</div>
|
||
<button class="btn btn-outline-secondary btn-sm" onclick="refreshLog()">
|
||
<i class="fa fa-rotate"></i> Refresh
|
||
</button>
|
||
<div class="small text-muted ms-md-auto" id="tableSummary"></div>
|
||
</div>
|
||
<div class="table-responsive" style="max-height: 65vh; overflow-y:auto;">
|
||
<table class="table table-sm table-hover align-middle mb-0" id="lastUpdatedTable">
|
||
<thead>
|
||
<tr>
|
||
<th>No Dokumen</th>
|
||
<th>Nama Dokumen</th>
|
||
<th>Kategori</th>
|
||
<th>Unit</th>
|
||
<th>Pengunggah</th>
|
||
<th class="text-center">Jumlah User Melihat</th>
|
||
<th class="text-center">Jumlah User Mengunduh</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="tableLogAktivityDocument">
|
||
<!-- data dari fetch masuk sini -->
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="d-flex flex-column flex-md-row align-items-md-center justify-content-between gap-2 mt-3" id="paginationControls"></div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
@endsection
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const tableState = { data: [], page: 1, pageSize: 10, search: '', lastPage: 1, total: 0, startDate: '', endDate: '' };
|
||
const tbody = document.getElementById('tableLogAktivityDocument');
|
||
const paginationEl = document.getElementById('paginationControls');
|
||
const summaryEl = document.getElementById('tableSummary');
|
||
const pageSizeSelect = document.getElementById('tablePageSize');
|
||
const startDateInput = document.getElementById('startDate');
|
||
const endDateInput = document.getElementById('endDate');
|
||
|
||
if(pageSizeSelect){
|
||
const initialSize = parseInt(pageSizeSelect.value);
|
||
if(!isNaN(initialSize)) tableState.pageSize = initialSize;
|
||
pageSizeSelect.addEventListener('change', (e) => {
|
||
const val = parseInt(e.target.value);
|
||
if(!isNaN(val) && val > 0){
|
||
tableState.pageSize = val;
|
||
tableState.page = 1;
|
||
fetchData();
|
||
}
|
||
});
|
||
}
|
||
|
||
window.applyDateFilter = function(){
|
||
tableState.startDate = startDateInput.value || '';
|
||
tableState.endDate = endDateInput.value || '';
|
||
tableState.page = 1;
|
||
fetchData();
|
||
}
|
||
|
||
function buildRow(item){
|
||
const tanggal = item.entry_at ? formatTanggal(item.entry_at) : '-';
|
||
const totalViews = item.total_views ?? 0;
|
||
return `
|
||
<tr role="button" onclick='showLogDetail(${JSON.stringify(item.id)})'>
|
||
<td>${item.no_dokumen}</td>
|
||
<td>${item.nama_dokumen || '-'}</td>
|
||
<td>${item.kategori || '-'}</td>
|
||
<td>${item.unit || '-'}</td>
|
||
<td>${item.pengunggah || '-'}</td>
|
||
<td class="text-center fw-semibold">${totalViews}</td>
|
||
<td class="text-center fw-semibold">${item.total_download}</td>
|
||
</tr>
|
||
`;
|
||
}
|
||
|
||
function formatTanggal(dateString) {
|
||
const d = new Date(dateString);
|
||
return d.toLocaleDateString('id-ID', {
|
||
day: '2-digit',
|
||
month: 'short',
|
||
year: 'numeric',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
}
|
||
|
||
window.showLogDetail = function(id){
|
||
if(!id) return;
|
||
const modalEl = document.getElementById('logDetailModal');
|
||
const modal = new bootstrap.Modal(modalEl);
|
||
modal.show();
|
||
|
||
renderLogModal(id, 1, '');
|
||
}
|
||
|
||
function renderLogModal(id, page, keyword){
|
||
const tbody = document.getElementById('logDetailTbody');
|
||
const searchInput = document.getElementById('logSearchInput');
|
||
const searchBtn = document.getElementById('logSearchBtn');
|
||
const paginationBtns = document.getElementById('logPaginationBtns');
|
||
const summaryText = document.getElementById('logSummaryText');
|
||
|
||
if(tbody) tbody.innerHTML = '<tr><td colspan="4" class="text-center text-muted py-3">Memuat...</td></tr>';
|
||
if(summaryText) summaryText.textContent = 'Memuat data...';
|
||
if(searchInput) searchInput.value = keyword || '';
|
||
|
||
const params = new URLSearchParams({ page, per_page: 10, keyword: keyword || '' });
|
||
fetch(`/datatable/log-activity/${id}?${params.toString()}`)
|
||
.then(res => res.json())
|
||
.then(resp => {
|
||
const logs = resp?.data || [];
|
||
const pagination = resp?.pagination || {};
|
||
|
||
const totalPages = pagination.last_page || 1;
|
||
const currentPage = pagination.current_page || 1;
|
||
|
||
if(searchInput) searchInput.value = keyword || '';
|
||
|
||
const rows = logs.map((row, idx) => `
|
||
<tr>
|
||
<td>${((currentPage - 1) * (pagination.per_page || 10)) + idx + 1}</td>
|
||
<td>${row.pegawai_nama_entry || '-'}</td>
|
||
<td>${row.total_open || 0}</td>
|
||
<td>${row.total_download || 0}</td>
|
||
<td>${formatTanggal(row.last_open)}</td>
|
||
</tr>
|
||
`).join('');
|
||
|
||
const emptyState = logs.length === 0 ? '<tr><td colspan="5" class="text-center text-muted py-3">Belum ada aktivitas</td></tr>' : '';
|
||
if(tbody) tbody.innerHTML = logs.length ? rows : emptyState;
|
||
|
||
if(summaryText){
|
||
summaryText.textContent = `Menampilkan ${logs.length} dari ${pagination.total ?? logs.length} pegawai`;
|
||
}
|
||
|
||
if(paginationBtns){
|
||
let paginationButtons = '';
|
||
if(totalPages > 1){
|
||
paginationButtons += `<button class="btn btn-sm btn-outline-secondary" data-page="prev" ${currentPage===1?'disabled':''}>‹</button>`;
|
||
for(let i=1;i<=totalPages;i++){
|
||
paginationButtons += `<button class="btn btn-sm ${i===currentPage?'btn-primary':'btn-outline-secondary'}" data-page="${i}">${i}</button>`;
|
||
}
|
||
paginationButtons += `<button class="btn btn-sm btn-outline-secondary" data-page="next" ${currentPage===totalPages?'disabled':''}>›</button>`;
|
||
}
|
||
paginationBtns.innerHTML = paginationButtons;
|
||
|
||
paginationBtns.onclick = (e) => {
|
||
const target = e.target;
|
||
const pageAttr = target.getAttribute('data-page');
|
||
if(!pageAttr) return;
|
||
let nextPage = currentPage;
|
||
if(pageAttr === 'prev') nextPage = Math.max(1, currentPage-1);
|
||
else if(pageAttr === 'next') nextPage = Math.min(totalPages, currentPage+1);
|
||
else nextPage = parseInt(pageAttr);
|
||
renderLogModal(id, nextPage, keyword);
|
||
};
|
||
}
|
||
|
||
if(searchBtn){
|
||
searchBtn.onclick = () => renderLogModal(id, 1, searchInput.value.trim());
|
||
}
|
||
if(searchInput){
|
||
searchInput.onkeypress = (e) => {
|
||
if(e.key === 'Enter') renderLogModal(id, 1, searchInput.value.trim());
|
||
};
|
||
}
|
||
})
|
||
.catch(() => {
|
||
if(tbody) tbody.innerHTML = '<tr><td colspan="4" class="text-center text-danger py-3">Gagal memuat data</td></tr>';
|
||
if(summaryText) summaryText.textContent = 'Gagal memuat data';
|
||
if(paginationBtns) paginationBtns.innerHTML = '';
|
||
});
|
||
}
|
||
|
||
function renderPagination(totalPages){
|
||
if(!paginationEl) return;
|
||
if(totalPages <= 1){
|
||
paginationEl.innerHTML = '';
|
||
return;
|
||
}
|
||
const maxButtons = 5;
|
||
let start = Math.max(1, tableState.page - Math.floor(maxButtons/2));
|
||
let end = Math.min(totalPages, start + maxButtons - 1);
|
||
start = Math.max(1, end - maxButtons + 1);
|
||
|
||
let buttons = '';
|
||
buttons += `<button class="btn btn-outline-secondary btn-sm" data-page="prev" ${tableState.page === 1 ? 'disabled' : ''}>‹</button>`;
|
||
for(let i=start; i<=end; i++){
|
||
buttons += `<button class="btn btn-sm ${i === tableState.page ? 'btn-primary' : 'btn-outline-secondary'}" data-page="${i}">${i}</button>`;
|
||
}
|
||
buttons += `<button class="btn btn-outline-secondary btn-sm" data-page="next" ${tableState.page === totalPages ? 'disabled' : ''}>›</button>`;
|
||
|
||
paginationEl.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 ${tableState.page} dari ${totalPages}</span>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
if(paginationEl){
|
||
paginationEl.addEventListener('click', (e) => {
|
||
const page = e.target.getAttribute('data-page');
|
||
if(!page) return;
|
||
if(page === 'prev' && tableState.page > 1) tableState.page--;
|
||
else if(page === 'next'){
|
||
if(tableState.page < tableState.lastPage) tableState.page++;
|
||
}else{
|
||
tableState.page = parseInt(page);
|
||
}
|
||
fetchData();
|
||
});
|
||
}
|
||
|
||
function renderTable(){
|
||
const pageData = tableState.data || [];
|
||
if(pageData.length === 0){
|
||
tbody.innerHTML = `
|
||
<tr>
|
||
<td colspan="6" class="text-center text-muted py-4">
|
||
Tidak ada data
|
||
</td>
|
||
</tr>
|
||
`;
|
||
}else{
|
||
tbody.innerHTML = pageData.map(buildRow).join('');
|
||
}
|
||
|
||
const from = tableState.total === 0 ? 0 : ((tableState.page -1) * tableState.pageSize) + 1;
|
||
const to = Math.min(((tableState.page -1) * tableState.pageSize) + pageData.length, tableState.total);
|
||
if(summaryEl){
|
||
summaryEl.textContent = tableState.total ? `Menampilkan ${from} - ${to} dari ${tableState.total} aktivitas` : 'Tidak ada data';
|
||
}
|
||
|
||
renderPagination(tableState.lastPage || 1);
|
||
}
|
||
|
||
let searchDebounce;
|
||
window.debouncedTableSearch = function(value){
|
||
clearTimeout(searchDebounce);
|
||
searchDebounce = setTimeout(() => {
|
||
tableState.search = value.trim();
|
||
tableState.page = 1;
|
||
fetchData();
|
||
}, 250);
|
||
}
|
||
|
||
window.refreshLog = function(){
|
||
tableState.search = '';
|
||
tableState.startDate = '';
|
||
tableState.endDate = '';
|
||
document.getElementById('tableSearch').value = '';
|
||
startDateInput.value = '';
|
||
endDateInput.value = '';
|
||
tableState.page = 1;
|
||
fetchData();
|
||
}
|
||
|
||
function fetchData(){
|
||
if(summaryEl) summaryEl.textContent = 'Memuat data...';
|
||
const params = new URLSearchParams({
|
||
page: tableState.page,
|
||
per_page: tableState.pageSize,
|
||
keyword: tableState.search || '',
|
||
start_date: tableState.startDate || '',
|
||
end_date: tableState.endDate || ''
|
||
});
|
||
fetch(`/datatable/log-activity?${params.toString()}`)
|
||
.then(res => res.json())
|
||
.then(data => {
|
||
tableState.data = data?.data || [];
|
||
tableState.lastPage = data?.pagination?.last_page || 1;
|
||
tableState.total = data?.pagination?.total || 0;
|
||
renderTable();
|
||
})
|
||
.catch(err => {
|
||
console.error(err);
|
||
if(summaryEl) summaryEl.textContent = 'Gagal memuat data';
|
||
});
|
||
}
|
||
|
||
fetchData();
|
||
});
|
||
</script>
|
||
|
||
<!-- Modal Detail Log -->
|
||
<div class="modal fade" id="logDetailModal" tabindex="-1" aria-labelledby="logDetailLabel" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="logDetailLabel">Detail Aktivitas</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||
</div>
|
||
<div class="modal-body" id="logDetailBody">
|
||
<div class="d-flex align-items-center gap-2 mb-2">
|
||
<input type="search" class="form-control form-control-sm" id="logSearchInput" placeholder="Cari nama pegawai">
|
||
<button class="btn btn-sm btn-outline-secondary" id="logSearchBtn"><i class="fa fa-search"></i></button>
|
||
</div>
|
||
<div class="table-responsive">
|
||
<table class="table table-sm table-striped mb-0">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>#</th>
|
||
<th>Nama</th>
|
||
<th>Jumlah Membuka</th>
|
||
<th>Jumlah Mengunduh</th>
|
||
<th>Terakhir Dilihat</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="logDetailTbody">
|
||
<tr><td colspan="4" class="text-center text-muted py-3">Memuat...</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="d-flex align-items-center justify-content-between mt-2">
|
||
<div class="small text-muted" id="logSummaryText">Menampilkan 0 pegawai</div>
|
||
<div class="btn-group" role="group" id="logPaginationBtns"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|