Carian langsung (AJAX) pada dashboard

Taip nama/no. KP terus mencari tanpa klik butang (debounce 300ms).
Tukar pusat mengundi/status juga menapis serta-merta. Jadual dan
statistik digantikan melalui fetch; URL dikemas kini (replaceState)
supaya refresh dan eksport kekal konsisten. Butang Cari kekal sebagai
fallback tanpa JavaScript; pilihan DUN (superadmin) kekal muat semula
penuh kerana senarai pusat mengundi berubah.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Saufi
2026-07-11 03:21:24 +08:00
parent cd5cfb49a9
commit 57c20353c7

View File

@@ -3,7 +3,7 @@
@section('tajuk', 'Dashboard Kehadiran KTM') @section('tajuk', 'Dashboard Kehadiran KTM')
@section('kandungan') @section('kandungan')
<div class="stat-grid"> <div class="stat-grid" id="stat-grid">
<div class="stat"> <div class="stat">
<div class="nilai">{{ $jumlahSemua }}</div> <div class="nilai">{{ $jumlahSemua }}</div>
<div class="label">Jumlah KTM</div> <div class="label">Jumlah KTM</div>
@@ -19,7 +19,7 @@
</div> </div>
<div class="kad"> <div class="kad">
<form method="GET" action="{{ route('dashboard') }}"> <form method="GET" action="{{ route('dashboard') }}" id="borang-tapis">
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(180px, 1fr)); gap:0 0.75rem;"> <div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(180px, 1fr)); gap:0 0.75rem;">
@if (auth()->user()->isSuperadmin()) @if (auth()->user()->isSuperadmin())
<div> <div>
@@ -71,7 +71,7 @@
</form> </form>
</div> </div>
<div class="kad" style="padding:0;"> <div class="kad" style="padding:0;" id="senarai-ktm">
<div class="jadual-kepala"> <div class="jadual-kepala">
<h2 class="tajuk-jadual mb-0">Senarai KTM (Ketua Tempat Mengundi)</h2> <h2 class="tajuk-jadual mb-0">Senarai KTM (Ketua Tempat Mengundi)</h2>
</div> </div>
@@ -145,4 +145,66 @@
<span class="teks-kecil">{{ $assignments->count() }} rekod dipaparkan</span> <span class="teks-kecil">{{ $assignments->count() }} rekod dipaparkan</span>
</div> </div>
</div> </div>
<script>
(function () {
var borang = document.getElementById('borang-tapis');
var inputCari = document.getElementById('q');
var pilihPusat = document.getElementById('pusat_id');
var pilihStatus = document.getElementById('status');
var timer = null;
var fetchSemasa = null;
// Cari terus semasa menaip (AJAX) — butang Cari kekal sebagai fallback tanpa JS.
function cariSekarang() {
var params = new URLSearchParams(new FormData(borang));
var url = borang.action + (params.toString() ? '?' + params.toString() : '');
if (fetchSemasa) {
fetchSemasa.abort();
}
fetchSemasa = new AbortController();
document.getElementById('senarai-ktm').style.opacity = '0.5';
fetch(url, {
headers: { 'X-Requested-With': 'XMLHttpRequest' },
signal: fetchSemasa.signal,
})
.then(function (r) { return r.text(); })
.then(function (html) {
var doc = new DOMParser().parseFromString(html, 'text/html');
['stat-grid', 'senarai-ktm'].forEach(function (id) {
var baru = doc.getElementById(id);
if (baru) {
document.getElementById(id).replaceWith(baru);
}
});
// Kekalkan URL supaya refresh/eksport/set-semula konsisten
window.history.replaceState(null, '', url);
})
.catch(function (e) {
if (e.name !== 'AbortError') {
borang.submit(); // fallback: muat semula penuh
}
});
}
function cariLewat() {
clearTimeout(timer);
timer = setTimeout(cariSekarang, 300);
}
inputCari.addEventListener('input', cariLewat);
pilihPusat.addEventListener('change', cariSekarang);
pilihStatus.addEventListener('change', cariSekarang);
// Elak penghantaran borang penuh bila tekan Enter dalam kotak carian
borang.addEventListener('submit', function (e) {
e.preventDefault();
clearTimeout(timer);
cariSekarang();
});
})();
</script>
@endsection @endsection