Call the inventory panel Assets, not Estate
All checks were successful
CI / naming (push) Successful in 3s
CI / frontend (push) Successful in 13s
CI / migrations-mysql (push) Successful in 40s
CI / backend (push) Successful in 4m11s

The panel had no group label before it was one panel, so grouping it needed a
heading and it got 'Estate' - RMM idiom, and the word the leadership documents
already used for the whole of what a site owns.

Wrong register for this surface. The sidebar already groups exactly these things
under a section named assets, so the dashboard had invented a second word for a
group the app already names, and the panel's own total row says 'All assets' -
the heading and the total disagreed inside one panel. 'Estate' is also
enterprise-IT and largely British idiom; it earns its place in an executive
summary and not on a screen read at 6am.

The scoped classes follow the label, so the markup does not keep a word the UI
stopped using. The documents keep 'estate', where it is doing real work for a
leadership reader.

Scope: product
This commit is contained in:
cproudlock
2026-08-30 12:57:05 -04:00
parent 58b53b50f6
commit 4c8291628f

View File

@@ -18,13 +18,13 @@
<template #rail> <template #rail>
<!-- Only when the totals actually loaded. /api/dashboard/summary <!-- Only when the totals actually loaded. /api/dashboard/summary
needs assets.view, and a caller without it got an empty object needs assets.view, and a caller without it got an empty object
that rendered as a tidy column of zeros - an estate of nothing, that rendered as a tidy column of zeros - a site with nothing
stated with total confidence. Same fault as reporting Low in it, stated with total confidence. Same fault as reporting Low
Supplies 0 while the monitoring is down. --> Supplies 0 while the monitoring is down. -->
<section v-if="summaryloaded" class="dc-panel estate"> <section v-if="summaryloaded" class="dc-panel assets">
<header class="estate-head"> <header class="assets-head">
<h3>Estate</h3> <h3>Assets</h3>
<router-link to="/reports" class="estate-link">Reports</router-link> <router-link to="/reports" class="assets-link">Reports</router-link>
</header> </header>
<!-- One line per asset type, then the total under a rule. <!-- One line per asset type, then the total under a rule.
@@ -37,21 +37,21 @@
EVERY counted type must appear here. The endpoint's grand total EVERY counted type must appear here. The endpoint's grand total
is machines + PCs + network + printers + measuring tools, and is machines + PCs + network + printers + measuring tools, and
omitting one leaves a total nothing on screen explains. --> omitting one leaves a total nothing on screen explains. -->
<div class="estate-row"> <div class="assets-row">
<router-link to="/machines" class="estate-k">Machines</router-link> <router-link to="/machines" class="assets-k">Machines</router-link>
<span class="estate-v">{{ stats.totalmachines || 0 }}</span> <span class="assets-v">{{ stats.totalmachines || 0 }}</span>
</div> </div>
<div class="estate-row"> <div class="assets-row">
<router-link to="/pcs" class="estate-k">PCs</router-link> <router-link to="/pcs" class="assets-k">PCs</router-link>
<span class="estate-v">{{ stats.totalpc || 0 }}</span> <span class="assets-v">{{ stats.totalpc || 0 }}</span>
</div> </div>
<div class="estate-row"> <div class="assets-row">
<router-link to="/printers" class="estate-k">Printers</router-link> <router-link to="/printers" class="assets-k">Printers</router-link>
<span class="estate-v">{{ stats.totalprinter || 0 }}</span> <span class="assets-v">{{ stats.totalprinter || 0 }}</span>
</div> </div>
<div class="estate-row"> <div class="assets-row">
<router-link to="/network" class="estate-k">Network devices</router-link> <router-link to="/network" class="assets-k">Network devices</router-link>
<span class="estate-v">{{ stats.totalnetwork || 0 }}</span> <span class="assets-v">{{ stats.totalnetwork || 0 }}</span>
</div> </div>
<!-- Measuring tools are IN the grand total the endpoint returns, so <!-- Measuring tools are IN the grand total the endpoint returns, so
they have to be visible here. Left out, the total silently they have to be visible here. Left out, the total silently
@@ -59,23 +59,23 @@
729 on an instance with gages - which is the exact fault this 729 on an instance with gages - which is the exact fault this
panel was built to remove. Dev has none, so it looked correct panel was built to remove. Dev has none, so it looked correct
here and was wrong everywhere the import had run. --> here and was wrong everywhere the import had run. -->
<div class="estate-row"> <div class="assets-row">
<router-link to="/measuringtools" class="estate-k">Measuring tools</router-link> <router-link to="/measuringtools" class="assets-k">Measuring tools</router-link>
<span class="estate-v">{{ stats.totalmeasuringtool || 0 }}</span> <span class="assets-v">{{ stats.totalmeasuringtool || 0 }}</span>
</div> </div>
<div class="estate-row estate-total"> <div class="assets-row assets-total">
<span class="estate-k">All assets</span> <span class="assets-k">All assets</span>
<span class="estate-v">{{ stats.totalassets || 0 }}</span> <span class="assets-v">{{ stats.totalassets || 0 }}</span>
</div> </div>
<!-- Status, on one line, and explicitly labelled as status. In <!-- Status, on one line, and explicitly labelled as status. In
repair is muted at zero rather than given a card of its own to repair is muted at zero rather than given a card of its own to
announce that nothing is wrong. --> announce that nothing is wrong. -->
<p class="estate-status"> <p class="assets-status">
<span class="estate-inuse">{{ stats.activeassets || 0 }}</span> in use <span class="assets-inuse">{{ stats.activeassets || 0 }}</span> in use
<span aria-hidden="true"> / </span> <span aria-hidden="true"> / </span>
<span :class="{ 'estate-zero': !stats.inrepair }"> <span :class="{ 'assets-zero': !stats.inrepair }">
{{ stats.inrepair || 0 }} in repair {{ stats.inrepair || 0 }} in repair
</span> </span>
</p> </p>
@@ -89,12 +89,12 @@
uses - server-filtered for a signed-out visitor - so it can never uses - server-filtered for a signed-out visitor - so it can never
offer a link that dead-ends at the router guard, and it names no offer a link that dead-ends at the router guard, and it names no
plugin this page had to know about. --> plugin this page had to know about. -->
<section v-else-if="openable.length" class="dc-panel estate"> <section v-else-if="openable.length" class="dc-panel assets">
<header class="estate-head"> <header class="assets-head">
<h3>Where you can go</h3> <h3>Where you can go</h3>
</header> </header>
<div v-for="item in openable" :key="item.route" class="estate-row"> <div v-for="item in openable" :key="item.route" class="assets-row">
<router-link :to="item.route" class="estate-k">{{ item.name }}</router-link> <router-link :to="item.route" class="assets-k">{{ item.name }}</router-link>
</div> </div>
</section> </section>
</template> </template>
@@ -165,7 +165,7 @@ onMounted(async () => {
<style scoped> <style scoped>
/* Matches the panels in DashboardCards; the rail reads as one column rather /* Matches the panels in DashboardCards; the rail reads as one column rather
than as a page card that happens to sit beside them. */ than as a page card that happens to sit beside them. */
.estate { .assets {
background: var(--bg-card); background: var(--bg-card);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 8px;
@@ -173,13 +173,13 @@ onMounted(async () => {
min-width: 0; min-width: 0;
} }
.estate-head { .assets-head {
display: flex; display: flex;
align-items: baseline; align-items: baseline;
gap: 0.5rem; gap: 0.5rem;
margin-bottom: 0.35rem; margin-bottom: 0.35rem;
} }
.estate-head h3 { .assets-head h3 {
margin: 0; margin: 0;
font-size: 0.78rem; font-size: 0.78rem;
font-weight: 700; font-weight: 700;
@@ -188,14 +188,14 @@ onMounted(async () => {
color: var(--text-light); color: var(--text-light);
flex: 1 1 auto; flex: 1 1 auto;
} }
.estate-link { .assets-link {
font-size: 0.78rem; font-size: 0.78rem;
color: var(--link); color: var(--link);
text-decoration: none; text-decoration: none;
} }
.estate-link:hover { text-decoration: underline; } .assets-link:hover { text-decoration: underline; }
.estate-row { .assets-row {
display: flex; display: flex;
align-items: baseline; align-items: baseline;
justify-content: space-between; justify-content: space-between;
@@ -203,32 +203,32 @@ onMounted(async () => {
padding: 0.35rem 0; padding: 0.35rem 0;
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
.estate-row:last-of-type { border-bottom: none; } .assets-row:last-of-type { border-bottom: none; }
.estate-k { .assets-k {
color: var(--link); color: var(--link);
text-decoration: none; text-decoration: none;
font-size: 0.9rem; font-size: 0.9rem;
} }
a.estate-k:hover { text-decoration: underline; } a.assets-k:hover { text-decoration: underline; }
span.estate-k { color: var(--text); font-weight: 600; } span.assets-k { color: var(--text); font-weight: 600; }
.estate-v { .assets-v {
font-weight: 600; font-weight: 600;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
.estate-total { .assets-total {
margin-top: 0.35rem; margin-top: 0.35rem;
border-top: 2px solid var(--border); border-top: 2px solid var(--border);
border-bottom: none; border-bottom: none;
} }
.estate-status { .assets-status {
margin: 0.5rem 0 0; margin: 0.5rem 0 0;
font-size: 0.85rem; font-size: 0.85rem;
color: var(--text-light); color: var(--text-light);
} }
.estate-inuse { color: var(--success); font-weight: 600; font-variant-numeric: tabular-nums; } .assets-inuse { color: var(--success); font-weight: 600; font-variant-numeric: tabular-nums; }
.estate-zero { color: var(--text-light); } .assets-zero { color: var(--text-light); }
</style> </style>