Shadow task fixes, asset link ids, backup verdict

This commit is contained in:
2026-08-13 13:20:24 -04:00
parent 6e0d8aec4e
commit 376b8df0fb
10 changed files with 1036 additions and 150 deletions

View File

@@ -61,23 +61,33 @@
<!-- What this PC IS or DRIVES, whichever applies. A part-marker
or bay PC shows its machine; a measuring-tool PC its tool;
a display its role. -->
<router-link v-if="report.machineassetid" class="asset-chip"
:to="`/machines/${report.machineassetid}`"
<!-- /machines/:id and /measuringtools/:id key on the PLUGIN
extension id, NOT the core assetid. Linking with the assetid
lands on whichever record happens to share that number - a
wrong page that looks right. No plugin id means the plugin
is not installed or the asset has no extension row, so show
the number as plain text rather than a link that misleads. -->
<router-link v-if="report.machinepluginid" class="asset-chip"
:to="`/machines/${report.machinepluginid}`"
title="Machine this PC controls">
{{ report.machinenumber }}
</router-link>
<router-link v-if="report.toolassetid" class="asset-chip"
:to="`/measuringtools/${report.toolassetid}`"
<span v-else-if="report.machinenumber" class="asset-chip muted"
title="Machine this PC controls">{{ report.machinenumber }}</span>
<router-link v-if="report.toolpluginid" class="asset-chip"
:to="`/measuringtools/${report.toolpluginid}`"
title="Measuring tool this PC controls">
{{ report.toolassetnumber }}
</router-link>
<span v-else-if="report.toolassetnumber" class="asset-chip muted"
title="Measuring tool this PC controls">{{ report.toolassetnumber }}</span>
<span v-if="report.displayrole" class="asset-chip muted"
title="Display role">{{ report.displayrole }}</span>
<!-- Deliberately NOT the PC's own asset number: the collector
stores a PC's hostname AS its assetnumber, so echoing it
here would just repeat the Host column. Blank means this PC
drives nothing and is not a display. -->
<span v-if="!report.machineassetid && !report.toolassetid && !report.displayrole"
<span v-if="!report.machinenumber && !report.toolassetnumber && !report.displayrole"
class="muted">-</span>
<div v-if="report.location" class="asset-location muted">{{ report.location }}</div>
</td>
@@ -99,15 +109,19 @@
<td>{{ report.installed }}</td>
<td class="muted">{{ report.skipped }}</td>
<td :class="{ 'fail-count': report.failed }">{{ report.failed }}</td>
<!-- Last CONFIRMED, not last changed: an unchanged config writes
no revision, so a stable machine is healthy with an old
revision. What matters is whether the backup still runs. -->
<td class="muted">
<template v-if="report.backuplastseen">
{{ formatDate(report.backuplastseen) }}
<div class="backup-kind">{{ report.backupkind }}</div>
</template>
<span v-else>-</span>
<!-- A VERDICT, not a date. An unchanged config writes no new
revision, so the timestamp is the last time the collector
CHECKED and found the backup intact - which read as neglect
when shown raw, because the healthy steady state is a
day-old confirmation. The badge names the kind and colours
it good/stale; the date moves to the tooltip. Any backup
kind added later inherits this with no change here. -->
<td>
<span v-if="report.backupkind"
class="badge"
:class="backupClass(report)"
:title="backupTitle(report)">{{ report.backupkind }}</span>
<span v-else class="muted">-</span>
</td>
<td class="muted">{{ formatDate(report.lastcheckin || report.receivedat) }}</td>
<td class="actions">
@@ -191,6 +205,27 @@ function statusClass(status) {
return { ok: 'badge-success', selfhealed: 'badge-info', failed: 'badge-danger' }[status] || ''
}
// null verdict = nothing to judge (no revision, or the check is disabled).
// Deliberately NOT green: "never seen" must not read as healthy.
function backupClass(report) {
if (report.backupok === true) return 'badge-success'
if (report.backupok === false) return 'badge-danger'
return ''
}
function backupTitle(report) {
const seen = report.backuplastseen ? formatDate(report.backuplastseen) : 'never'
if (report.backupok === true) {
return `${report.backupkind}: checked and verified ${seen}.`
+ ' Config unchanged since, which is why the date does not move.'
}
if (report.backupok === false) {
return `${report.backupkind}: last confirmed ${seen}, more than`
+ ` ${report.backupstaleafterdays} day(s) ago - the backup has stopped running.`
}
return `${report.backupkind}: last confirmed ${seen}.`
}
function staleTitle(report) {
const received = report.receivedat ? formatDate(report.receivedat) : 'never'
const checkin = report.lastcheckin ? formatDate(report.lastcheckin) : 'not reported'