Improve soundbox ops dashboard and registry editing

This commit is contained in:
Wira Basalamah
2026-06-08 15:56:12 +07:00
parent 836eb7db85
commit 67dc286c1a
18 changed files with 768 additions and 120 deletions

View File

@ -167,7 +167,7 @@
<div class="flex items-center gap-6">
<div class="relative w-64 group">
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 group-focus-within:text-primary transition-colors" data-icon="search">search</span>
<input class="w-full bg-slate-50 border-none rounded-lg pl-10 pr-4 py-2 text-body-md focus:ring-2 focus:ring-primary/20 focus:bg-white transition-all" placeholder="Search batch or merchant..." type="text"/>
<input id="settlement-search-input" class="w-full bg-slate-50 border-none rounded-lg pl-10 pr-4 py-2 text-body-md focus:ring-2 focus:ring-primary/20 focus:bg-white transition-all" placeholder="Search batch or merchant..." type="text"/>
</div>
<nav class="hidden md:flex gap-6 items-center">
<a class="font-body-md font-bold text-primary border-b-2 border-primary h-[72px] flex items-center" href="/ui/admin-dashboard-overview">Dashboard</a>
@ -504,6 +504,7 @@
if (!api) return;
const rowsEl = document.getElementById('settlement-batch-rows');
const searchInput = document.getElementById('settlement-search-input');
const generateBtn = document.getElementById('generate-settlement-batch');
const statusFilter = document.getElementById('settlement-status-filter');
const summaryEl = document.getElementById('settlement-pagination-summary');
@ -535,6 +536,7 @@
const reprocessBtn = document.getElementById('reprocess-settlement-batch');
const settlementEventsEl = document.getElementById('drawerSettlementEvents');
let batches = [];
let visibleBatches = [];
let activeBatchId = null;
let activeBatchCode = '';
let adminProfile = null;
@ -586,11 +588,12 @@
};
const renderKpis = () => {
const created = batches.filter((batch) => batch.status === 'created');
const paid = batches.filter((batch) => batch.status === 'paid');
const source = searchInput?.value ? visibleBatches : batches;
const created = source.filter((batch) => batch.status === 'created');
const paid = source.filter((batch) => batch.status === 'paid');
const pendingAmount = created.reduce((sum, batch) => sum + Number(batch.net_payable_amount || 0), 0);
const totalFees = batches.reduce((sum, batch) => sum + Number(batch.platform_fee_amount || 0), 0);
const totalAdjustments = batches.reduce((sum, batch) => sum + Number(batch.metadata_json?.total_adjustment_amount || 0), 0);
const totalFees = source.reduce((sum, batch) => sum + Number(batch.platform_fee_amount || 0), 0);
const totalAdjustments = source.reduce((sum, batch) => sum + Number(batch.metadata_json?.total_adjustment_amount || 0), 0);
setText('kpi-pending-payouts', money(pendingAmount));
setText('kpi-created-batches', String(created.length));
setText('kpi-paid-batches', String(paid.length));
@ -600,14 +603,29 @@
const renderRows = () => {
if (!rowsEl) return;
if (!batches.length) {
rowsEl.innerHTML = '<tr><td colspan="8" class="px-6 py-10 text-center text-slate-500">No settlement batches yet.</td></tr>';
summaryEl.textContent = 'Showing 0 batches';
const query = String(searchInput?.value || '').trim().toLowerCase();
visibleBatches = batches.filter((batch) => {
const haystack = [
batch.id,
batch.batch_code,
batch.merchant_id,
batch.status,
batch.failure_reason,
batch.metadata_json?.paid_reference,
batch.metadata_json?.paid_note,
batch.gross_amount,
batch.net_payable_amount
].filter(Boolean).join(' ').toLowerCase();
return !query || haystack.includes(query);
});
if (!visibleBatches.length) {
rowsEl.innerHTML = `<tr><td colspan="8" class="px-6 py-10 text-center text-slate-500">${query ? 'No settlement batches matched the search.' : 'No settlement batches yet.'}</td></tr>`;
summaryEl.textContent = query ? `No results for "${searchInput.value.trim()}"` : 'Showing 0 batches';
renderKpis();
return;
}
rowsEl.innerHTML = batches.map((batch) => `
rowsEl.innerHTML = visibleBatches.map((batch) => `
<tr class="h-[52px] hover:bg-slate-50 transition-colors group cursor-pointer" data-batch-id="${batch.id}">
<td class="px-6 font-body-md font-bold text-primary tabular-nums">${batch.batch_code}</td>
<td class="px-6 font-body-md text-on-surface-variant">${dt(batch.cutoff_at)}</td>
@ -629,7 +647,7 @@
rowsEl.querySelectorAll('tr[data-batch-id]').forEach((row) => {
row.addEventListener('click', () => openDrawer(row.dataset.batchId));
});
summaryEl.textContent = `Showing ${batches.length} batches`;
summaryEl.textContent = `Showing ${visibleBatches.length} of ${batches.length} batches`;
renderKpis();
};
@ -743,6 +761,13 @@
});
statusFilter?.addEventListener('change', loadBatches);
searchInput?.addEventListener('input', renderRows);
searchInput?.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && searchInput.value) {
searchInput.value = '';
renderRows();
}
});
markPaidBtn?.addEventListener('click', async () => {
if (!activeBatchId) return;
if (paidForm && !paidForm.reportValidity()) return;