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:
1 parent
f72e39892c
commit
9c21567f6d
7 files changed
+31
-35
No files matched your search
@@ -52,8 +52,9 @@
|
||||
# A task-sequence log modified within this window counts as "active"
|
||||
ActiveLogHours = 24
|
||||
|
||||
# Completed pairs skip network checks in the sweep, so they're pinged this often to keep
|
||||
# "last online" / "days offline" current (every other device is pinged every cycle).
|
||||
# Completed pairs and Skip USMT devices skip network checks in the sweep, so they're
|
||||
# pinged this often to keep "last online" / "days offline" current (every other device
|
||||
# is pinged every cycle).
|
||||
# Cycles only run in business hours plus the midnight pitstop: 60 = hourly, plus midnight.
|
||||
PresenceSweepMinutes = 60
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ a 1,300-device scope.
|
||||
*Not in SCCM*, *No DNS record*, or *Waiting for worker...* if it hasn't been looked up yet.
|
||||
The dialog refreshes in place when the worker finishes a cycle.
|
||||
- **Presence**: every ping is recorded, so each device shows when it was last online and how
|
||||
many days it has been offline. Completed pairs, which skip network checks in the sweep, are
|
||||
pinged hourly during business hours and at midnight to keep theirs current.
|
||||
many days it has been offline. Completed pairs and Skip USMT devices, which skip network checks
|
||||
in the sweep, are pinged hourly during business hours and at midnight to keep theirs current.
|
||||
- **Snapshots**: the last free space and scanstate / loadstate lines seen while a device was
|
||||
reachable are kept with their timestamps, and shown for completed migrations and offline
|
||||
devices instead of blanks.
|
||||
|
||||
+8
-7
@@ -735,27 +735,28 @@ while ($true) {
|
||||
$sweepSeconds = $phaseWatch.Elapsed.TotalSeconds
|
||||
$phaseWatch.Restart()
|
||||
|
||||
# --- PRESENCE SWEEP: completed pairs ---
|
||||
# Every other device is pinged by the sweep itself on every cycle. Completed pairs skip
|
||||
# their network checks, so they're pinged here: hourly during business hours (cycles only
|
||||
# run then) and on the midnight pitstop.
|
||||
# --- PRESENCE SWEEP: completed pairs and Skip USMT devices ---
|
||||
# Every other device is pinged by the sweep itself on every cycle. Completed pairs and
|
||||
# devices under a Skip USMT tag skip network checks, so they're pinged here: hourly during
|
||||
# business hours (cycles only run then) and on the midnight pitstop.
|
||||
$presenceCount = 0
|
||||
$presenceDue = ((Get-Date) - $global:LastPresenceSweep).TotalMinutes -ge ($presenceMinutes - 0.5) -or $runFullSoftwareSweep
|
||||
if ($presenceDue) {
|
||||
$completedDevs = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
|
||||
foreach ($r in $deviceTable) {
|
||||
if ($r.Pairing -ne "Cached") { continue }
|
||||
if ($r.Pairing -ne "Cached" -and $r.Pairing -ne "Skipped") { continue }
|
||||
foreach ($n in @($r."Source Device", $r."Destination Device")) { if ($n -and $n -ne "N/A") { $null = $completedDevs.Add($n) } }
|
||||
}
|
||||
$toPing = @($completedDevs | Where-Object { -not $global:PingedThisCycle.Contains($_) } | Sort-Object)
|
||||
Write-Host "`n[Presence Sweep: pinging $($toPing.Count) device(s) from completed migrations]" -ForegroundColor Cyan
|
||||
Write-Host "`n[Presence Sweep: pinging $($toPing.Count) completed / Skip USMT device(s)]" -ForegroundColor Cyan
|
||||
$i = 0
|
||||
foreach ($n in $toPing) {
|
||||
$i++
|
||||
Update-WorkerState "Presence Sweep..." $i $toPing.Count $globalLastDuration "Calculating..."
|
||||
$null = Test-Device $n -Session $session
|
||||
}
|
||||
# Cached / skipped rows pick up the fresh result
|
||||
# Completed rows pick up the fresh result (Skip USMT rows keep "Skipped" in the ping
|
||||
# column; their presence shows in the device details and Custom View)
|
||||
foreach ($r in $deviceTable) {
|
||||
if ($r.Pairing -eq "Cached") { Set-RowPresence $r -Source -Destination }
|
||||
}
|
||||
|
||||
+1
-15
@@ -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
@@ -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
@@ -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
@@ -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';
|
||||
|
||||
Reference in new issue
Block a user