ledger: collapse "Collection" column into the system subtitle

The ledger had two columns that told the same story twice: "Coverage"
(the Monitor* surfaces) and "Collection" ("API" / "Agent" / "Docker" /
"Kubernetes"). The Collection column repeated information the System
column's subtitle already carries (the product label — "Proxmox VE",
"Pulse Unified Agent", etc.) in a less informative form: "API" doesn't
tell a new user anything the word "Proxmox VE" below the row name
doesn't already imply.

Drop the Collection column and rewrite the subtitle to speak the
explainer's vocabulary directly. Platform-API-backed rows now render
"Platform API · Proxmox VE" / "Platform API · TrueNAS" / etc. — the
exact two-mode split the explainer just taught at the top of the page.
Agent / Docker / Kubernetes rows keep their product-name subtitle
("Pulse Unified Agent" / "Docker" / "Kubernetes"); the product name
already carries the source.

Table widths rebalanced (Coverage now gets the space Collection used).
colSpan for error rows drops 6→5 / 5→4. Test fixtures drop the
collectionLabel field; the "renders one row per monitored system" case
asserts the new "Platform API · TrueNAS" subtitle string.
This commit is contained in:
rcourtman
2026-04-20 10:39:57 +01:00
parent c9974e006f
commit 06a602c5ef
5 changed files with 22 additions and 38 deletions
@@ -48,7 +48,7 @@ export const ConnectionsTable: Component<ConnectionsTableProps> = (props) => {
const hasActions = () =>
Boolean(props.actions) || Boolean(props.onEdit);
const colSpan = () => (hasActions() ? 6 : 5);
const colSpan = () => (hasActions() ? 5 : 4);
return (
<Card padding="none" tone="card" class="rounded-md">
@@ -90,13 +90,10 @@ export const ConnectionsTable: Component<ConnectionsTableProps> = (props) => {
<TableHead class="w-[26%] py-2 pl-4 pr-3 text-left text-xs font-semibold uppercase tracking-wide text-muted whitespace-nowrap 2xl:w-[18%]">
System
</TableHead>
<TableHead class="w-[28%] px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-muted whitespace-nowrap 2xl:w-[26%]">
<TableHead class="w-[34%] px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-muted whitespace-nowrap 2xl:w-[32%]">
Coverage
</TableHead>
<TableHead class="hidden w-[12%] px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-muted whitespace-nowrap 2xl:table-cell">
Collection
</TableHead>
<TableHead class="w-[14%] px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-muted whitespace-nowrap 2xl:w-[10%]">
<TableHead class="w-[14%] px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-muted whitespace-nowrap 2xl:w-[12%]">
Status
</TableHead>
<TableHead class="hidden w-[12%] px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-muted whitespace-nowrap 2xl:table-cell">
@@ -135,7 +132,6 @@ export const ConnectionsTable: Component<ConnectionsTableProps> = (props) => {
{row.lastErrorMessage}
</div>
</Show>
<div class="text-xs text-muted 2xl:hidden">{row.collectionLabel}</div>
</div>
</TableCell>
@@ -151,10 +147,6 @@ export const ConnectionsTable: Component<ConnectionsTableProps> = (props) => {
</div>
</TableCell>
<TableCell class="hidden px-3 py-3 align-top whitespace-nowrap text-base-content 2xl:table-cell">
{row.collectionLabel}
</TableCell>
<TableCell class="px-3 py-3 align-top">
<div class="space-y-1">
<span
@@ -30,7 +30,6 @@ const row = (overrides: Partial<InfrastructureSystemRow> = {}): InfrastructureSy
subtitle: undefined,
host: '10.0.0.1',
coverageLabels: ['Host telemetry'],
collectionLabel: 'Agent',
statusLabel: 'online',
statusClassName: 'bg-green-100 text-green-800',
lastActivityText: '5s ago',
@@ -65,7 +64,7 @@ describe('ConnectionsTable', () => {
expect(screen.queryByRole('table')).toBeNull();
});
it('renders one row per monitored system with coverage, collection, and status labels', () => {
it('renders one row per monitored system with coverage and status labels', () => {
render(() => (
<ConnectionsTable
rows={() => [
@@ -73,10 +72,9 @@ describe('ConnectionsTable', () => {
row({
id: 'truenas:nas',
name: 'nas',
subtitle: 'TrueNAS',
subtitle: 'Platform API · TrueNAS',
host: undefined,
coverageLabels: ['Datasets'],
collectionLabel: 'API',
statusLabel: 'Paused',
connection: connectionFixture({ id: 'truenas:nas', type: 'truenas', name: 'nas' }),
}),
@@ -87,7 +85,7 @@ describe('ConnectionsTable', () => {
expect(screen.getByRole('table')).toBeInTheDocument();
expect(screen.getByText('tower')).toBeInTheDocument();
expect(screen.getByText('nas')).toBeInTheDocument();
expect(screen.getByText('TrueNAS')).toBeInTheDocument();
expect(screen.getByText('Platform API · TrueNAS')).toBeInTheDocument();
expect(screen.getByText('Datasets')).toBeInTheDocument();
expect(screen.getByText('Paused')).toBeInTheDocument();
expect(screen.getByText('online')).toBeInTheDocument();
@@ -48,7 +48,6 @@ vi.mock('../useConnectionsLedger', () => ({
subtitle: connection.type,
host: connection.address,
coverageLabels: ['VMs'],
collectionLabel: connection.type === 'agent' ? 'Agent' : 'API',
statusLabel: connection.state === 'paused' ? 'Paused' : 'Active',
statusClassName: '',
lastActivityText: '1m ago',
@@ -6,7 +6,6 @@ export interface InfrastructureSystemRow {
subtitle?: string;
host?: string;
coverageLabels: string[];
collectionLabel: string;
statusLabel: string;
statusClassName: string;
lastActivityText: string;
@@ -70,24 +70,18 @@ const SURFACE_LABELS: Record<string, string> = {
export const surfaceLabel = (key: string): string => SURFACE_LABELS[key] ?? key;
const collectionLabelFor = (type: ConnectionType): string => {
switch (type) {
case 'pve':
case 'pbs':
case 'pmg':
case 'vmware':
case 'truenas':
return 'API';
case 'agent':
return 'Agent';
case 'docker':
return 'Docker';
case 'kubernetes':
return 'Kubernetes';
default:
return 'Runtime';
}
};
// The ledger subtitle speaks the explainer's vocabulary so the user reads the
// same "Platform API vs Pulse Unified Agent" split here that the explainer
// card above just taught them. API-backed types get a "Platform API · {product}"
// prefix; agent/docker/k8s rows use the product label alone (the product name
// already carries the source — "Pulse Unified Agent" / "Docker" / "Kubernetes").
const PLATFORM_API_TYPES: ReadonlySet<ConnectionType> = new Set([
'pve',
'pbs',
'pmg',
'vmware',
'truenas',
]);
const lastActivityText = (connection: Connection): string => {
if (!connection.lastSeen) return 'No activity yet';
@@ -106,7 +100,10 @@ const lastActivityText = (connection: Connection): string => {
const subtitleFor = (connection: Connection): string | undefined => {
if (connection.stateReason) return connection.stateReason;
return CONNECTION_TYPE_LABELS[connection.type] ?? connection.type;
const productLabel = CONNECTION_TYPE_LABELS[connection.type] ?? connection.type;
return PLATFORM_API_TYPES.has(connection.type)
? `Platform API · ${productLabel}`
: productLabel;
};
const EDITABLE_CONNECTION_TYPES: readonly ConnectionType[] = [
@@ -135,7 +132,6 @@ export const connectionToRow = (connection: Connection): InfrastructureSystemRow
subtitle: subtitleFor(connection),
host,
coverageLabels: coverage,
collectionLabel: collectionLabelFor(connection.type),
statusLabel: presentation.label,
statusClassName: presentation.badgeClass,
lastActivityText: lastActivityText(connection),