From c70b1637010ac5e82eb2415a91b67de5c30d6914 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 29 Sep 2026 02:00:59 -0500 Subject: [PATCH] feat(dashboard): account and character pages update without reloading Actions on an account (ban, mute, lock, GM level, email) and on a character (edit, restoring a version, saving its XML) show the result in place (reloadInPlace) instead of reloading the page. Deleting an account goes back to Accounts without leaving the deleted account in the history. After an in-place update (dash:refreshed) the pages show the server's values again the way they do on load: last logins and the mute end in the browser's time, and the GM level picker (unless a new level is being picked). Check: lock and unlock an account: the badge changes with no reload. Change an account's GM level from another tab: the first tab's level picker follows. Edit a character's items: the page updates when the dialog closes. A character's last login updates when they log in. Co-Authored-By: Claude Opus 5.5 --- dDashboardServer/static/js/character-tools.js | 4 +-- .../templates/account-view.jinja2 | 26 ++++++++++++------- .../templates/character-view.jinja2 | 11 +++++--- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/dDashboardServer/static/js/character-tools.js b/dDashboardServer/static/js/character-tools.js index 2201291ab..8d7bf03a0 100644 --- a/dDashboardServer/static/js/character-tools.js +++ b/dDashboardServer/static/js/character-tools.js @@ -132,7 +132,7 @@ var body = buildEdit(); if (!confirm('Save these changes? The player is disconnected if online.')) return; api.action('/api/characters/' + config.id + '/edit', body).then(function (d) { return d.result; }).then(function (r) { - if (r && r.success) { bootstrap.Modal.getInstance(document.getElementById('editModal')).hide(); setTimeout(function () { location.reload(); }, 600); } + if (r && r.success) { bootstrap.Modal.getInstance(document.getElementById('editModal')).hide(); setTimeout(reloadInPlace, 600); } }).catch(function () {}); }); } @@ -205,7 +205,7 @@ var restore = e.target.closest('[data-restore]'); if (!restore || !confirm('Put the character back to this version? The player is disconnected if online, and the current version is kept in the history.')) return; api.action('/api/snapshots/' + restore.dataset.restore + '/restore', {}).then(function (d) { return d.result; }).then(function (r) { - if (r && r.success) setTimeout(function () { location.reload(); }, 600); + if (r && r.success) setTimeout(reloadInPlace, 600); }).catch(function () {}); }); } diff --git a/dDashboardServer/templates/account-view.jinja2 b/dDashboardServer/templates/account-view.jinja2 index 6027da824..0eaafa304 100644 --- a/dDashboardServer/templates/account-view.jinja2 +++ b/dDashboardServer/templates/account-view.jinja2 @@ -485,9 +485,19 @@ document.addEventListener('DOMContentLoaded', function () { loadEmailStatus(); var ownEmail = document.getElementById('ownEmail'); if (ownEmail) ownEmail.value = accountEl.getAttribute('data-email') || ''; + showServerValues(); + // Update in place when someone else changes this account + Live.refreshPage('accounts', accountId); +}); +// After an in-place update (nav.js) the server's values may have changed: show them the same way again +document.addEventListener('dash:refreshed', showServerValues); + +// The GM level, mute and last logins as the server has them, shown in the browser's time +function showServerValues() { var gmSelect = document.getElementById('gmLevelSelect'); - if (gmSelect) { - gmSelect.value = accountEl.getAttribute('data-gm-level'); + // Not while someone is picking a new level + if (gmSelect && (!gmSelect.dataset.shown || gmSelect.value === gmSelect.dataset.shown)) { + gmSelect.value = gmSelect.dataset.shown = accountEl.getAttribute('data-gm-level'); // Only operators may grant their own level or higher Array.prototype.forEach.call(gmSelect.options, function (opt) { if (DASH.gmLevel < 9 && parseInt(opt.value, 10) >= DASH.gmLevel) opt.disabled = true; @@ -503,13 +513,11 @@ document.addEventListener('DOMContentLoaded', function () { var ts = parseInt(el.getAttribute('data-ts'), 10); el.textContent = ts > 0 ? fmt.unix(ts) : 'Never'; }); - // Reload when someone else changes this account - Live.refreshPage('accounts', accountId); -}); +} -// Reload once the change (and any live action it started, like a kick) has finished +// Show the page again once the change (and any live action it started, like a kick) has finished function reloadOnSuccess(promise) { - promise.then(function (d) { return d.result || d; }).then(function () { location.reload(); }).catch(function () {}); + promise.then(function (d) { return d.result || d; }).then(function () { reloadInPlace(); }).catch(function () {}); } var reset2faBtn = document.getElementById('reset2faBtn'); @@ -610,7 +618,7 @@ function deleteAccount() { if (!confirm('PERMANENTLY delete this account and all its characters? This cannot be undone.')) return; if (!confirm('Are you absolutely sure?')) return; } - api.action(base + '/delete', body).then(function (d) { return d.result; }).then(function () { window.location.href = accountEl.dataset.self ? '/login' : '/accounts'; }).catch(function () {}); + api.action(base + '/delete', body).then(function (d) { return d.result; }).then(function () { if (accountEl.dataset.self) window.location.href = '/login'; else goTo('/accounts', { replace: true }); }).catch(function () {}); } function resetPassword() { var pwd = document.getElementById('newPwd').value; @@ -643,7 +651,7 @@ function saveOwnEmail() { pwd.value = ''; toast(d.message, 'success'); return d.result; - }).then(function () { location.reload(); }).catch(function () {}); + }).then(function () { reloadInPlace(); }).catch(function () {}); } function resendVerification() { api.action('/api/account/email/resend', {}).catch(function () {}); diff --git a/dDashboardServer/templates/character-view.jinja2 b/dDashboardServer/templates/character-view.jinja2 index bde02f9ab..a90f5d456 100644 --- a/dDashboardServer/templates/character-view.jinja2 +++ b/dDashboardServer/templates/character-view.jinja2 @@ -486,18 +486,23 @@ {% block scripts %}