geenforce: reports say what the PC is, where, and whether it is backed up
A report row carried a hostname and some counts. Everything an operator wants next hangs off the asset behind that host, and none of it was there. Host now links to its PC page. Beside it, what the PC IS or DRIVES: the machine number for a bay or part-marker PC, the measuring tool for a tool PC, the role for a display - each linking to that asset's own page. A map pin appears only when the asset has coordinates and hovers to the same floor-plan preview the asset pages use; an icon that opened an empty map would be worse than no icon. The backup column is LAST CONFIRMED, not last changed. Dedup means an unchanged config writes no revision, so a machine stable for six months has a six-month-old newest revision and is perfectly healthy - the one to worry about is the machine whose backup stopped running. It reads lastseenat and names the kind rather than assuming ntlars, since udc/file kinds on the share are coming. Resolution is bulk, never per row: this table shows the whole fleet, so a lookup inside the loop would be one query per PC. It reads the collector's existing 'controls' relationship rather than re-deriving which machine a PC drives - that same resolution living in two places is what put a wrong subtype filter on the map. Every plugin it touches is optional, so each lookup is ImportError-guarded and a lean build renders the table without those columns. A host ShopDB has no asset for still shows: the enforcement result is real even when the inventory is behind.
This commit is contained in:
@@ -34,14 +34,49 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Host</th><th>PC type</th><th>Received</th><th>Version</th>
|
||||
<th>Host</th><th>Asset</th><th>PC type</th><th>Received</th><th>Version</th>
|
||||
<th>Status</th><th>Installed</th><th>Skipped</th><th>Failed</th>
|
||||
<th>Last check-in</th><th></th>
|
||||
<th>Backup</th><th>Last check-in</th><th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="report in reports" :key="report.reportid">
|
||||
<td>{{ report.hostname }}</td>
|
||||
<td class="host-cell">
|
||||
<router-link v-if="report.computerid" :to="`/pcs/${report.computerid}`">
|
||||
{{ report.hostname }}
|
||||
</router-link>
|
||||
<span v-else>{{ report.hostname }}</span>
|
||||
<!-- Coordinates give the same floor-plan preview the asset's own
|
||||
page uses. No coordinates, no icon - an icon that opened an
|
||||
empty map would be worse than none. -->
|
||||
<LocationMapTooltip
|
||||
v-if="report.mapx != null && report.mapy != null"
|
||||
:left="report.mapx" :top="report.mapy"
|
||||
:machineName="report.hostname"
|
||||
>
|
||||
<span class="map-pin" :title="report.location || 'On the floor plan'">◎</span>
|
||||
</LocationMapTooltip>
|
||||
</td>
|
||||
<td class="asset-cell">
|
||||
<!-- 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}`"
|
||||
title="Machine this PC controls">
|
||||
{{ report.machinenumber }}
|
||||
</router-link>
|
||||
<router-link v-if="report.toolassetid" class="asset-chip"
|
||||
:to="`/measuringtools/${report.toolassetid}`"
|
||||
title="Measuring tool this PC controls">
|
||||
{{ report.toolassetnumber }}
|
||||
</router-link>
|
||||
<span v-if="report.displayrole" class="asset-chip muted"
|
||||
title="Display role">{{ report.displayrole }}</span>
|
||||
<span v-if="!report.machineassetid && !report.toolassetid && !report.displayrole"
|
||||
class="muted">{{ report.assetnumber || '-' }}</span>
|
||||
<div v-if="report.location" class="asset-location muted">{{ report.location }}</div>
|
||||
</td>
|
||||
<td>{{ report.scopename }}</td>
|
||||
<td>
|
||||
<span class="badge" :class="report.receivedlatest ? 'badge-success' : 'badge-warning'">
|
||||
@@ -53,12 +88,22 @@
|
||||
<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>
|
||||
</td>
|
||||
<td class="muted">{{ formatDate(report.lastcheckin || report.receivedat) }}</td>
|
||||
<td class="actions">
|
||||
<button class="btn btn-sm btn-secondary" @click="openDetail(report.reportid)">Detail</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="!reports.length"><td colspan="10" class="empty">No reports yet.</td></tr>
|
||||
<tr v-if="!reports.length"><td colspan="12" class="empty">No reports yet.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -105,6 +150,7 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import api from '@/api'
|
||||
import LocationMapTooltip from '@/components/LocationMapTooltip.vue'
|
||||
|
||||
const reports = ref([])
|
||||
const detail = ref(null)
|
||||
@@ -163,4 +209,17 @@ load()
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.modal-close:hover { color: var(--text); }
|
||||
.host-cell { display: flex; align-items: center; gap: 0.35rem; }
|
||||
.map-pin { cursor: pointer; color: var(--primary); font-size: 0.95rem; line-height: 1; }
|
||||
.asset-cell { white-space: nowrap; }
|
||||
.asset-chip {
|
||||
display: inline-block;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.05rem 0.4rem;
|
||||
margin-right: 0.25rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.asset-location { font-size: 0.75rem; margin-top: 0.15rem; }
|
||||
.backup-kind { font-size: 0.72rem; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user