Undocumented apps: per-device filter and wider device column; presence for Skip USMT

- Source Devices in the undocumented-apps list filters by device name, like
  the software columns; Devices and the action column take less room.
- Drop the one-click documented pick (Documented + Only ticked does it).
- Stage legend underline spans label and number.
- Device details title stays on one line; the name truncates instead.
- Presence sweep also pings devices under Skip USMT tags.
This commit is contained in:
Johan Sandoval committed 2026-10-06 20:49:58 +00:00
1 parent f72e39892c
commit 9c21567f6d
7 files changed
+31 -35

No files matched your search

+1 -15
View File
@@ -22,7 +22,7 @@
const cardsQuery = window.matchMedia('(max-width: 1100px)');
const sheetQuery = window.matchMedia('(max-width: 600px)');
const LIST_COLUMNS = /^(tags|missing software|specialty software)$/i;
const LIST_COLUMNS = /^(tags|missing software|specialty software|source devices)$/i;
const SKIP_COLUMNS = /(^|\s)apps$/i; // "Apps" button columns have nothing to filter
const BLANK = '(Blanks)';
const NO_READING = '(No reading)'; // space column: offline / cached / not checked
@@ -596,20 +596,6 @@
};
pick('Documented', isDocumented);
pick('Undocumented', v => !isDocumented(v));
// One click: devices whose apps ALL have an article ("easy" devices)
if (setMode) {
const b = el('button', 'xf-pick xf-pick-main'); b.type = 'button';
b.textContent = 'Devices with only documented apps';
b.title = 'Show devices where every app in this column has a documentation article (and devices with none)';
b.onclick = () => {
search.value = '';
opts.forEach(o => { o.row.hidden = false; o.cb.checked = o.v === 'None' || (o.v !== BLANK && isDocumented(o.v)); });
setMode('subset');
sync();
ok.click();
};
pickBar.appendChild(b);
}
}
search.oninput = () => {
const q = search.value.trim().toLowerCase();
+7 -6
View File
@@ -476,16 +476,17 @@ a.nav-brand:hover h2 { color: #d6eaf8; }
a.nav-brand:focus-visible { outline: 2px solid #3498db; outline-offset: 3px; }
/* Device details title: name on the left, Simple / Advanced View toggle on the right, one line */
#detailsTitle { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding-right: 10px; min-width: 0; }
#detailsTitle .dt-name { min-width: 0; overflow-wrap: anywhere; }
#detailsTitle { display: flex; align-items: center; gap: 12px; flex-wrap: nowrap; padding-right: 10px; min-width: 0; }
/* the name gives way (ellipsis) so the toggle never drops to a second line */
#detailsTitle .dt-name { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#detailsTitle .dt-name .copy-row-btn { vertical-align: middle; }
#detailsTitle .dt-toggle { flex: 0 0 auto; }
#detailsTitle .dt-toggle {
margin-left: auto; cursor: pointer; padding: 6px 12px; border-radius: 4px; background: #34495e; color: #fff;
border: none; font-size: 0.55em; font-weight: bold; white-space: nowrap; transition: background 0.2s;
}
#detailsTitle .dt-toggle:hover { background: #3498db; }
@media (max-width: 700px) { #detailsTitle { padding-right: 32px; } #detailsTitle .dt-toggle { margin-left: 0; } }
@media (max-width: 700px) { #detailsTitle { font-size: 1.15em; gap: 8px; } }
/* Excel filter: three list modes, and the one-click "only documented" pick */
/* Excel filter: three list modes */
.xf-seg3 .xf-seg-btn { font-size: 0.78em; padding-left: 4px; padding-right: 4px; }
.xf-pick.xf-pick-main { flex-basis: 100%; background: #27ae60; border-color: #27ae60; color: #fff; font-weight: 600; }
.xf-pick.xf-pick-main:hover { background: #219150; border-color: #219150; }
+5 -1
View File
@@ -24,7 +24,8 @@
.ov-tile.ov-selected { border: 2px solid #3498db !important; background: rgba(52,152,219,0.12); box-shadow: 0 0 0 3px rgba(52,152,219,0.25); transform: none; position: relative; }
.ov-showing { display: inline-block; margin-top: 6px; font-size: 0.72em; font-weight: 700; color: #fff; background: #3498db; border-radius: 999px; padding: 2px 8px; }
.ov-bar-row.ov-selected { background: rgba(52,152,219,0.16); box-shadow: inset 3px 0 0 #3498db; }
.ov-legend-item.ov-selected { color: var(--text-main); text-decoration: underline; text-decoration-color: #3498db; text-decoration-thickness: 2px; text-underline-offset: 4px; }
.ov-legend-item.ov-selected { color: var(--text-main); box-shadow: inset 0 -2px 0 #3498db; }
.ov-legend-item.ov-selected .ov-key { box-shadow: none; }
.ov-heat-cell.ov-selected, .ov-heat-sum.ov-selected { box-shadow: 0 0 0 2px var(--bg-card), 0 0 0 5px #3498db !important; }
.ov-heat-sum.ov-selected { background: rgba(52,152,219,0.16); }
@@ -160,3 +161,6 @@ table.ov-heat td { padding: 0; text-align: center; border: 0; background: none;
.ov-bar-row { grid-template-columns: 128px 1fr 32px; }
.ov-bar-label { font-size: 0.82em; }
}
/* Undocumented apps list: room goes to the device names */
#ov-table td.ov-src-list { white-space: normal; overflow-wrap: anywhere; }
+5 -2
View File
@@ -604,14 +604,17 @@
const table = el('table', 'master-table ov-table');
table.id = 'ov-table';
const thead = el('thead'), tr = el('tr');
['App', 'Devices', 'Sources with it', ''].forEach(h => tr.appendChild(el('th', null, h)));
// Source Devices is a list column: its filter lists every device name, like the software columns
[['App', '26%'], ['Devices', '90px'], ['Source Devices', ''], ['', '130px']].forEach(([h, w]) => { const th = el('th', null, h); if (w) th.style.width = w; tr.appendChild(th); });
thead.appendChild(tr); table.appendChild(thead);
const tb = el('tbody');
items.forEach(({ app, devs }) => {
const row = el('tr');
const a = el('td'); a.appendChild(el('b', null, app)); row.appendChild(a);
row.appendChild(el('td', null, String(devs.length)));
const d = el('td', null, devs.slice(0, 6).join(', ') + (devs.length > 6 ? ` +${devs.length - 6} more` : '')); d.title = devs.join(', ');
const d = el('td', 'ov-src-list', devs.slice(0, 8).join(', ') + (devs.length > 8 ? ` +${devs.length - 8} more` : ''));
d.title = devs.join(', ');
d.setAttribute('data-val', devs.join(', ')); // the filter and copy see every name
row.appendChild(d);
const act = el('td', 'no-copy');
const btn = el('button', 'ov-btn', 'Write article'); btn.type = 'button';