Confirm a finding in the page instead of with a browser dialog
Both browsers offer "prevent this page from creating additional dialogs" inside the dialog itself, and once it is ticked every later confirm() returns false without drawing anything — so Fix and Never a problem silently declined and read as dead buttons. Unraid swaps tabs by AJAX without reloading, so the suppression outlived leaving the tab and only clearing history got it back. Both now arm on the first press and act on the second, and say what the press will do while they wait for it.
This commit is contained in:
+59
-13
@@ -188,6 +188,12 @@ if (is_dir('/var/log/varaverk')) {
|
||||
/* Each button explains itself on hover from the server's own text, so the page never has to
|
||||
restate what an action means and cannot restate it differently. */
|
||||
.vv-ai-fnd-a .vv-ai-btn { padding:3px 11px; font-size:11px; }
|
||||
/* Waiting for its second press. Confirmation is in the page rather than a browser dialog —
|
||||
confirm() can be switched off from inside itself, after which it answers no forever without
|
||||
drawing anything, and the button reads as dead. This state has to be loud enough that a
|
||||
second press is obviously a second press and not a first one that failed. */
|
||||
.vv-ai-fnd-a .vv-ai-btn.armed { background:#3a2410; border-color:#8a6a3a; color:#ffb74d; }
|
||||
.vv-ai-fnd-a .vv-ai-btn.armed:hover:not(:disabled) { background:#4a2e14; }
|
||||
.vv-ai-fnd-msg { font-size:10px; color:#5a5a5a; margin-left:4px; }
|
||||
|
||||
/* ── Settings card ──────────────────────────────────────────────────────── */
|
||||
@@ -598,6 +604,28 @@ vv_ai_chat_markup('vv-ai', [
|
||||
// uses for the same row.
|
||||
const FND_LABEL = { fix: 'Fix', ack: 'I know', dismiss: 'Never a problem', reopen: 'Reopen' };
|
||||
|
||||
// Which actions take a second press, and what the button says while it is waiting for it.
|
||||
// Fix writes conf; dismiss is the one state the sweep will never reopen on its own however
|
||||
// many times the fault comes back. Ack and reopen are both reversible and go on one press.
|
||||
const FND_ARM = { fix: 'Confirm write', dismiss: 'Confirm — permanent' };
|
||||
let fndArmTimer = null;
|
||||
|
||||
// Put every armed button back, optionally sparing the one being pressed right now.
|
||||
function fndDisarm(keep) {
|
||||
clearTimeout(fndArmTimer);
|
||||
$('vv-ai-fnd').querySelectorAll('button[data-armed="1"]').forEach(b => {
|
||||
if (b === keep) return;
|
||||
b.dataset.armed = '';
|
||||
if (b.dataset.label) b.textContent = b.dataset.label;
|
||||
b.classList.remove('armed');
|
||||
// Cleared the way it was set. textContent would also empty it, but the arming message is
|
||||
// written as innerHTML and a clear that does not match its write is the kind of pairing
|
||||
// that quietly stops matching later.
|
||||
const m = b.parentNode.querySelector('[data-msg]');
|
||||
if (m) m.innerHTML = '';
|
||||
});
|
||||
}
|
||||
|
||||
function loadFindings() {
|
||||
fetch(API + '?action=findings' + (fndAll ? '&all=1' : '')).then(r => r.json())
|
||||
.then(d => { if (d.ok) renderFindings(d); })
|
||||
@@ -675,18 +703,32 @@ vv_ai_chat_markup('vv-ai', [
|
||||
const f = fndRows.find(x => x.id === id);
|
||||
if (!f) return;
|
||||
|
||||
// The two that cannot be walked back get asked about first. Fix edits a conf file, and
|
||||
// dismiss is the one state the sweep will never reopen on its own however many times the
|
||||
// fault comes back.
|
||||
if (act === 'fix' && !confirm(
|
||||
`Write ${f.conf_key} = ${f.proposed}\n\nin ${f.conf_file}, replacing ${f.observed || '(empty)'}.`
|
||||
+ (f.proven ? '' : '\n\nNothing has probed this value — it is a proposal, not a proven fix.')))
|
||||
return;
|
||||
if (act === 'dismiss' && !confirm(
|
||||
`Dismiss "${f.subject} — ${f.ref}" permanently?\n\nIt stays closed even when it is seen `
|
||||
+ `again. To be told if it changes, use "I know" instead.`)) return;
|
||||
|
||||
const msg = $('vv-ai-fnd').querySelector(`[data-msg="${id}"]`);
|
||||
|
||||
// Fix and dismiss are confirmed in the page, never with confirm(). Both browsers offer
|
||||
// "prevent this page from creating additional dialogs" inside the dialog itself, and once
|
||||
// that is ticked every later confirm() returns false without showing anything — so the
|
||||
// buttons silently decline and read as dead. Unraid swaps tabs by AJAX without reloading,
|
||||
// so the suppression outlives leaving the tab and only a full reload clears it. That is
|
||||
// exactly what happened here: not a broken button, a dialog answering no on its own.
|
||||
if (FND_ARM[act] && btn.dataset.armed !== '1') {
|
||||
fndDisarm();
|
||||
btn.dataset.armed = '1';
|
||||
btn.dataset.label = btn.textContent;
|
||||
btn.textContent = FND_ARM[act];
|
||||
btn.classList.add('armed');
|
||||
if (msg) msg.innerHTML = act === 'fix'
|
||||
? `<span class="vv-ai-warn">Writes the change above to ${esc(f.conf_file)}`
|
||||
+ `${f.proven ? '' : ', and nothing has probed it'}. Click again to confirm.</span>`
|
||||
: `<span class="vv-ai-warn">Stays closed even when it is seen again. `
|
||||
+ `Use “I know” to be told if it changes. Click again to confirm.</span>`;
|
||||
// Arming expires on its own so a half-pressed button cannot sit there waiting to be
|
||||
// completed by an unrelated click later.
|
||||
fndArmTimer = setTimeout(fndDisarm, 8000);
|
||||
return;
|
||||
}
|
||||
clearTimeout(fndArmTimer);
|
||||
|
||||
const btns = Array.from(btn.parentNode.querySelectorAll('button'));
|
||||
btns.forEach(b => b.disabled = true);
|
||||
if (msg) msg.textContent = 'working…';
|
||||
@@ -694,10 +736,14 @@ vv_ai_chat_markup('vv-ai', [
|
||||
// Reload on success, never on failure. A refused conf write is the case that must not
|
||||
// disappear quietly: the guarded path rolled back, the finding is unchanged, and a reload
|
||||
// would repaint the row identically half a second later and take the error with it. So the
|
||||
// failed row keeps its message and its buttons, and the operator decides what to do next.
|
||||
// failed row keeps its message, and its buttons go back to reading Fix rather than sitting
|
||||
// at "Confirm write" — a second press should have to arm again, not fire again.
|
||||
const failed = text => {
|
||||
if (msg) msg.innerHTML = `<span class="vv-ai-bad">${esc(text)}</span>`;
|
||||
btn.dataset.armed = '';
|
||||
if (btn.dataset.label) btn.textContent = btn.dataset.label;
|
||||
btn.classList.remove('armed');
|
||||
btns.forEach(b => b.disabled = false);
|
||||
if (msg) msg.innerHTML = `<span class="vv-ai-bad">${esc(text)}</span>`;
|
||||
};
|
||||
|
||||
fetch(API, { method: 'POST',
|
||||
|
||||
Reference in New Issue
Block a user