dashboard: numbers that agree, a map on hover, wider cards
Some checks failed
CI / backend (push) Failing after 7s
CI / naming (push) Successful in 2s
CI / frontend (push) Successful in 9s
CI / migrations-mysql (push) Failing after 7s

"All assets 704" sat beside "all assets in use 737", and both were correct
about different populations. The totals summed five specific asset types and
subtracted dual-bay secondaries; the status counts took every asset row of any
type with no collapse, so USB devices and hidden secondary bays inflated one
side of a comparison the layout invites. Status is now counted over exactly the
same assets the totals describe.

Warranty rows fell back to asset.name when the covered asset had no hostname,
and an asset's name is usually the MACHINE's descriptive name - which is how a
column meant to identify a PC ended up showing a machine. Hostname, else the
asset number, never the name. The machine number loses its label too: the row
is hostname, machine, state, and "machine 3015" spends a word on what position
already conveys.

Printer names now carry the floor-plan preview on hover, the same
LocationMapTooltip the printer's own page uses - a location name tells you the
room, the map tells you where to walk. Declared as map.maphover on the card, so
any card with coordinates gets it; a row without them shows a plain link rather
than being dropped.

Cards are four across rather than five. At five columns a row holding a
hostname, a machine number and a state truncates on exactly the rows that
matter. auto-fit, so two cards fill the width instead of leaving empty tracks.

Not covered by a test: the count fix. I started one and it was interrupted, and
I have not gone back for it - the assertion worth having is that in-use can
never exceed the total.
This commit is contained in:
cproudlock
2026-08-11 16:27:14 -04:00
parent 221bbb226e
commit 94d8d6c9b6
7 changed files with 85 additions and 13 deletions

View File

@@ -19,7 +19,17 @@
<ul v-else class="dc-rows">
<li v-for="(row, index) in visibleRows(card)" :key="index" class="dc-row"
:class="{ 'dc-row-stacked': card.layout === 'stacked' }">
<router-link v-if="row.link" :to="row.link" class="dc-row-title"
<!-- With coordinates, the name carries the same floor-plan preview
the asset's own page uses. Without them, a plain link. -->
<LocationMapTooltip v-if="row.link && row.maphover"
:left="row.maphover.x" :top="row.maphover.y"
:machineName="row.maphover.label">
<router-link :to="row.link" class="dc-row-title"
:title="row.titletip || undefined">
{{ row.title }}
</router-link>
</LocationMapTooltip>
<router-link v-else-if="row.link" :to="row.link" class="dc-row-title"
:title="row.titletip || undefined">
{{ row.title }}
</router-link>
@@ -64,6 +74,7 @@
import { ref, computed, onMounted } from 'vue'
import api from '../api'
import { useAuthStore } from '@/stores/auth'
import LocationMapTooltip from '@/components/LocationMapTooltip.vue'
import {
toApiPath, visibleRows, overflowCount, metricValue, cardVisible, sortCards,
permittedCards, renderableCards, rows as cardData,
@@ -109,7 +120,12 @@ defineExpose({ load })
<style scoped>
.dc-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
/* Four across on a wide screen, not five. A card holds a hostname, a machine
number and a state on one line; at five columns those lines truncate on
the very rows that matter most. auto-fit rather than auto-fill so two
cards fill the width instead of leaving three empty tracks. */
grid-template-columns: repeat(auto-fit, minmax(28rem, 1fr));
max-width: 120rem;
gap: 1rem;
margin-bottom: 1.75rem;
align-items: start;

View File

@@ -118,10 +118,23 @@ export function mapTitleTip(card, item) {
return key ? (item[key] || '') : ''
}
// Floor-plan preview on hover, for rows that carry coordinates. Returns null
// when the card declares none or the row has not been placed on the map - an
// unplaced printer still belongs on the card, it just has nothing to preview.
export function mapHover(card, item) {
const spec = card.map && card.map.maphover
if (!spec) return null
const x = item[spec.x]
const y = item[spec.y]
if (x === null || x === undefined || y === null || y === undefined) return null
return { x, y, label: spec.label ? (item[spec.label] || '') : '' }
}
export function cardRows(card) {
return rows(card).map((item) => ({
title: mapTitle(card, item),
titletip: mapTitleTip(card, item),
maphover: mapHover(card, item),
detail: mapDetail(card, item),
detailtip: mapDetailTip(card, item),
meta: mapMeta(card, item),