import { StatCard, DataTable, ProgressBar } from '@cameleer/design-system'; import type { Column } from '@cameleer/design-system'; import { useClickHouseStatus, useClickHouseTables, useClickHousePerformance, useClickHouseQueries, useIndexerPipeline } from '../../api/queries/admin/clickhouse'; import styles from './ClickHouseAdminPage.module.css'; import sectionStyles from '../../styles/section-card.module.css'; import tableStyles from '../../styles/table-section.module.css'; export default function ClickHouseAdminPage() { const { data: status, isError: statusError } = useClickHouseStatus(); const { data: tables } = useClickHouseTables(); const { data: perf } = useClickHousePerformance(); const { data: queries } = useClickHouseQueries(); const { data: pipeline } = useIndexerPipeline(); const unreachable = statusError || (status && !status.reachable); const totalSize = (tables || []).reduce((sum, t) => sum + (t.dataSizeBytes || 0), 0); const totalSizeLabel = totalSize > 0 ? formatBytes(totalSize) : ''; const tableColumns: Column[] = [ { key: 'name', header: 'Table', sortable: true }, { key: 'engine', header: 'Engine' }, { key: 'rowCount', header: 'Rows', sortable: true, render: (v) => Number(v).toLocaleString() }, { key: 'dataSize', header: 'Size', sortable: true }, { key: 'partitionCount', header: 'Partitions', sortable: true }, ]; const queryColumns: Column[] = [ { key: 'elapsedSeconds', header: 'Elapsed', render: (v) => `${v}s` }, { key: 'memory', header: 'Memory' }, { key: 'readRows', header: 'Rows Read', render: (v) => Number(v).toLocaleString() }, { key: 'query', header: 'Query', render: (v) => {String(v).slice(0, 100)} }, ]; return (
{/* Status */}
{/* Storage overview */} {perf && (
)} {/* Pipeline */} {pipeline && (
Indexer Pipeline
0 ? (pipeline.queueDepth / pipeline.maxQueueSize) * 100 : 0} />
Queue: {pipeline.queueDepth}/{pipeline.maxQueueSize} Indexed: {pipeline.indexedCount.toLocaleString()} Failed: {pipeline.failedCount} Rate: {pipeline.indexingRate.toFixed(1)}/s
)} {/* Tables */}
Tables ({(tables || []).length}) {totalSizeLabel && {totalSizeLabel} total}
({ ...t, id: t.name }))} sortable pageSize={20} flush />
{/* Active Queries */}
Active Queries ({(queries || []).length})
({ ...q, id: q.queryId || String(i) }))} flush />
); } function formatBytes(bytes: number): string { if (bytes === 0) return '0 B'; const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB']; const i = Math.floor(Math.log(bytes) / Math.log(1024)); const val = bytes / Math.pow(1024, i); return `${val.toFixed(val < 10 ? 2 : 1)} ${units[i]}`; }