Confirm dialog (#5314): - Every data-confirm form now asks in one native <dialog> (base.html + static/confirm.js) instead of window.confirm. - The question becomes the heading and what follows it the detail; the act's button is named after the form's own button; Cancel has the focus and Esc backs out. Optional data-confirm-title/-detail/-action/-dismiss attributes. - All 27 existing uses work as they are. Cancelling a run says 'Keep running'. Alerts list (#5315): - A verdict line (what is firing, how many rules, where notifications go, or 'notifies nobody yet' with a link) replaces the empty Active card. - Each rule's name opens it; rows show 'last fired' and fold fire/clear into the condition; History and Disable stay; Delete leaves the list. - .docker-verdict becomes the shared .page-verdict. Rule page (#5317): - Named for the rule (title and crumb), with when it last fired and how many changes of state it holds. - Three sections: what it watches, when it fires (checks side by side, seconds echoed as a duration), what happens (where notifications go, no-data, the playbook, whose fields stay folded until ticked). - Delete at the foot, behind the dialog, saying its history goes with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
52 lines
2.5 KiB
JavaScript
52 lines
2.5 KiB
JavaScript
// Steward's own confirm dialog (#5314): a form, or its submit button, with
|
||
// data-confirm="Question? What follows." asks in the #confirm-dialog that
|
||
// base.html renders before it submits, never through window.confirm.
|
||
//
|
||
// The prompt is attribute text read through dataset and written with
|
||
// textContent, never parsed as HTML or JavaScript, so a name inside it
|
||
// cannot run as script (#5099). Optional attributes beside data-confirm:
|
||
// data-confirm-title the heading (default: the prompt up to its first "? ")
|
||
// data-confirm-detail the line under it (default: the rest of the prompt)
|
||
// data-confirm-action the button that goes ahead (default: the submit
|
||
// button's own label, or the prompt's first word)
|
||
// data-confirm-dismiss the button that backs out (default: "Cancel")
|
||
// Cancel has the focus, and Esc backs out.
|
||
(function () {
|
||
function label(btn, title) {
|
||
var own = btn ? (btn.dataset.confirmAction || btn.textContent || btn.value || '').trim() : '';
|
||
if (own.length > 2) return own; // "×" and other icons say nothing
|
||
return title.split(' ')[0].replace(/\?$/, '');
|
||
}
|
||
|
||
function ask(form, btn) {
|
||
var src = btn && btn.dataset.confirm ? btn : form;
|
||
var prompt = src.dataset.confirm;
|
||
var cut = prompt.indexOf('? ');
|
||
var title = src.dataset.confirmTitle || (cut > 0 ? prompt.slice(0, cut + 1) : prompt);
|
||
var detail = src.dataset.confirmDetail || (cut > 0 ? prompt.slice(cut + 2) : '');
|
||
var d = document.getElementById('confirm-dialog');
|
||
d.querySelector('[data-part="title"]').textContent = title;
|
||
var p = d.querySelector('[data-part="detail"]');
|
||
p.textContent = detail;
|
||
p.hidden = !detail;
|
||
d.querySelector('[data-part="go"]').textContent = src.dataset.confirmAction || label(btn, title);
|
||
d.querySelector('[data-part="back"]').textContent = src.dataset.confirmDismiss || 'Cancel';
|
||
d.returnValue = '';
|
||
d.onclose = function () {
|
||
if (d.returnValue !== 'go') return;
|
||
form.dataset.confirmed = '1';
|
||
form.requestSubmit(btn && btn.form === form ? btn : undefined);
|
||
};
|
||
d.showModal();
|
||
}
|
||
|
||
document.addEventListener('submit', function (ev) {
|
||
var form = ev.target, btn = ev.submitter;
|
||
if (!((btn && btn.dataset.confirm) || form.dataset.confirm)) return;
|
||
if (form.dataset.confirmed === '1') { delete form.dataset.confirmed; return; } // the asked-for submit
|
||
ev.preventDefault();
|
||
ev.stopPropagation(); // before HTMX sees it: an hx-post form waits for the answer too
|
||
ask(form, btn);
|
||
}, true);
|
||
})();
|