eSPJ/wwwroot/driver/js/history-upst.js

292 lines
11 KiB
JavaScript

document.addEventListener('DOMContentLoaded', function () {
const pageEl = document.getElementById('history-page');
if (!pageEl) return;
const apiUrl = pageEl.dataset.historyApi;
const detailBase = pageEl.dataset.historyDetailBase || '/upst/history/details/__id__';
const loadingEl = document.getElementById('history-loading');
const emptyEl = document.getElementById('history-empty');
const listEl = document.getElementById('history-list');
const paginationEl = document.getElementById('history-pagination');
const pageInfoEl = document.getElementById('history-page-info');
const totalInfoEl = document.getElementById('history-total-info');
const pageButtonsEl = document.getElementById('history-page-buttons');
const prevBtn = document.getElementById('history-prev-page');
const nextBtn = document.getElementById('history-next-page');
const fromDateInput = document.getElementById('history-from-date-filter');
const toDateInput = document.getElementById('history-to-date-filter');
const applyFilterBtn = document.getElementById('history-apply-filter');
const resetFilterBtn = document.getElementById('history-reset-filter');
const state = {
page: 1,
pageSize: 5,
totalPages: 1,
totalItems: 0,
fromDate: '',
toDate: ''
};
function formatTanggal(dateString) {
const date = new Date(dateString);
return new Intl.DateTimeFormat('id-ID', {
day: '2-digit',
month: 'short',
year: 'numeric'
}).format(date);
}
function formatWaktu(dateString) {
const date = new Date(dateString);
return new Intl.DateTimeFormat('id-ID', {
hour: '2-digit',
minute: '2-digit'
}).format(date);
}
function getStatusMarkup(status) {
if ((status || '').toLowerCase() === 'completed') {
return '<span class="bg-green-50 text-green-600 px-3 py-1 rounded-lg text-[10px] font-black uppercase border border-green-100">Selesai</span>';
}
return '<span class="bg-blue-50 text-blue-600 px-3 py-1 rounded-lg text-[10px] font-black uppercase border border-blue-100 animate-pulse">Proses</span>';
}
function buildDetailUrl(id) {
return detailBase.replace('__id__', String(id));
}
function renderItems(items) {
listEl.innerHTML = '';
items.forEach(function (item) {
const card = document.createElement('a');
card.href = buildDetailUrl(item.id);
card.className = 'block group';
card.innerHTML = `
<div class="bg-white rounded-3xl p-5 shadow-sm border border-gray-100 group-hover:shadow-md group-hover:-translate-y-1 transition-all duration-300">
<div class="flex justify-between items-start mb-4">
<div class="space-y-0.5">
<p class="text-[10px] font-bold text-gray-400 uppercase tracking-widest">Nomor Dokumen</p>
<p class="text-sm font-black text-gray-800">${item.noSpj}</p>
</div>
${getStatusMarkup(item.status)}
</div>
<div class="flex items-center gap-4 bg-gray-50/80 p-3 rounded-2xl border border-dashed border-gray-200">
<div class="w-12 h-12 bg-upst rounded-xl flex items-center justify-center shadow-inner">
<i class="w-6 h-6 text-white" data-lucide="truck"></i>
</div>
<div class="flex-1">
<div class="flex justify-between items-baseline gap-2">
<h2 class="font-black text-gray-900 leading-tight">${item.plat}</h2>
<span class="text-[11px] font-bold text-upst whitespace-nowrap">${formatWaktu(item.tanggalWaktu)}</span>
</div>
<div class="flex justify-between items-center gap-2">
<span class="text-xs text-gray-500 font-medium">${item.kode}</span>
<span class="text-[10px] text-gray-400 font-semibold whitespace-nowrap">${formatTanggal(item.tanggalWaktu)}</span>
</div>
</div>
</div>
<div class="mt-4 flex items-center justify-between gap-3">
<div class="flex items-center gap-2 min-w-0">
<div class="w-2 h-2 rounded-full bg-upst/40"></div>
<div class="flex flex-col min-w-0">
<span class="text-[10px] text-gray-400 font-bold uppercase">Tujuan Akhir</span>
<span class="text-sm font-bold text-gray-700 truncate">${item.tujuan}</span>
</div>
</div>
<div class="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center group-hover:bg-upst transition-colors shrink-0">
<i class="w-4 h-4" data-lucide="chevron-right"></i>
</div>
</div>
</div>
`;
listEl.appendChild(card);
});
if (window.lucide) {
window.lucide.createIcons();
}
}
function renderPagination() {
const hasItems = state.totalItems > 0;
paginationEl.classList.toggle('hidden', !hasItems);
if (!hasItems) return;
pageInfoEl.textContent = `Halaman ${state.page} dari ${state.totalPages}`;
totalInfoEl.textContent = `${state.totalItems} data`;
prevBtn.disabled = state.page <= 1;
nextBtn.disabled = state.page >= state.totalPages;
pageButtonsEl.innerHTML = '';
const pages = [];
const startPage = Math.max(1, state.page - 2);
const endPage = Math.min(state.totalPages, state.page + 2);
if (startPage > 1) {
pages.push(1);
if (startPage > 2) pages.push('ellipsis-left');
}
for (let page = startPage; page <= endPage; page++) {
pages.push(page);
}
if (endPage < state.totalPages) {
if (endPage < state.totalPages - 1) pages.push('ellipsis-right');
pages.push(state.totalPages);
}
pages.forEach(function (page) {
if (typeof page !== 'number') {
const ellipsis = document.createElement('span');
ellipsis.className = 'w-10 h-10 flex items-center justify-center text-xs font-black text-gray-400';
ellipsis.textContent = '...';
pageButtonsEl.appendChild(ellipsis);
return;
}
const btn = document.createElement('button');
btn.type = 'button';
btn.textContent = String(page);
btn.className = page === state.page
? 'w-10 h-10 rounded-2xl bg-upst text-white text-xs font-black'
: 'w-10 h-10 rounded-2xl border border-gray-200 text-gray-700 text-xs font-black bg-white';
btn.addEventListener('click', function () {
if (page === state.page) return;
loadHistory(page);
});
pageButtonsEl.appendChild(btn);
});
}
function setLoading(isLoading) {
loadingEl.classList.toggle('hidden', !isLoading);
if (isLoading) {
emptyEl.classList.add('hidden');
listEl.innerHTML = '';
paginationEl.classList.add('hidden');
}
}
function showEmpty() {
listEl.innerHTML = '';
emptyEl.classList.remove('hidden');
paginationEl.classList.add('hidden');
if (window.lucide) {
window.lucide.createIcons();
}
}
async function loadHistory(page) {
state.page = page || 1;
setLoading(true);
try {
const params = new URLSearchParams({
page: String(state.page),
pageSize: String(state.pageSize)
});
if (state.fromDate) {
params.set('fromDate', state.fromDate);
}
if (state.toDate) {
params.set('toDate', state.toDate);
}
const response = await fetch(`${apiUrl}?${params.toString()}`, { cache: 'no-store' });
if (!response.ok) {
throw new Error('Gagal memuat history');
}
const data = await response.json();
state.page = data.page || 1;
state.totalPages = data.totalPages || 1;
state.totalItems = data.totalItems || 0;
loadingEl.classList.add('hidden');
if (!data.items || data.items.length === 0) {
showEmpty();
return;
}
emptyEl.classList.add('hidden');
renderItems(data.items);
renderPagination();
} catch (error) {
console.error(error);
loadingEl.classList.add('hidden');
emptyEl.classList.remove('hidden');
emptyEl.innerHTML = `
<div class="w-16 h-16 bg-red-50 rounded-full flex items-center justify-center mx-auto mb-4">
<i class="w-8 h-8 text-red-400" data-lucide="triangle-alert"></i>
</div>
<h3 class="text-base font-black text-gray-900 mb-1">Gagal Memuat Riwayat</h3>
<p class="text-sm text-gray-500">Silakan coba lagi beberapa saat.</p>
`;
if (window.lucide) {
window.lucide.createIcons();
}
}
}
function applyDateRangeFilter() {
const nextFromDate = fromDateInput?.value || '';
const nextToDate = toDateInput?.value || '';
if (nextFromDate && nextToDate && nextFromDate > nextToDate) {
const temp = nextFromDate;
state.fromDate = nextToDate;
state.toDate = temp;
if (fromDateInput) fromDateInput.value = state.fromDate;
if (toDateInput) toDateInput.value = state.toDate;
} else {
state.fromDate = nextFromDate;
state.toDate = nextToDate;
}
loadHistory(1);
}
applyFilterBtn?.addEventListener('click', function () {
applyDateRangeFilter();
});
resetFilterBtn?.addEventListener('click', function () {
state.fromDate = '';
state.toDate = '';
if (fromDateInput) fromDateInput.value = '';
if (toDateInput) toDateInput.value = '';
loadHistory(1);
});
[fromDateInput, toDateInput].forEach(function (input) {
input?.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
event.preventDefault();
applyDateRangeFilter();
}
});
});
prevBtn?.addEventListener('click', function () {
if (state.page > 1) {
loadHistory(state.page - 1);
}
});
nextBtn?.addEventListener('click', function () {
if (state.page < state.totalPages) {
loadHistory(state.page + 1);
}
});
loadHistory(1);
});