2026-01-30 01:06:11 +07:00

358 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@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 file / 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>File</th>
<th>Kategori</th>
<th>Unit</th>
<th>Sub Unit</th>
<th class="text-center">Jumlah Pegawai Melihat</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.file}</td>
<td>${item.kategori || '-'}</td>
<td>${item.unit || '-'}</td>
<td>${item.sub_unit || '-'}</td>
<td class="text-center fw-semibold">${totalViews}</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>${formatTanggal(row.last_open)}</td>
</tr>
`).join('');
const emptyState = logs.length === 0 ? '<tr><td colspan="4" 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>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>