Files
2026-06-13 00:31:36 +07:00

146 lines
5.3 KiB
PHP

</div><!-- /admin-content -->
</main><!-- /admin-main -->
<!-- Toast Container -->
<div class="toast-container" id="toastContainer"></div>
<script>
// ── Theme toggle ──
(function() {
const html = document.documentElement;
const saved = localStorage.getItem('portal-theme') || 'dark';
setUserTheme(saved);
document.addEventListener('DOMContentLoaded', function() {
const btn = document.getElementById('userThemeToggle');
const icon = document.getElementById('userThemeIcon');
if (!btn) return;
btn.addEventListener('click', () => {
const next = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
setUserTheme(next);
localStorage.setItem('portal-theme', next);
});
function updateIcon() {
if (icon) icon.className = html.getAttribute('data-theme') === 'dark' ? 'fas fa-sun' : 'fas fa-moon';
}
updateIcon();
});
function setUserTheme(t) {
html.setAttribute('data-theme', t);
const icon = document.getElementById('userThemeIcon');
if (icon) icon.className = t === 'dark' ? 'fas fa-sun' : 'fas fa-moon';
}
})();
// ── Clock ──
function updateClock() {
const now = new Date();
document.getElementById('clockText').textContent =
now.toLocaleTimeString('id-ID', { hour:'2-digit', minute:'2-digit', second:'2-digit' });
}
updateClock();
setInterval(updateClock, 1000);
// ── Toast utility ──
window.showToast = function(msg, type = 'success') {
const t = document.createElement('div');
t.className = `toast ${type}`;
const icons = { success: 'check-circle', error: 'exclamation-circle', info: 'info-circle' };
t.innerHTML = `<i class="fas fa-${icons[type]||'info-circle'}" style="margin-right:8px;"></i>${msg}`;
document.getElementById('toastContainer').appendChild(t);
setTimeout(() => { t.style.opacity = '0'; setTimeout(() => t.remove(), 400); }, 3000);
};
// ── Modal helpers ──
window.openModal = function(id) {
document.getElementById(id)?.classList.add('active');
document.body.style.overflow = 'hidden';
};
window.closeModal = function(id) {
document.getElementById(id)?.classList.remove('active');
document.body.style.overflow = '';
};
// Close modal on overlay click
document.querySelectorAll('.modal-overlay').forEach(overlay => {
overlay.addEventListener('click', e => {
if (e.target === overlay) closeModal(overlay.id);
});
});
// ── Search filter ──
window.filterTable = function(inputId, tableId) {
const q = document.getElementById(inputId)?.value.toLowerCase() || '';
document.querySelectorAll(`#${tableId} tbody tr`).forEach(row => {
row.style.display = row.textContent.toLowerCase().includes(q) ? '' : 'none';
});
};
// Realtime SPBU snackbar
(function initSpbuRealtimeSnackbar() {
const API_URL = APP_BASE + '/api/spbu_status.php';
const REFRESH_MS = 60000;
const SHOW_MS = 7000;
let lastOpen = null;
let hideTimer = null;
function ensureSnackbar() {
let el = document.getElementById('spbuRealtimeSnackbar');
if (el) return el;
el = document.createElement('div');
el.id = 'spbuRealtimeSnackbar';
el.className = 'spbu-snackbar';
el.innerHTML = `
<div class="spbu-snackbar-icon"><i class="fas fa-gas-pump"></i></div>
<div>
<div class="spbu-snackbar-title" id="spbuSnackbarTitle">SPBU sedang buka</div>
<div class="spbu-snackbar-meta" id="spbuSnackbarMeta">Memuat status terbaru...</div>
</div>
<div class="spbu-snackbar-count" id="spbuSnackbarCount">-</div>
`;
document.body.appendChild(el);
return el;
}
function showSnackbar(data) {
const el = ensureSnackbar();
const open = Number(data.spbu_buka || 0);
const total = Number(data.total_spbu || 0);
const closed = Number(data.spbu_tutup || Math.max(0, total - open));
const time = data.server_time ? data.server_time.slice(11, 16) : new Date().toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit' });
document.getElementById('spbuSnackbarTitle').textContent = `${open} SPBU sedang buka`;
document.getElementById('spbuSnackbarMeta').textContent = `${closed} tutup/terbatas dari ${total} total - update ${time} WIB`;
document.getElementById('spbuSnackbarCount').textContent = open;
el.classList.add('show');
clearTimeout(hideTimer);
hideTimer = setTimeout(() => el.classList.remove('show'), SHOW_MS);
}
async function refreshStatus(forceShow = false) {
try {
const res = await fetch(API_URL, { cache: 'no-store' });
const json = await res.json();
if (json.status !== 'success' || !json.data) return;
const open = Number(json.data.spbu_buka || 0);
if (forceShow || lastOpen === null || open !== lastOpen) {
showSnackbar(json.data);
}
lastOpen = open;
} catch (e) {
// Snackbar is informational; keep the page quiet if polling fails.
}
}
document.addEventListener('DOMContentLoaded', () => {
refreshStatus(true);
setInterval(() => refreshStatus(false), REFRESH_MS);
});
})();
</script>
<?= $extraScript ?? '' ?>
</body>
</html>