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:
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user