mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-24 12:13:28 +00:00
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:
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user