/* ui.js — Helper tampilan: escape, format, badge, modal, toast, confirm */
(function (global) {
'use strict';
function escape(v) {
if (v == null) return '';
return String(v)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function formatDate(iso) {
return global.WA ? global.WA.tanggalPanjang(iso) : (iso || '');
}
function formatDateTime(iso) {
if (!iso) return '';
var d = new Date(iso);
if (isNaN(d.getTime())) return '';
var jam = String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0');
return formatDate(iso) + ' ' + jam;
}
function formatBytes(bytes) {
if (!bytes && bytes !== 0) return '';
var units = ['B', 'KB', 'MB', 'GB'];
var i = 0;
var n = bytes;
while (n >= 1024 && i < units.length - 1) { n /= 1024; i++; }
return (i === 0 ? n : n.toFixed(1)) + ' ' + units[i];
}
function badge(status) {
if (!status) return '';
return '' +
escape(status.nama) + '';
}
function escapeAttr(v) { return escape(v); }
/* ---------- Toast ---------- */
function toastBox() {
var box = document.getElementById('toastBox');
if (!box) {
box = document.createElement('div');
box.id = 'toastBox';
document.body.appendChild(box);
}
return box;
}
function toast(message, type) {
var el = document.createElement('div');
el.className = 'toast' + (type === 'ok' ? ' ok' : type === 'err' ? ' err' : '');
el.textContent = message;
toastBox().appendChild(el);
setTimeout(function () {
el.style.transition = 'opacity .25s';
el.style.opacity = '0';
setTimeout(function () { el.remove(); }, 260);
}, 3200);
}
/* ---------- Modal ---------- */
var modalEl = null;
function ensureModal() {
if (modalEl) return modalEl;
modalEl = document.createElement('div');
modalEl.className = 'modal-overlay';
modalEl.innerHTML =
'
';
document.body.appendChild(modalEl);
modalEl.addEventListener('click', function (e) {
if (e.target === modalEl) closeModal();
});
modalEl.querySelector('.modal-x').addEventListener('click', closeModal);
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && modalEl && modalEl.parentNode) closeModal();
});
return modalEl;
}
function openModal(opts) {
var el = ensureModal();
opts = opts || {};
var modal = el.querySelector('.modal');
modal.className = 'modal' + (opts.size === 'lg' ? ' lg' : '');
el.querySelector('.modal-head h3').textContent = opts.title || '';
el.querySelector('.modal-body').innerHTML = opts.body || '';
el.querySelector('.modal-foot').innerHTML = opts.footer || '';
el.querySelector('.modal-foot').style.display = opts.footer ? 'flex' : 'none';
el.style.display = 'flex';
if (typeof opts.onMount === 'function') opts.onMount(el);
var firstInput = el.querySelector('.modal-body input, .modal-body select, .modal-body textarea');
if (firstInput) setTimeout(function () { firstInput.focus(); }, 50);
return el;
}
function closeModal() {
if (modalEl) modalEl.style.display = 'none';
}
function isModalOpen() {
return !!(modalEl && modalEl.style.display !== 'none');
}
function confirmDialog(message, title) {
return global.confirm((title ? title + '\n\n' : '') + message);
}
/* ---------- Banner peringatan ---------- */
function banner(message, type) {
if (!document.body) return;
var host = document.querySelector('main.container') || document.body;
var existing = document.getElementById('appBanner');
if (existing) existing.remove();
var el = document.createElement('div');
el.id = 'appBanner';
var base = 'padding:10px 14px;border-radius:10px;margin-bottom:14px;font-size:13px;line-height:1.5;';
if (type === 'warn') {
el.style.cssText = base + 'background:#fffbeb;border:1px solid #fde68a;color:#92400e;';
} else {
el.style.cssText = base + 'background:#fef2f2;border:1px solid #fecaca;color:#991b1b;';
}
el.textContent = message;
host.insertBefore(el, host.firstChild);
}
// Tampilkan error apa pun sebagai banner, bukan halaman kosong.
global.addEventListener('error', function (e) {
var msg = (e && (e.message || (e.error && e.error.message))) || 'Kesalahan tidak diketahui';
try { banner('Terjadi kesalahan: ' + msg, 'err'); } catch (_) { /* abaikan */ }
});
global.addEventListener('unhandledrejection', function (e) {
var r = e && e.reason;
var msg = (r && (r.message || r)) || 'Operasi gagal';
try { banner('Terjadi kesalahan: ' + msg, 'err'); } catch (_) { /* abaikan */ }
});
global.UI = {
escape: escape, escapeAttr: escapeAttr, formatDate: formatDate, formatDateTime: formatDateTime,
formatBytes: formatBytes, badge: badge, toast: toast, banner: banner,
openModal: openModal, closeModal: closeModal, isModalOpen: isModalOpen,
confirmDialog: confirmDialog
};
})(window);