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:
@@ -3,7 +3,7 @@
|
||||
@section('tajuk', 'Dashboard Kehadiran KTM')
|
||||
|
||||
@section('kandungan')
|
||||
<div class="stat-grid">
|
||||
<div class="stat-grid" id="stat-grid">
|
||||
<div class="stat">
|
||||
<div class="nilai">{{ $jumlahSemua }}</div>
|
||||
<div class="label">Jumlah KTM</div>
|
||||
@@ -19,7 +19,7 @@
|
||||
</div>
|
||||
|
||||
<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;">
|
||||
@if (auth()->user()->isSuperadmin())
|
||||
<div>
|
||||
@@ -71,7 +71,7 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="kad" style="padding:0;">
|
||||
<div class="kad" style="padding:0;" id="senarai-ktm">
|
||||
<div class="jadual-kepala">
|
||||
<h2 class="tajuk-jadual mb-0">Senarai KTM (Ketua Tempat Mengundi)</h2>
|
||||
</div>
|
||||
@@ -145,4 +145,66 @@
|
||||
<span class="teks-kecil">{{ $assignments->count() }} rekod dipaparkan</span>
|
||||
</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
|
||||
|
||||
Reference in New Issue
Block a user