Files
pxe-server/webapp/static/pxe-ui.css
cproudlock a6f95fcca1 Webapp redesign phase 5: Blancco reports, report and to-do
Reports (/reports):
- Grouped by month of the erasure date inside each report (not the file
  date), newest first; older months fold. Each month header shows the
  evidence pack state (builds on the 1st, to upload with Download, or
  uploaded).
- One line per report: erasure date, system serial, model, drives,
  result ('Successful, fallback' when a failed attempt fell back to
  another standard), cloud and ServiceNow state. The row body lists every
  drive and erasure attempt with the failure reason.
- Search by system serial, model, drive serial or file; filters for
  failed, not in cloud and to tick. 'To tick' uses the to-do's 30-day
  window; older unticked reports read 'Not ticked'.
- Export and the evidence pack download moved into header popovers.

Report (/reports/view/<file>):
- Header with serial, model and the per-drive result; a notice names
  the drive to destroy until its COD is recorded; Copy ServiceNow note.
- One card per drive with every attempt, a step track and the failure
  reason; steps and error counts fold. System, customer and provenance
  (integrity signature folded, copyable) on the side. Sizes in decimal
  units, as on the list.
- blancco_evidence.failure_note() is shared with the ServiceNow list.

To-do (/reports/todo):
- The ServiceNow tick list is now its first section (period and show
  ticked filters); /reports/servicenow redirects there.
- Five counters; one line per item with details in the row body.
- COD recorded, Mark uploaded and Resolved confirm first (the attribute
  quoting is safe for JSON) and can be undone for 24 hours from the
  page. The header total includes hardware gaps, matching the sidebar.
2026-10-14 14:00:54 -04:00

283 lines
19 KiB
CSS

