style(webui): unify UI across all pages to solid modern system
tokens instead of hardcoded, v9 everywhere, dialog guarded, confirm->dialog, empty unified, health out of nav, swagger icons/topbar, gutters, th uppercase, content 1280 centered.
This commit is contained in:
+44
-6
@@ -38,6 +38,42 @@ function showToast(message, type = 'info') {
|
||||
}, 3600);
|
||||
}
|
||||
|
||||
function confirmAction(message) {
|
||||
return new Promise((resolve) => {
|
||||
let settled = false;
|
||||
const done = (value) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
if (dialog.open) dialog.close();
|
||||
resolve(value);
|
||||
};
|
||||
let dialog = document.getElementById('confirm-dialog');
|
||||
if (!dialog) {
|
||||
dialog = document.createElement('dialog');
|
||||
dialog.id = 'confirm-dialog';
|
||||
dialog.className = 'confirm-dialog';
|
||||
dialog.innerHTML =
|
||||
'<div class="confirm-dialog-body"><div class="eyebrow">Confirm</div>' +
|
||||
'<p class="confirm-dialog-message muted"></p>' +
|
||||
'<div class="confirm-dialog-actions">' +
|
||||
'<button class="button" data-confirm-cancel type="button">Cancel</button>' +
|
||||
'<button class="button primary" data-confirm-ok type="button">Confirm</button>' +
|
||||
'</div></div>';
|
||||
document.body.appendChild(dialog);
|
||||
}
|
||||
dialog.querySelector('.confirm-dialog-message').textContent = message;
|
||||
dialog.querySelector('[data-confirm-ok]').onclick = () => done(true);
|
||||
dialog.querySelector('[data-confirm-cancel]').onclick = () => done(false);
|
||||
dialog.onclose = () => done(false);
|
||||
if (!dialog.open) dialog.showModal();
|
||||
});
|
||||
}
|
||||
|
||||
function setHealthState(el, ok, warnOnly = false) {
|
||||
el.classList.remove('is-ok', 'is-warn', 'is-dim');
|
||||
el.classList.add(ok ? 'is-ok' : warnOnly ? 'is-dim' : 'is-warn');
|
||||
}
|
||||
|
||||
function downloadJson(filename, payload) {
|
||||
const blob = new Blob([JSON.stringify(payload, null, 2) + '\n'], {
|
||||
type: 'application/json',
|
||||
@@ -105,7 +141,7 @@ function renderAccountList() {
|
||||
root.innerHTML = '';
|
||||
|
||||
if (!settingsState.accounts.length) {
|
||||
root.innerHTML = '<div class="empty-state">No accounts yet.</div>';
|
||||
root.innerHTML = '<div class="empty-state"><p class="muted">No accounts yet.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -168,17 +204,19 @@ function renderAccountData() {
|
||||
: 'No account selected.';
|
||||
|
||||
document.getElementById('health-credentials').textContent = health.api_credentials ? 'Saved' : 'Missing';
|
||||
document.getElementById('health-credentials').style.color = health.api_credentials ? 'var(--ok)' : 'var(--warn)';
|
||||
setHealthState(document.getElementById('health-credentials'), Boolean(health.api_credentials));
|
||||
document.getElementById('health-session').textContent =
|
||||
health.session_ready || isAccountAuthorized(auth) ? 'Ready' : 'Missing';
|
||||
document.getElementById('health-session').style.color =
|
||||
health.session_ready || isAccountAuthorized(auth) ? 'var(--ok)' : 'var(--warn)';
|
||||
setHealthState(
|
||||
document.getElementById('health-session'),
|
||||
Boolean(health.session_ready || isAccountAuthorized(auth)),
|
||||
);
|
||||
document.getElementById('health-continuous').textContent = continuousStatus.running
|
||||
? 'Running'
|
||||
: continuousConfig.enabled
|
||||
? 'Enabled'
|
||||
: 'Stopped';
|
||||
document.getElementById('health-continuous').style.color = continuousStatus.running ? 'var(--ok)' : 'var(--dim)';
|
||||
setHealthState(document.getElementById('health-continuous'), Boolean(continuousStatus.running), true);
|
||||
document.getElementById('health-last-scrape').textContent = displayTime(
|
||||
health.last_scrape || continuousStatus.last_iteration_at,
|
||||
);
|
||||
@@ -342,7 +380,7 @@ async function exportAccount() {
|
||||
async function deleteAccount() {
|
||||
if (!settingsState.activeAccount) return;
|
||||
const account = settingsState.accounts.find((item) => item.id === settingsState.activeAccount);
|
||||
if (!window.confirm(`Remove account "${accountLabel(account)}"? All its account data will be deleted.`)) return;
|
||||
if (!(await confirmAction(`Remove account "${accountLabel(account)}"? All its account data will be deleted.`))) return;
|
||||
await api(`/api/accounts/${encodeURIComponent(settingsState.activeAccount)}`, { method: 'DELETE' });
|
||||
setActiveAccount(null);
|
||||
await loadAccounts();
|
||||
|
||||
Reference in New Issue
Block a user