Webapp redesign: fixes from the functional regression test

- Unattend: a command's '...' menu stayed open after Move up/down and
  covered the next row's menu, so the next click could stage removing the
  wrong command. The menu now closes and focus returns to its button.
- '...' menus close on an outside click, on Escape and when another
  opens (static/app.js), for every page.
- Drivers: Remove asks to confirm again, as before the redesign. Long zip
  paths in a row body wrap (the page scrolled sideways at 390 px).
- Rollback review returns to the page it was opened from (Dell page or
  Updates overview), for both Cancel and the rollback itself.
- To-do: the ServiceNow period offers a year again (366 days).
- Imaging floor: a search that matches a log-only bay opens that group;
  its open state survives the live refresh. 'Clear all' states the
  number of session files it deletes, hidden finished ones included.
- Dashboard: the image key pattern is valid under the browser's v flag
  (client-side validation was silently skipped).
- Import: allowed_import_source() compares real paths, so
  '<upload dir>/../..' is refused. This path traversal predates the
  redesign; the import moves the source tree, so it mattered.
This commit is contained in:
cproudlock
2026-10-14 14:48:10 -04:00
parent 2bcc9d8016
commit 2ed2cbe2e9
11 changed files with 61 additions and 15 deletions

View File

@@ -33,7 +33,7 @@
.dbody { padding: 2px 24px 16px 68px; display: grid; grid-template-columns: minmax(300px, .9fr) minmax(0, 1.6fr); gap: 12px 32px;
font-size: 13px; align-items: start; }
.dbody dl { margin: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 4px 14px; align-content: start; align-items: baseline; }
.dbody dt { color: var(--muted); font-weight: 400; } .dbody dd { margin: 0; }
.dbody dt { color: var(--muted); font-weight: 400; } .dbody dd { margin: 0; overflow-wrap: anywhere; min-width: 0; }
.dbody h4 { margin: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--muted); letter-spacing: .03em; text-transform: uppercase; }
.dbody .find { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; line-height: 1.5; }
.dbody .find.cleared li { color: var(--muted); }
@@ -676,6 +676,7 @@
else if (act === 'moveto') openMove(it);
else if (act === 'edit') openEditor(state.items.indexOf(it));
else if (act === 'remove') {
if (!confirm('Remove #' + livePos(it) + ' ' + label(it) + ' from the map? The zip stays on disk. Nothing is written until Review and save, and Undo remove puts it back.')) return;
if (it.orig === null) state.items.splice(state.items.indexOf(it), 1);
else { it.removed = true; it.open = false; }
changed();