feat(web): Schedule and General become ES-module pages (stage 3) (#767)

* feat(web): Schedule and General become ES-module pages (stage 3)

Schedule and General follow stage 2 (#727): no inline scripts or inline
handlers in either partial. Their code moves to static/v3/js/pages/schedule.js
and pages/general.js, started per swap-in by the page registry.

- Schedule: both pickers are drawn from the saved config carried as JSON in
  data-* attributes. The forms' hx-on save handlers become one
  htmx:afterRequest listener on the page; the forms are marked
  data-reports-result, which app.js now treats like an hx-on after-request
  handler, so a save still shows one notification.
- General: the timezone picker reads data-timezone. The Security section's
  forms and buttons are delegated data-actions; requests go through
  core/api.js, so the login redirect is quiet, and a change made just
  before a swap is still reported.
- handleScheduleResponse, handleDimScheduleResponse and webLogin stay as
  deprecated aliases through window.LEDMatrix.
- New DOM suites test_schedule_page.js and test_general_page.js; the web
  login unit suite imports the module; test_es_modules.py pins the pages,
  the aliases, and the schedule config's round trip through its attribute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(web): no unused catch bindings or computed writes in the stage 3 modules

Codacy flagged two unused catch variables and dynamic-key writes in
pages/schedule.js and boot.js. The schedule config is read with
getAttribute, and the default days and the webLogin alias object are built
with Object.fromEntries. No behaviour change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-10-04 23:18:02 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 3866aa4519
commit 5a7893b11a
15 changed files with 1069 additions and 421 deletions
+26 -6
View File
@@ -43,18 +43,33 @@ const pages = {
'operation-history': page(function() { return import('../pages/operation-history.js'); }),
'raw-json': page(function() { return import('../pages/raw-json.js'); }),
'backup-restore': page(function() { return import('../pages/backup-restore.js'); }),
'schedule': page(function() { return import('../pages/schedule.js'); }),
'general': page(function() { return import('../pages/general.js'); }),
};
Object.keys(pages).forEach(function(name) { registry.register(name, pages[name]); });
/** Keep window[name] working: forward to the page module's export of the same name. */
function alias(pageName, name, replacement) {
/** A function that calls `pick(module)` with its arguments, loading the page's module if needed. */
function forward(pageName, pick) {
const loader = pages[pageName];
facade.deprecate(name, function() {
return function() {
const args = arguments;
const module = loader.loaded();
if (module) return module[name].apply(null, args);
return loader().then(function(loaded) { return loaded[name].apply(null, args); });
}, replacement);
if (module) return pick(module).apply(null, args);
return loader().then(function(loaded) { return pick(loaded).apply(null, args); });
};
}
/** Keep window[name] working: forward to the page module's export of the same name. */
function alias(pageName, name, replacement) {
facade.deprecate(name, forward(pageName, function(module) { return module[name]; }), replacement);
}
/** The same for an old global object of functions: each method forwards to the export's method. */
function aliasObject(pageName, name, methods, replacement) {
const target = Object.fromEntries(methods.map(function(method) {
return [method, forward(pageName, function(module) { return module[name][method]; })];
}));
facade.deprecate(name, Object.freeze(target), replacement);
}
// Old globals the converted pages used to define.
@@ -65,5 +80,10 @@ alias('cache', 'deleteCacheFile', "the Cache tab's Delete buttons");
['exportBackup', 'loadBackupList', 'validateRestoreFile', 'clearRestore', 'runRestore'].forEach(function(name) {
alias('backup-restore', name, "the Backup & Restore tab's buttons");
});
['handleScheduleResponse', 'handleDimScheduleResponse'].forEach(function(name) {
alias('schedule', name, "the Schedule tab's own save handling");
});
aliasObject('general', 'webLogin', ['setPassword', 'disable', 'createToken', 'copyToken', 'revoke'],
"the General tab's Security buttons");
registry.start();