Skip to content

Commit a7adcfc

Browse files
bootcclaude
andcommitted
Add clickable unreported nodes box on dashboard
Links the unreported count box to the node overview page filtered to show unreported nodes. Since 'unreported' is not a PuppetDB status but is calculated from report timestamps, the node overview page now handles it as a special client-side filter using the unreported hours threshold from the server metadata. Also fixes a pre-existing bug where route query status params were cast as string[] without normalisation, causing a TypeError when a single status value was passed as a string. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Chris Boot <crb@tiger-computing.co.uk>
1 parent 2a1e459 commit a7adcfc

File tree

2 files changed

+34
-4
lines changed

2 files changed

+34
-4
lines changed

ui/src/pages/DashboardPage.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ onMounted(() => {
174174
suffix="nodes"
175175
:caption="$t('LABEL_UNREPORTED', { dur: unreportedDuration?.humanize() })"
176176
title_color="secondary"
177+
:to="{ name: 'NodeOverview', query: { status: 'unreported' } }"
177178
/>
178179
<DashboardItem
179180
v-model="population"

ui/src/pages/node/NodeOverviewPage.vue

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useSettingsStore } from 'stores/settings';
55
import { PuppetNodeWithEventCount } from 'src/puppet/models/puppet-node';
66
import NodeTable from 'components/NodeTable.vue';
77
import { useRoute, useRouter } from 'vue-router';
8+
import moment from 'moment';
89
910
const route = useRoute();
1011
const router = useRouter();
@@ -13,13 +14,26 @@ const nodes = ref<PuppetNodeWithEventCount[]>([]);
1314
const settings = useSettingsStore();
1415
const isLoading = ref(false);
1516
const statusFilter = ref<string[]>();
16-
const statusOptions = ['failed', 'changed', 'unchanged', 'pending'];
17+
const statusOptions = ['failed', 'changed', 'unchanged', 'pending', 'unreported'];
18+
const unreportedDate = ref<moment.Moment>();
19+
20+
function loadMeta() {
21+
void Backend.getMeta().then((result) => {
22+
if (result.status === 200 && result.data.Data.UnreportedHours) {
23+
unreportedDate.value = moment().subtract(
24+
moment.duration(result.data.Data.UnreportedHours, 'hours'),
25+
);
26+
}
27+
});
28+
}
1729
1830
function loadData() {
1931
if (!settings.environment) return;
2032
const env = settings.hasEnvironment() ? settings.environment : undefined;
33+
const hasUnreported = statusFilter.value?.includes('unreported');
34+
const apiStatuses = hasUnreported ? undefined : statusFilter.value;
2135
isLoading.value = true;
22-
void Backend.getViewNodeOverview(env, statusFilter.value)
36+
void Backend.getViewNodeOverview(env, apiStatuses)
2337
.then((result) => {
2438
if (result.status === 200) {
2539
nodes.value = result.data.Data.map((s) =>
@@ -33,7 +47,19 @@ function loadData() {
3347
}
3448
3549
const filteredNodes = computed(() => {
36-
return nodes.value.filter((s) => s.certname.includes(filter.value));
50+
let result = nodes.value.filter((s) => s.certname.includes(filter.value));
51+
52+
if (statusFilter.value?.includes('unreported')) {
53+
const ud = unreportedDate.value;
54+
const otherStatuses = statusFilter.value.filter((s) => s !== 'unreported');
55+
result = result.filter((s) => {
56+
const isUnreported = !s.report_timestamp || (ud ? ud.isAfter(s.report_timestamp) : false);
57+
if (otherStatuses.length === 0) return isUnreported;
58+
return isUnreported || otherStatuses.includes(s.latest_report_status);
59+
});
60+
}
61+
62+
return result;
3763
});
3864
3965
function updateRoute() {
@@ -60,8 +86,11 @@ watch(settings, () => {
6086
});
6187
6288
onMounted(() => {
89+
loadMeta();
90+
6391
if (route.query.status) {
64-
statusFilter.value = route.query.status as string[];
92+
const s = route.query.status;
93+
statusFilter.value = (Array.isArray(s) ? s : [s]).filter((v): v is string => v !== null);
6594
}
6695
6796
if (route.query.filter) {

0 commit comments

Comments
 (0)