import { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { Stats, DeduplicationStats, ReferencedArtifact } from '../types'; import { getStats, getDeduplicationStats } from '../api'; import { DataTable } from '../components/DataTable'; import './Dashboard.css'; function formatBytes(bytes: number): string { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`; } function formatNumber(num: number): string { return num.toLocaleString(); } function truncateHash(hash: string, length: number = 12): string { if (hash.length <= length) return hash; return `${hash.slice(0, length)}...`; } interface StatCardProps { label: string; value: string; subvalue?: string; icon: React.ReactNode; variant?: 'default' | 'success' | 'accent'; trend?: 'up' | 'down' | 'neutral'; } function StatCard({ label, value, subvalue, icon, variant = 'default', trend }: StatCardProps) { return (
{icon}
{label} {value} {trend && ( {trend === 'up' && '↑'} {trend === 'down' && '↓'} )} {subvalue && {subvalue}}
); } interface ProgressBarProps { value: number; max: number; label?: string; showPercentage?: boolean; variant?: 'default' | 'success' | 'accent'; } function ProgressBar({ value, max, label, showPercentage = true, variant = 'default' }: ProgressBarProps) { const percentage = max > 0 ? Math.min((value / max) * 100, 100) : 0; return (
{label && (
{label} {showPercentage && {percentage.toFixed(1)}%}
)}
); } function Dashboard() { const [stats, setStats] = useState(null); const [dedupStats, setDedupStats] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { async function loadStats() { try { setLoading(true); const [statsData, dedupData] = await Promise.all([ getStats(), getDeduplicationStats(), ]); setStats(statsData); setDedupStats(dedupData); setError(null); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load statistics'); } finally { setLoading(false); } } loadStats(); }, []); if (loading) { return (
Loading statistics...
); } if (error) { return (

Unable to load dashboard

{error}

); } const artifactColumns = [ { key: 'artifact_id', header: 'Artifact ID', render: (item: ReferencedArtifact) => ( {truncateHash(item.artifact_id, 16)} ), }, { key: 'original_name', header: 'Name', render: (item: ReferencedArtifact) => ( {item.original_name || '—'} ), }, { key: 'size', header: 'Size', render: (item: ReferencedArtifact) => formatBytes(item.size), }, { key: 'ref_count', header: 'References', render: (item: ReferencedArtifact) => ( {formatNumber(item.ref_count)} refs ), }, { key: 'storage_saved', header: 'Storage Saved', render: (item: ReferencedArtifact) => ( {formatBytes(item.storage_saved)} ), }, ]; return (

Storage Dashboard

Real-time deduplication and storage analytics

Storage Overview

} variant="default" /> } variant="success" /> } variant="accent" /> } variant="success" />

Artifact Statistics

} /> } /> } variant="success" /> } />

Deduplication Effectiveness

Logical vs Physical Storage

Logical (with duplicates) {formatBytes(dedupStats?.total_logical_bytes || 0)}
Physical (actual storage) {formatBytes(dedupStats?.total_physical_bytes || 0)}
{formatBytes(dedupStats?.bytes_saved || 0)} saved through deduplication

Deduplication Rate

{(dedupStats?.savings_percentage || 0).toFixed(1)} %
{(stats?.deduplication_ratio || 1).toFixed(2)}x Compression Ratio
{formatNumber(stats?.deduplicated_uploads || 0)} Duplicate Uploads
{dedupStats && dedupStats.most_referenced_artifacts.length > 0 && (

Top Referenced Artifacts

These artifacts are referenced most frequently across your storage, maximizing deduplication savings.

item.artifact_id} emptyMessage="No referenced artifacts found" className="artifacts-table" />
)}
); } export default Dashboard;