/* ==========================================================================
pxe-ui.css - PXE Manager redesign: tokens, shell and components.
Loads AFTER bootstrap.min.css and pxe-theme.css. The legacy tokens
(--bg-card, --border, --text-light, ...) are pointed at the new ones at
the bottom of the token block, so pages not yet redesigned blend in.
Air-gap safe: no external fonts or URLs.
========================================================================== */
/* --------------------------------------------------------------------------
Tokens. Light on :root, dark under [data-theme="dark"] (set by base.html
before first paint) and under prefers-color-scheme as a no-JS fallback.
-------------------------------------------------------------------------- */
:root {
--bg: #EEF1F4; --surface: #FFFFFF; --surface-2: #F6F7F9; --surface-3: #F1F3F6;
--text: #12161C; --text-2: #3A424E; --muted: #4A5361; --line: #E6EAEF; --line-2: #D5DBE2; --control: #7D8899;
--accent: #1F5FBF; --accent-hover: #174A94; --on-accent: #FFFFFF;
--fail: #B42318; --on-fail: #FFFFFF; --fail-soft: #FDECEA; --fail-text: #8F1C13;
--act: #F2A93B; --on-act: #151B24; --act-edge: #9A6200; --act-track: #C27C0E;
--run: #1F5FBF; --on-run: #FFFFFF;
--ok: #2E7D4F; --ok-soft: #E3F2E9; --ok-text: #14532D;
--warn-soft: #FFF0D6; --warn-text: #6B4000;
--neutral-soft: #E8EDF3; --neutral-text: #151B24;
--w11-soft: #E1ECFA; --w11-text: #174A94;
--w10-soft: #E8EBEF; --w10-text: #3A424E;
--off-soft: #FFFFFF; --off-text: #3A424E; --off-edge: #7D8899;
--edit-soft: #F2F7FF; --remove-soft: #F4F5F7;
--track-empty: #C9D1DC; --track-done: #7D8899;
--tip-bg: #151B24; --tip-text: #FFFFFF;
--del-bg: #FDECEA; --del-text: #8F1C13; --add-bg: #E3F2E9; --add-text: #14532D;
--backdrop: rgba(18, 22, 28, .55); --shadow: rgba(18, 22, 28, .08);
/* GE brand sidebar (theme-invariant colours, own tokens so dark can deepen it) */
--side-bg: #00003D; --side-hi: rgba(255, 255, 255, .12); --side-text: #E3E6F5; --side-head: #A4ABD6;
--side-mark: #4181FF; --badge-bg: #E8EDF3; --badge-text: #151B24;
--savebar-bg: #151B24; --savebar-edge: #151B24; --savebar-text: #FFFFFF; --savebar-sub: #C9D1DC;
--savebar-pri: #78A9F0; --on-savebar-pri: #0F141A;
--font: 'Segoe UI', system-ui, -apple-system, sans-serif;
--font-mono: Consolas, 'SF Mono', ui-monospace, monospace;
}
[data-theme="dark"] {
--bg: #0F141A; --surface: #182029; --surface-2: #1E2731; --surface-3: #232D38;
--text: #E6EAF0; --text-2: #C2CAD4; --muted: #A3ADB9; --line: #26303B; --line-2: #34404D; --control: #7B8796;
--accent: #78A9F0; --accent-hover: #9BC0F5; --on-accent: #0F141A;
--fail: #F0705F; --on-fail: #1A0B09; --fail-soft: #3A1A17; --fail-text: #FFB4A9;
--act: #F2A93B; --on-act: #151B24; --act-edge: #F2A93B; --act-track: #F2A93B;
--run: #78A9F0; --on-run: #0F141A;
--ok: #4CAF78; --ok-soft: #173323; --ok-text: #9AD8B0;
--warn-soft: #3A2C10; --warn-text: #F6CD7E;
--neutral-soft: #2A3440; --neutral-text: #E6EAF0;
--w11-soft: #1C2F4A; --w11-text: #A9C8F5;
--w10-soft: #2A3038; --w10-text: #C7CED7;
--off-soft: #182029; --off-text: #C2CAD4; --off-edge: #7B8796;
--edit-soft: #17243A; --remove-soft: #1B222B;
--track-empty: #34404D; --track-done: #8592A3;
--tip-bg: #E6EAF0; --tip-text: #0F141A;
--del-bg: #3A1A17; --del-text: #FFB4A9; --add-bg: #173323; --add-text: #9AD8B0;
--backdrop: rgba(0, 0, 0, .6); --shadow: rgba(0, 0, 0, .35);
--side-bg: #00002A; --badge-bg: #2A3440; --badge-text: #E6EAF0;
--savebar-bg: #2B3542; --savebar-edge: #4A5666;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) {
--bg: #0F141A; --surface: #182029; --surface-2: #1E2731; --surface-3: #232D38;
--text: #E6EAF0; --text-2: #C2CAD4; --muted: #A3ADB9; --line: #26303B; --line-2: #34404D; --control: #7B8796;
--accent: #78A9F0; --accent-hover: #9BC0F5; --on-accent: #0F141A;
}
}
/* Legacy tokens (pxe-theme.css and Bootstrap pages) follow the new ones. */
:root, [data-theme="dark"] {
--bg-card: var(--surface); --bg-card-solid: var(--surface); --border: var(--line-2);
--text-light: var(--muted); --link: var(--accent); --primary: var(--accent); --primary-dark: var(--accent-hover);
--sidebar-bg: var(--side-bg); --sidebar-width: 236px;
--bs-body-bg: var(--bg); --bs-body-color: var(--text); --bs-border-color: var(--line-2);
}
/* --------------------------------------------------------------------------
Base
-------------------------------------------------------------------------- */
body { font-family: var(--font); font-size: 14px; letter-spacing: 0; line-height: 1.5; background: var(--bg); color: var(--text); }
a { color: var(--accent); }
a:hover { color: var(--accent-hover); }
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible,
summary:focus-visible, [tabindex]:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.mono { font-family: var(--font-mono); }
.muted { color: var(--muted); }
.sub { font-size: 12px; color: var(--muted); }
.vh { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.skip-link { position: absolute; left: 8px; top: -60px; background: var(--surface); color: var(--text); padding: 10px 14px; border-radius: 6px; z-index: 2000; }
.skip-link:focus { top: 8px; }
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 4px; flex: none; }
/* --------------------------------------------------------------------------
Shell: GE sidebar + main
-------------------------------------------------------------------------- */
.ui-shell { display: flex; min-height: 100vh; }
.ui-side {
position: sticky; top: 0; height: 100vh; overflow-y: auto;
flex: 0 0 236px; max-width: 236px; box-sizing: border-box;
background: var(--side-bg); color: var(--side-text);
padding: 16px 12px; display: flex; flex-direction: column; gap: 18px; z-index: 1000;
}
.ui-brand { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 2px 8px; }
.ui-brand .logo { width: 150px; max-width: 100%; filter: brightness(0) invert(1); display: block; }
.ui-brand .app { font-weight: 600; font-size: 14px; color: #FFFFFF; margin-top: 6px; }
.ui-brand .host { font-family: var(--font-mono); font-size: 11px; color: var(--side-head); }
.ui-menu-btn { display: none; width: 44px; height: 44px; align-items: center; justify-content: center; border: 1px solid rgba(255,255,255,.35); border-radius: 8px; background: transparent; color: #FFFFFF; }
.ui-groups { display: flex; flex-direction: column; gap: 16px; }
.ui-group { display: flex; flex-direction: column; gap: 2px; }
.ui-navhead { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--side-head); padding: 0 12px 4px; }
.ui-navlink {
display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 36px; padding: 0 12px;
border-radius: 7px; color: var(--side-text); text-decoration: none; font-size: 14px;
}
.ui-navlink:hover { background: var(--side-hi); color: #FFFFFF; }
.ui-navlink[aria-current] { background: var(--side-hi); color: #FFFFFF; font-weight: 600; box-shadow: inset 3px 0 0 var(--side-mark); }
.ui-navlink .count { font-family: var(--font-mono); font-size: 12px; background: var(--badge-bg); color: var(--badge-text); border-radius: 10px; padding: 1px 8px; }
.ui-side a:focus-visible, .ui-side button:focus-visible { outline-color: #FFFFFF; }
.ui-theme {
display: flex; align-items: center; gap: 8px; min-height: 36px; padding: 0 12px; margin-top: auto;
border: 1px solid rgba(255,255,255,.35); border-radius: 7px; background: transparent; color: var(--side-text); font: inherit; font-size: 13px;
}
.ui-main { flex: 1 1 560px; min-width: 0; padding: 28px clamp(16px, 4vw, 48px) 56px; box-sizing: border-box; }
/* Legacy pages set .main-content; inside the new shell it needs no offset. */
.ui-main.main-content { margin-left: 0; }
@media (max-width: 991px) {
.ui-shell { flex-direction: column; }
.ui-side { position: static; height: auto; flex: none; max-width: none; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; }
.ui-side .ui-groups, .ui-side .ui-theme { display: none; }
.ui-side.open .ui-groups { display: flex; flex-basis: 100%; }
.ui-side.open .ui-theme { display: flex; flex-basis: 100%; }
.ui-menu-btn { display: inline-flex; }
}
/* Page header ------------------------------------------------------------ */
.ui-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 14px; margin-bottom: 22px; }
.ui-head .eyebrow-t { font-size: 13px; color: var(--muted); }
.ui-head h1 { margin: 2px 0 0; font-size: 28px; font-weight: 600; letter-spacing: 0; }
.ui-head .actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.ui-stack { display: flex; flex-direction: column; gap: 22px; }
h2.sec { margin: 0; font-size: 18px; font-weight: 600; }
/* Global to-do strip (one line) ------------------------------------------- */
.ui-todo { display: flex; align-items: center; gap: 12px; padding: 10px 14px; margin-bottom: 18px; border-radius: 10px; background: var(--surface); box-shadow: 0 1px 2px var(--shadow); color: var(--text); text-decoration: none; }
.ui-todo:hover { color: var(--text); box-shadow: 0 2px 8px var(--shadow); }
/* --------------------------------------------------------------------------
Components
-------------------------------------------------------------------------- */
.panel { background: var(--surface); border-radius: 14px; box-shadow: 0 1px 2px var(--shadow); }
.chip { display: inline-block; white-space: nowrap; font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 5px; line-height: 1.4; letter-spacing: 0; }
.c-fail { background: var(--fail); color: var(--on-fail); }
.c-err { background: var(--fail-soft); color: var(--fail-text); box-shadow: inset 0 0 0 1px var(--fail); }
.c-act { background: var(--act); color: var(--on-act); box-shadow: inset 0 0 0 1px var(--act-edge); }
.c-run { background: var(--run); color: var(--on-run); }
.c-ok { background: var(--ok-soft); color: var(--ok-text); }
.c-warn { background: var(--warn-soft); color: var(--warn-text); }
.c-neutral { background: var(--neutral-soft); color: var(--neutral-text); }
.c-w11 { background: var(--w11-soft); color: var(--w11-text); }
.c-w10 { background: var(--w10-soft); color: var(--w10-text); }
.c-off { background: var(--off-soft); color: var(--off-text); box-shadow: inset 0 0 0 1px var(--off-edge); }
.c-edit { background: var(--accent); color: var(--on-accent); }
.c-remove { background: var(--text-2); color: var(--surface); }
/* Truncation: full text stays in the DOM (screen readers read it); hover
shows it. Status, errors, safety warnings and reasons are never hover-only. */
.tip { position: relative; min-width: 0; display: block; }
.tip.inl { display: inline-block; }
.trunc { display: block; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tip[data-tip]:hover::after {
content: attr(data-tip); position: absolute; left: 0; top: calc(100% + 6px); z-index: 20;
width: max-content; max-width: min(440px, 80vw); white-space: normal;
background: var(--tip-bg); color: var(--tip-text); font: 400 12px/1.45 var(--font);
padding: 7px 10px; border-radius: 6px; box-shadow: 0 4px 14px rgba(0,0,0,.25); text-align: left;
}
.tip.right[data-tip]:hover::after { left: auto; right: 0; }
details > summary { list-style: none; cursor: pointer; }
details > summary::-webkit-details-marker { display: none; }
.chev { color: var(--muted); font-size: 11px; transition: transform .15s; flex: none; }
details[open] > summary .chev { transform: rotate(180deg); }
.disc { display: flex; align-items: center; gap: 8px; min-height: 36px; font-size: 13px; color: var(--accent); font-weight: 600; }
.ui-btn {
display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 36px; min-width: 36px; box-sizing: border-box;
padding: 0 12px; border: 1px solid var(--control); border-radius: 7px; background: var(--surface); color: var(--text);
text-decoration: none; font: inherit; font-size: 13px; cursor: pointer; white-space: nowrap; letter-spacing: 0;
}
.ui-btn:hover { color: var(--text); background: var(--surface-3); }
.ui-btn.pri { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.ui-btn.pri:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--on-accent); }
.ui-btn.danger { border-color: var(--fail); color: var(--fail-text); }
.ui-btn.attest { border-color: var(--text-2); font-weight: 600; }
.ui-btn.ghost { border-color: transparent; background: transparent; }
.ui-btn.ghost:hover { background: var(--surface-3); }
.ui-btn[disabled] { opacity: .55; cursor: not-allowed; }
.ui-btn.sm { min-height: 28px; padding: 0 8px; font-size: 12px; }
.fbtn { white-space: nowrap; padding: 0 12px; min-height: 30px; border-radius: 15px; border: 1px solid var(--line-2); background: var(--surface-3); color: var(--text-2); font: inherit; font-size: 13px; cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; gap: 6px; }
.fbtn[aria-pressed="true"], .fbtn[aria-current] { background: var(--text); color: var(--surface); border-color: var(--text); }
details.more { display: inline-block; position: relative; vertical-align: middle; }
details.more .menu {
position: absolute; right: 0; top: calc(100% + 4px); z-index: 30; box-sizing: border-box;
min-width: min(180px, calc(100vw - 32px)); max-width: calc(100vw - 32px);
background: var(--surface); border: 1px solid var(--line-2); border-radius: 8px; box-shadow: 0 6px 18px var(--shadow);
padding: 4px; display: flex; flex-direction: column; text-align: left;
}
details.more .menu a, details.more .menu button, details.more .menu form > button {
display: block; width: 100%; padding: 9px 12px; border: 0; background: none; font: inherit; font-size: 13px; color: var(--text);
text-decoration: none; text-align: left; border-radius: 5px; cursor: pointer; white-space: nowrap;
}
details.more .menu a:hover, details.more .menu button:hover { background: var(--surface-3); }
details.more .menu .sep { height: 1px; background: var(--line); margin: 4px 0; }
details.more .menu .del { color: var(--fail-text); }
details.more .menu [disabled] { color: var(--muted); cursor: not-allowed; }
details.more .menu form { margin: 0; }
@media (max-width: 600px) { details.more.lft .menu { left: 0; right: auto; } }
.field { height: 36px; box-sizing: border-box; border: 1px solid var(--control); border-radius: 8px; padding: 0 12px; font: inherit; font-size: 14px; background: var(--surface); color: var(--text); }
.notice { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; border-radius: 8px; background: var(--warn-soft); color: var(--warn-text); font-size: 13px; line-height: 1.5; }
.notice.info { background: var(--neutral-soft); color: var(--neutral-text); }
.notice.bad { background: var(--fail-soft); color: var(--fail-text); }
.errbox { padding: 10px 12px; border-radius: 8px; background: var(--fail-soft); color: var(--fail-text); font-size: 13px; line-height: 1.5; }
.errbox pre { margin: 8px 0 0; font-size: 12px; white-space: pre-wrap; color: var(--text-2); }
.track { display: flex; gap: 3px; margin-top: 8px; }
.track i { flex: 1; height: 6px; border-radius: 2px; background: var(--track-empty); }
.track i.d { background: var(--track-done); }
.track i.fail { background: var(--fail); } .track i.act { background: var(--act-track); }
.track i.run { background: var(--run); } .track i.ok { background: var(--ok); }
/* Expandable list rows: one line, details below, actions outside <summary>. */
.ui-list { container-type: inline-size; }
.ui-row { position: relative; border-top: 1px solid var(--line); }
.ui-row > .act { position: absolute; right: 16px; top: 7px; display: flex; gap: 6px; align-items: center; }
.ui-rowhead { font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.hidden-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; padding: 10px 24px; border-top: 1px solid var(--line); font-size: 13px; color: var(--text-2); background: var(--surface-2); }
.panel > .hidden-bar:last-child { border-radius: 0 0 14px 14px; }
/* Flash messages restyled to the token palette (Bootstrap markup kept). */
.ui-main > .alert { border-radius: 10px; border: 0; box-shadow: 0 1px 2px var(--shadow); }
/* --------------------------------------------------------------------------
Imaging floor rows (_imaging_tiles.html), used by /imaging and Overview.
-------------------------------------------------------------------------- */
.floor { container-type: inline-size; }
.frow, .fhead {
display: grid; grid-template-columns: 118px minmax(160px, 270px) minmax(0, 1fr);
grid-template-areas: "st pc stage"; align-items: center; gap: 0 20px; padding: 14px 230px 14px 24px;
}
.fhead { padding-top: 6px; padding-bottom: 6px; font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.g-st { grid-area: st; } .g-pc { grid-area: pc; min-width: 0; display: flex; flex-direction: column; } .g-stage { grid-area: stage; min-width: 0; }
.floor-row .g-act { position: absolute; right: 20px; top: 16px; width: 190px; justify-content: flex-end; }
.floor-row .serial { font-size: 15px; }
.stageline { display: flex; align-items: center; gap: 12px; font-size: 13px; min-height: 24px; }
.stageline .txt { flex: 1 1 auto; min-width: 0; white-space: normal; }
.stageline .warnline { font-weight: 600; }
.stageline .kind { flex: none; font-size: 12px; color: var(--muted); max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stageline .step { flex: none; color: var(--muted); font-size: 12px; }
.fbody { display: flex; gap: 24px; align-items: flex-start; padding: 0 24px 16px 162px; font-size: 13px; }
.fbody-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.fbody-qr { flex: none; line-height: 0; background: #FFFFFF; padding: 6px; border-radius: 6px; }
.fmeta { display: flex; flex-wrap: wrap; gap: 4px 16px; }
.fline { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.imaging-log-pre { max-height: 14rem; overflow-y: auto; margin: 6px 0 0; padding: 8px 10px; background: var(--surface-2); border: 1px solid var(--line); border-radius: 6px; font-size: 12px; white-space: pre-wrap; }
.floor-logs { border-top: 1px solid var(--line); }
.floor-logs > summary { padding: 0 24px; min-height: 44px; color: var(--text-2); font-weight: 400; }
.floor-empty { padding: 28px 24px; text-align: center; border-top: 1px solid var(--line); }
@container (max-width: 820px) {
.fhead { display: none; }
.frow { grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "st pc" "stage stage"; gap: 10px 14px; padding: 14px 18px; }
.floor-row .g-act { position: static; width: auto; display: flex; justify-content: flex-end; padding: 0 18px 12px; }
.fbody { padding: 0 18px 14px; flex-direction: column; }
}
/* Overview image cards */
.img-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr)); gap: 16px; }
.img-card { padding: 16px 18px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.img-top { display: flex; align-items: center; gap: 8px; }
.img-name { font-weight: 600; font-size: 16px; color: var(--text); text-decoration: none; margin-right: auto; }
.img-name:hover { color: var(--accent); }
/* The legacy .mono rule sets 13px; inside headings mono text keeps the heading size. */
h1 .mono, h2 .mono, .ui-head h1 .mono { font-size: inherit; }