diff --git a/ui/src/pages/vendor/InfrastructurePage.tsx b/ui/src/pages/vendor/InfrastructurePage.tsx index 957cf6d..db148c2 100644 --- a/ui/src/pages/vendor/InfrastructurePage.tsx +++ b/ui/src/pages/vendor/InfrastructurePage.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; -import { Spinner } from '@cameleer/design-system'; +import { Card, KpiStrip, Spinner } from '@cameleer/design-system'; +import { Database, ChevronDown, ChevronRight } from 'lucide-react'; import { useInfraPostgres, useInfraClickHouse, @@ -29,30 +30,9 @@ function formatNumber(n: number): string { return n.toLocaleString(); } -const kpiStyle: React.CSSProperties = { - display: 'flex', - flexDirection: 'column', - gap: 2, - minWidth: 120, -}; - -const kpiLabelStyle: React.CSSProperties = { - fontSize: 11, - color: 'var(--text-muted)', - textTransform: 'uppercase', - letterSpacing: '0.05em', -}; - -const kpiValueStyle: React.CSSProperties = { - fontSize: 20, - fontWeight: 600, - color: 'var(--amber)', - fontVariantNumeric: 'tabular-nums', -}; - const thStyle: React.CSSProperties = { textAlign: 'left', - padding: '6px 12px', + padding: '8px 16px', fontSize: 11, fontWeight: 600, color: 'var(--text-muted)', @@ -62,14 +42,14 @@ const thStyle: React.CSSProperties = { }; const tdStyle: React.CSSProperties = { - padding: '8px 12px', + padding: '10px 16px', fontSize: 13, borderBottom: '1px solid var(--border)', fontVariantNumeric: 'tabular-nums', }; const monoStyle: React.CSSProperties = { - fontFamily: 'monospace', + fontFamily: 'var(--font-mono, monospace)', fontSize: 12, }; @@ -98,32 +78,30 @@ function PgDetailRow({ slug }: { slug: string }) { } return ( - <> - - - - - - - - - + + - - + ))} + +
TableRowsData SizeIndex Size
+ + + + + + + + + + + {data.map((t) => ( + + + + + - - - {data.map((t) => ( - - - - - - - ))} - -
TableRowsData SizeIndex Size
{t.tableName}{formatNumber(t.rowCount)}{formatBytes(t.dataSizeBytes)}{formatBytes(t.indexSizeBytes)}
{t.tableName}{formatNumber(t.rowCount)}{formatBytes(t.dataSizeBytes)}{formatBytes(t.indexSizeBytes)}
-
+ + ); } @@ -158,14 +136,14 @@ function ChDetailRow({ tenantId }: { tenantId: string }) { Table - Rows + Rows {data.map((t) => ( {t.tableName} - {formatNumber(t.rowCount)} + {formatNumber(t.rowCount)} ))} @@ -179,6 +157,9 @@ function ChDetailRow({ tenantId }: { tenantId: string }) { function PgTenantRow({ tenant }: { tenant: TenantPgStats }) { const [expanded, setExpanded] = useState(false); + const chevron = expanded + ? + : ; return ( <> @@ -186,13 +167,12 @@ function PgTenantRow({ tenant }: { tenant: TenantPgStats }) { style={{ cursor: 'pointer' }} onClick={() => setExpanded((e) => !e)} > - {tenant.slug} - {formatBytes(tenant.schemaSizeBytes)} - {formatNumber(tenant.tableCount)} - {formatNumber(tenant.totalRows)} - - {expanded ? '▲ hide' : '▼ detail'} + + {chevron} {tenant.slug} + {formatBytes(tenant.schemaSizeBytes)} + {formatNumber(tenant.tableCount)} + {formatNumber(tenant.totalRows)} {expanded && } @@ -207,6 +187,9 @@ function ChTenantRow({ tenant }: { tenant: TenantChStats }) { .slice(0, 3) .map(([tbl, rows]) => `${tbl}: ${formatNumber(rows)}`) .join(', '); + const chevron = expanded + ? + : ; return ( <> @@ -214,11 +197,13 @@ function ChTenantRow({ tenant }: { tenant: TenantChStats }) { style={{ cursor: 'pointer' }} onClick={() => setExpanded((e) => !e)} > - {tenant.tenantId} - {formatNumber(tenant.totalRows)} + + {chevron} {tenant.tenantId} + + {formatNumber(tenant.totalRows)} - {tablePreview || '—'} - {Object.keys(tenant.rowsByTable).length > 3 ? ' …' : ''} + {tablePreview || '\u2014'} + {Object.keys(tenant.rowsByTable).length > 3 ? ' \u2026' : ''} {expanded && } @@ -226,88 +211,58 @@ function ChTenantRow({ tenant }: { tenant: TenantChStats }) { ); } +// --- Section Header --- + +function SectionHeader({ title, loading, error }: { title: string; loading?: boolean; error?: boolean }) { + return ( +
+ +

{title}

+ {loading && } + {error && Failed to load} +
+ ); +} + // --- Main Page --- export function InfrastructurePage() { const pg = useInfraPostgres(); const ch = useInfraClickHouse(); - const cardStyle: React.CSSProperties = { - background: 'var(--surface-1)', - border: '1px solid var(--border)', - borderRadius: 8, - overflow: 'hidden', - }; - - const cardHeaderStyle: React.CSSProperties = { - padding: '16px 20px', - borderBottom: '1px solid var(--border)', - display: 'flex', - alignItems: 'center', - gap: 12, - }; - - const cardTitleStyle: React.CSSProperties = { - margin: 0, - fontSize: '1rem', - fontWeight: 600, - }; - - const kpiRowStyle: React.CSSProperties = { - padding: '16px 20px', - display: 'flex', - gap: 32, - flexWrap: 'wrap', - borderBottom: '1px solid var(--border)', - }; - return ( -
+

Infrastructure

{/* PostgreSQL Card */} -
-
-

PostgreSQL

- {pg.isLoading && } - {pg.isError && ( - Failed to load - )} -
+ + {pg.data && ( <> -
-
- Version - - {pg.data.overview.version.split(' ').slice(0, 2).join(' ')} - -
-
- Database Size - {formatBytes(pg.data.overview.databaseSizeBytes)} -
-
- Active Connections - {formatNumber(pg.data.overview.activeConnections)} -
+
+
- - - - - + + + + {pg.data.tenants.length === 0 ? ( - @@ -320,55 +275,33 @@ export function InfrastructurePage() {
Tenant SlugSchema SizeTablesTotal RowsTenantSchema SizeTablesRows
+ No tenant schemas found
)} -
+
{/* ClickHouse Card */} -
-
-

ClickHouse

- {ch.isLoading && } - {ch.isError && ( - Failed to load - )} -
+ + {ch.data && ( <> -
-
- Version - - {ch.data.overview.version} - -
-
- Uptime - {formatUptime(ch.data.overview.uptimeSeconds)} -
-
- Disk Usage - {formatBytes(ch.data.overview.totalDiskBytes)} -
-
- Total Rows - {formatNumber(ch.data.overview.totalRows)} -
-
- Compression - {ch.data.overview.compressionRatio.toFixed(1)}x -
-
- Active Merges - {formatNumber(ch.data.overview.activeMerges)} -
+
+
- - - + + + @@ -387,7 +320,7 @@ export function InfrastructurePage() {
Tenant IDTotal RowsBy Table (preview)TenantTotal RowsBy Table
)} -
+
); }