Site branch tooling, branch-aware publication, dashboard fixes

Publication now mirrors the branch and writes one bundle per branch. Dashboard:
guests see what they can reach rather than a blank page, and the estate total
lists every type it counts.
This commit is contained in:
2026-08-30 11:19:00 -04:00
parent 870e068c44
commit 43fed63277
4 changed files with 139 additions and 16 deletions

View File

@@ -16,19 +16,27 @@
an exception and DashboardCards has no business declaring one. -->
<DashboardCards>
<template #rail>
<section class="dc-panel estate">
<!-- Only when the totals actually loaded. /api/dashboard/summary
needs assets.view, and a caller without it got an empty object
that rendered as a tidy column of zeros - an estate of nothing,
stated with total confidence. Same fault as reporting Low
Supplies 0 while the monitoring is down. -->
<section v-if="summaryloaded" class="dc-panel estate">
<header class="estate-head">
<h3>Estate</h3>
<router-link to="/reports" class="estate-link">Reports</router-link>
</header>
<!-- One line per asset type, then the total under a rule.
These used to be seven equal tiles in which four of them summed
to a fifth: machines + PCs + printers + network IS all assets,
shown beside its own addends at the same weight. A total that
looks like a sixth category invites people to add it in again.
Two of the seven also counted STATUS rather than type without
saying so. -->
These used to be seven equal tiles in which some of them summed
to another: the type counts ARE all assets, shown beside their
own total at the same weight. A total that looks like another
category invites people to add it in again. Two of the seven
also counted STATUS rather than type without saying so.
EVERY counted type must appear here. The endpoint's grand total
is machines + PCs + network + printers + measuring tools, and
omitting one leaves a total nothing on screen explains. -->
<div class="estate-row">
<router-link to="/machines" class="estate-k">Machines</router-link>
<span class="estate-v">{{ stats.totalmachines || 0 }}</span>
@@ -45,6 +53,16 @@
<router-link to="/network" class="estate-k">Network devices</router-link>
<span class="estate-v">{{ stats.totalnetwork || 0 }}</span>
</div>
<!-- Measuring tools are IN the grand total the endpoint returns, so
they have to be visible here. Left out, the total silently
stopped matching the rows above it - 610 of rows over a stated
729 on an instance with gages - which is the exact fault this
panel was built to remove. Dev has none, so it looked correct
here and was wrong everywhere the import had run. -->
<div class="estate-row">
<router-link to="/measuringtools" class="estate-k">Measuring tools</router-link>
<span class="estate-v">{{ stats.totalmeasuringtool || 0 }}</span>
</div>
<div class="estate-row estate-total">
<span class="estate-k">All assets</span>
@@ -62,6 +80,23 @@
</span>
</p>
</section>
<!-- Never leave the page blank, and do not apologise either: show
what this reader CAN open. A guest who can see none of the cards
and cannot read the totals previously got a heading and nothing
else, which reads as a broken site rather than a closed door.
The list is the same permission-filtered navigation the sidebar
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
plugin this page had to know about. -->
<section v-else-if="openable.length" class="dc-panel estate">
<header class="estate-head">
<h3>Where you can go</h3>
</header>
<div v-for="item in openable" :key="item.route" class="estate-row">
<router-link :to="item.route" class="estate-k">{{ item.name }}</router-link>
</div>
</section>
</template>
</DashboardCards>
</template>
@@ -84,21 +119,46 @@
// needed a person put together. A filtered "recently changed" view belongs in
// Reports.
import DashboardCards from '@/components/DashboardCards.vue'
import { ref, onMounted } from 'vue'
import { ref, computed, onMounted } from 'vue'
import { dashboardApi } from '../api'
const loading = ref(true)
const stats = ref({})
// Whether the totals actually came back. Swallowing the error and rendering
// stats as {} turned a 403 into a column of confident zeros.
const summaryloaded = ref(false)
// Where this reader can actually go, for the case where the board has nothing
// for them. Same source as the sidebar, so it is already filtered to what they
// hold - and to what the guest role reaches, when nobody is signed in.
const navitems = ref([])
const openable = computed(
// The dashboard itself is not somewhere to go from the dashboard.
() => navitems.value.filter((item) => item.route && item.route !== '/'))
onMounted(async () => {
try {
const response = await dashboardApi.summary().catch(() => ({ data: { data: {} } }))
const response = await dashboardApi.summary()
stats.value = response.data.data || {}
summaryloaded.value = Object.keys(stats.value).length > 0
} catch (error) {
console.error('Dashboard load error:', error)
} finally {
loading.value = false
// A caller without assets.view is restricted, not broken, so this is not
// logged as an error - the panel offers them somewhere to go instead.
summaryloaded.value = false
}
if (!summaryloaded.value) {
// Only fetched when it is needed, so the common case costs nothing.
try {
const response = await dashboardApi.navigation()
navitems.value = response.data.data || []
} catch (error) {
navitems.value = []
}
}
loading.value = false
})
</script>