import { AlertCircle, Database, FolderOpen, HardDrive, Server, Zap } from 'lucide-react'; import { Link } from 'react-router-dom'; import { PageHeader } from '@/components/ui/page-header'; import { IconTile } from '@/components/ui/icon-tile'; import { EmptyState } from '@/components/ui/empty-state'; import { BucketUsageChart } from '@/components/charts/BucketUsageChart'; import { useDashboardData } from '@/hooks/useApi'; import { formatBytes } from '@/lib/file-utils'; import type { ClusterHealth } from '@/types'; type StatTone = 'primary' | 'destructive' | 'neutral'; type HealthLabel = 'Healthy' | 'Degraded' | 'Unhealthy' | 'Unknown'; function deriveHealth(health: ClusterHealth | null): { label: HealthLabel; tone: StatTone } { if (!health) return { label: 'Unknown', tone: 'neutral' }; if ( health.storageNodesUp === health.storageNodes && health.partitionsAllOk === health.partitions && health.connectedNodes === health.knownNodes ) return { label: 'Healthy', tone: 'primary' }; if (health.storageNodesUp > 0 && health.partitionsQuorum > 0) return { label: 'Degraded', tone: 'primary' }; return { label: 'Unhealthy', tone: 'destructive' }; } export function Dashboard() { const { metrics: metricsQuery, buckets: bucketsQuery, health: healthQuery, isLoading } = useDashboardData(); const metrics = metricsQuery.data; const buckets = bucketsQuery.data ?? []; const clusterHealth = healthQuery.data ?? null; const health = deriveHealth(clusterHealth); return (
{isLoading ? (

Loading dashboard…

) : (
{/* KPI row */}
} /> } /> } /> : } iconTone={health.tone} />
{/* Cluster row */}
} /> } /> } />
{/* Charts */}
{metrics?.usageByBucket && metrics.usageByBucket.length > 0 ? ( ) : (
No data available
)}
{metrics?.usageByBucket && metrics.usageByBucket.length > 0 ? (
{metrics.usageByBucket.map((bucket) => (
{bucket.bucketName}
{bucket.objectCount.toLocaleString()} objects {formatBytes(bucket.size)} {bucket.percentage.toFixed(1)}%
))}
) : (
No buckets available
)}
{/* Recent buckets */} {buckets.length === 0 ? ( } title="No buckets yet" description="Create your first bucket from the Buckets page to start storing objects." tone="neutral" /> ) : (
    {buckets.slice(0, 5).map((bucket) => (
  • } tone="primary" size="md" />

    {bucket.name}

    Created {new Date(bucket.creationDate).toLocaleDateString()}

    {bucket.objectCount?.toLocaleString() ?? '—'} objects

    {bucket.size ? formatBytes(bucket.size) : '—'}

  • ))}
)}
)}
); } function StatCard({ label, value, sub, icon, iconTone = 'primary', valueTone = 'neutral', }: { label: string; value: string; sub?: string; icon: React.ReactNode; iconTone?: StatTone; valueTone?: StatTone; }) { const valueColor = valueTone === 'primary' ? 'text-[var(--primary)]' : valueTone === 'destructive' ? 'text-[var(--destructive)]' : 'text-[var(--foreground)]'; return (
{label}
{value}
{sub &&
{sub}
}
); } function Card({ title, description, children, }: { title: string; description?: string; children: React.ReactNode; }) { return (

{title}

{description &&

{description}

}
{children}
); }