From 81248bb159a4199e245a920bfe6c2bae5851c446 Mon Sep 17 00:00:00 2001 From: mrhid6 Date: Thu, 6 Aug 2026 15:49:39 +0100 Subject: [PATCH] style: bring the vulnerabilities page onto the house page shape Every other page under app/(app) opens with `p-4 sm:p-6 lg:p-8` and the layout adds none of its own, so this page alone sat flush against the shell edge. Its h1 was text-xl where every other page is text-2xl. The findings list was a stack of separately bordered cards; it is now rows inside one Card, separated by border-border-soft, matching the monitors and workflows lists. Loading is the shared spinner rather than a line of text, the error is the shared danger strip, and the empty state uses the same proportions as the monitors one. --- web/app/(app)/vulnerabilities/page.tsx | 89 +++++++++++-------- web/components/vulnerabilities/FindingRow.tsx | 18 ++-- 2 files changed, 63 insertions(+), 44 deletions(-) diff --git a/web/app/(app)/vulnerabilities/page.tsx b/web/app/(app)/vulnerabilities/page.tsx index 6f15535..1ed48d3 100644 --- a/web/app/(app)/vulnerabilities/page.tsx +++ b/web/app/(app)/vulnerabilities/page.tsx @@ -4,7 +4,7 @@ import { useMemo, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { api, vulnerabilities, type FindingState, type Severity, type VulnFinding } from "@/lib/api"; import { useAuth } from "@/components/AuthProvider"; -import { Button } from "@/components/ui"; +import { Button, Card } from "@/components/ui"; import { AcceptDialog } from "@/components/vulnerabilities/AcceptDialog"; import { DBFreshness } from "@/components/vulnerabilities/DBFreshness"; import { FindingRow } from "@/components/vulnerabilities/FindingRow"; @@ -78,12 +78,12 @@ export default function VulnerabilitiesPage() { const total = SEVERITY_ORDER.reduce((n, s) => n + (counts[s] ?? 0), 0); return ( -
-
+
+
-

Vulnerabilities

+

Vulnerabilities

- Installed packages matched against distribution security advisories. + {total} open finding{total !== 1 ? "s" : ""} · installed packages matched against distribution security advisories

{isAdmin && ( @@ -93,32 +93,36 @@ export default function VulnerabilitiesPage() { )}
- +
+ +
-
+ {/* Severity counts double as the filter. They are always the whole + fleet's open counts, never the filtered view's, so switching + state cannot make the fleet look better than it is. */} +
{SEVERITY_ORDER.map((s) => ( ))} - - {total} open -
-
+
{STATES.map((s) => (
- {groups.isLoading &&

Loading…

} - {groups.error &&

{(groups.error as Error).message}

} - - {groups.data && groups.data.length === 0 && ( -
-

No {state} findings.

-

- Servers report packages hourly. A server whose distribution has no advisory feed is reported as unsupported on its own - page rather than counted here. -

+ {groups.error && ( +
+ {(groups.error as Error).message}
)} -
- {groups.data?.map((g) => ( - unaccept.mutate(f.id)} - onApplyUpdates={(serverId) => applyUpdates.mutate(serverId)} - applying={applyUpdates.isPending ? (applyUpdates.variables as string) : undefined} - /> - ))} -
+ + {groups.isLoading ? ( +
+
+
+ ) : groups.data && groups.data.length > 0 ? ( + groups.data.map((g) => ( + unaccept.mutate(f.id)} + onApplyUpdates={(serverId) => applyUpdates.mutate(serverId)} + applying={applyUpdates.isPending ? (applyUpdates.variables as string) : undefined} + /> + )) + ) : ( +
+

+ No {state} findings{severity ? ` at ${severity} severity` : ""}. +

+

+ Servers report their packages hourly. A server whose distribution has no advisory feed is reported as unsupported + on its own page rather than counted as clean here. +

+
+ )} + {accepting && ( f.fixed_in); return ( -
+ // A row inside the page's one bordered container, not a card of its + // own — the same stack idiom as the monitors and workflows lists. +
{open && ( -
+
{group.findings.map((f) => ( -
+
{serverName(f.server_id)}