chore: matikan realtime stream di dashboard

Ganti SSE /api/dashboard/stream dengan fetch sekali ke /api/dashboard/realtime supaya halaman dashboard lebih ringan dan cepat dibuka.
This commit is contained in:
mwpn
2026-03-06 16:55:03 +07:00
parent 8d7cdd05b7
commit 461c5c7882

View File

@@ -43,8 +43,8 @@
<div class="rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-sm overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
<h2 class="text-lg font-semibold">Realtime Attendance</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-0.5">Live stream via Server-Sent Events. Newest at top.</p>
<h2 class="text-lg font-semibold">Riwayat Absensi Terbaru</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-0.5">Daftar absen terakhir (tanpa live stream), paling baru di atas.</p>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left">
@@ -57,11 +57,7 @@
<th class="px-6 py-3 font-medium">Status</th>
</tr>
</thead>
<tbody id="attendance-tbody" class="divide-y divide-gray-200 dark:divide-gray-700">
<tr>
<td colspan="5" class="px-6 py-8 text-center text-gray-500 dark:text-gray-400">Connecting to stream…</td>
</tr>
</tbody>
<tbody id="attendance-tbody" class="divide-y divide-gray-200 dark:divide-gray-700"></tbody>
</table>
</div>
</div>
@@ -131,10 +127,7 @@
.catch(function() {});
var tbody = document.getElementById('attendance-tbody');
var streamUrl = baseUrl + '/api/dashboard/stream';
var afterId = 0;
var placeholderRow = null;
var connected = false;
var realtimeApiUrl = baseUrl + '/api/dashboard/realtime';
function badgeClass(status) {
var s = (status || '').toUpperCase();
@@ -152,10 +145,6 @@
}
function addRow(data) {
if (placeholderRow && placeholderRow.parentNode) {
placeholderRow.remove();
placeholderRow = null;
}
var tr = document.createElement('tr');
tr.className = 'hover:bg-gray-50 dark:hover:bg-gray-700/30';
tr.setAttribute('data-id', data.id);
@@ -166,8 +155,7 @@
'<td class="px-6 py-3 text-gray-600 dark:text-gray-400">' + escapeHtml(data.class_name) + '</td>' +
'<td class="px-6 py-3 text-gray-600 dark:text-gray-400">' + escapeHtml(data.subject) + '</td>' +
'<td class="px-6 py-3"><span class="inline-flex px-2.5 py-0.5 rounded-full text-xs font-medium ' + statusClass + '">' + escapeHtml(data.status) + '</span></td>';
tbody.insertBefore(tr, tbody.firstChild);
if (data.id > afterId) afterId = data.id;
tbody.appendChild(tr);
}
function escapeHtml(str) {
@@ -177,43 +165,29 @@
return div.innerHTML;
}
function setPlaceholder(msg) {
if (placeholderRow && placeholderRow.parentNode) placeholderRow.remove();
placeholderRow = document.createElement('tr');
placeholderRow.innerHTML = '<td colspan="5" class="px-6 py-8 text-center text-gray-500 dark:text-gray-400">' + escapeHtml(msg) + '</td>';
tbody.appendChild(placeholderRow);
function loadRealtimeOnce() {
fetch(realtimeApiUrl, { method: 'GET', credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' } })
.then(function(res) { return res.json().then(function(j) { return { ok: res.ok, data: j }; }); })
.then(function(r) {
if (!r.ok) return;
var list = (r.data && r.data.data) ? r.data.data : (Array.isArray(r.data) ? r.data : []);
tbody.innerHTML = '';
if (!list.length) {
var tr = document.createElement('tr');
tr.innerHTML = '<td colspan="5" class="px-6 py-8 text-center text-gray-500 dark:text-gray-400">Belum ada data absensi hari ini.</td>';
tbody.appendChild(tr);
return;
}
list.forEach(function(row) { addRow(row); });
})
.catch(function() {
var tr = document.createElement('tr');
tr.innerHTML = '<td colspan="5" class="px-6 py-8 text-center text-red-500 dark:text-red-400">Gagal memuat data absensi.</td>';
tbody.appendChild(tr);
});
}
function connect() {
var url = streamUrl + (afterId ? '?after_id=' + afterId : '');
var es = new EventSource(url);
es.addEventListener('attendance', function(e) {
try {
var data = JSON.parse(e.data);
if (data && data.id) addRow(data);
} catch (err) {}
});
es.addEventListener('heartbeat', function() {
if (!connected) {
connected = true;
setPlaceholder('No attendance records yet. Waiting for new check-ins…');
}
});
es.addEventListener('timeout', function() {
es.close();
connected = false;
setPlaceholder('Stream ended. Reconnecting…');
setTimeout(connect, 3000);
});
es.onerror = function() {
es.close();
connected = false;
setPlaceholder('Connection lost. Reconnecting…');
setTimeout(connect, 3000);
};
}
connect();
loadRealtimeOnce();
if (typeof window.addEventListener === 'function') {
window.addEventListener('beforeunload', function() {