[feat](trx-frontend-http): implement Phase 1 UX/UI quick wins from Settings analysis

- IX-2: Add confirm() dialogs before all destructive actions (10 history
  clear buttons, scheduler reset, background decode reset)
- IX-6: Add Select All / Deselect All buttons for background decode
  bookmark checklist
- IX-1: Add dirty-state indicator (pulsing dot) on Save buttons when
  unsaved changes exist in scheduler and background decode panels
- A-4: Add role="alert" and aria-live="polite" to toast notification
  elements for screen reader accessibility
- A-3: Add Unicode symbol prefixes to background decode state labels
  (checkmark/triangle/cross) so state is distinguishable without color

https://claude.ai/code/session_01ShfPMW9hPLD3czp9YovkbJ
Signed-off-by: Claude <noreply@anthropic.com>
This commit is contained in:
Claude
2026-03-30 07:33:45 +00:00
committed by Stan Grams
parent 8ea7bf3b84
commit c85a9c9bc4
14 changed files with 155 additions and 13 deletions
@@ -19,6 +19,7 @@
let interleaveTicker = null;
let schedulerStepPending = false;
let schEntryEditIdx = null; // null = adding, number = editing that index
let schedulerDirty = false; // true when unsaved changes exist
// Satellite entry editing state moved to sat-scheduler.js
// -------------------------------------------------------------------------
@@ -127,6 +128,7 @@
bookmarkList = Array.isArray(bms) ? bms : [];
populateTsBookmarkSelect();
renderScheduler();
clearSchedulerDirty();
renderSchedulerInterleaveStatus();
})
.catch(function (e) {
@@ -569,6 +571,7 @@
schCloseEntryForm();
renderTimespanEntries();
markSchedulerDirty();
}
// -------------------------------------------------------------------------
@@ -781,6 +784,7 @@
if (!currentConfig || !currentConfig.entries) return;
currentConfig.entries.splice(idx, 1);
renderTimespanEntries();
markSchedulerDirty();
}
// -------------------------------------------------------------------------
@@ -830,6 +834,7 @@
.then(function (saved) {
currentConfig = saved;
renderScheduler();
clearSchedulerDirty();
showSchedulerToast("Scheduler saved.");
})
.catch(function (e) {
@@ -859,6 +864,7 @@
entries: [],
};
renderScheduler();
clearSchedulerDirty();
showSchedulerToast("Scheduler reset.");
})
.catch(function (e) {
@@ -866,6 +872,22 @@
});
}
// -------------------------------------------------------------------------
// Dirty-state tracking
// -------------------------------------------------------------------------
function markSchedulerDirty() {
if (schedulerDirty) return;
schedulerDirty = true;
var btn = document.getElementById("scheduler-save-btn");
if (btn) btn.classList.add("sch-dirty");
}
function clearSchedulerDirty() {
schedulerDirty = false;
var btn = document.getElementById("scheduler-save-btn");
if (btn) btn.classList.remove("sch-dirty");
}
// -------------------------------------------------------------------------
// Toast helper
// -------------------------------------------------------------------------
@@ -918,6 +940,21 @@
schedulerSelectRelativeEntry(1);
});
// Dirty-state: mark dirty on any user input/change within the scheduler panel
var schPanel = document.getElementById("scheduler-panel");
if (schPanel && !schPanel._dirtyWired) {
schPanel._dirtyWired = true;
schPanel.addEventListener("input", function (e) {
// Ignore the entry-form inputs (they don't affect saved config until submitted)
if (e.target.closest("#sch-entry-form") || e.target.closest("#sch-sat-form")) return;
markSchedulerDirty();
});
schPanel.addEventListener("change", function (e) {
if (e.target.closest("#sch-entry-form") || e.target.closest("#sch-sat-form")) return;
markSchedulerDirty();
});
}
wireExtraBmAdd();
wireSatelliteEvents();
}