import { useT } from '../../lib/i18n/I18nContext'; interface MemoryStatsBarProps { totalDocs: number; totalFiles: number; totalNamespaces: number; totalRelations: number; totalSessions: number | null; totalTokens: number | null; /** Estimated storage in bytes (sum of document content lengths). */ estimatedStorageBytes: number; /** Unix-epoch seconds of the oldest document. */ oldestDocTimestamp: number | null; /** Unix-epoch seconds of the newest document. */ newestDocTimestamp: number | null; docsToday: number; loading?: boolean; } function formatBytes(bytes: number): string { if (bytes === 0) return '0 B'; const units = ['B', 'KB', 'MB', 'GB']; const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1); const value = bytes / Math.pow(1024, i); return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[i]}`; } function formatTimeAgo(epochSeconds: number): string { const now = Date.now() / 1000; const diff = now - epochSeconds; if (diff < 60) return 'just now'; if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; if (diff < 2592000) return `${Math.floor(diff / 86400)}d ago`; if (diff < 31536000) return `${Math.floor(diff / 2592000)}mo ago`; return `${(diff / 31536000).toFixed(1)}y ago`; } function formatNumber(value: number): string { return new Intl.NumberFormat().format(value); } export function MemoryStatsBar(props: MemoryStatsBarProps) { const { t } = useT(); const { totalDocs, totalFiles, totalNamespaces, totalRelations, totalSessions, totalTokens, estimatedStorageBytes, oldestDocTimestamp, newestDocTimestamp, docsToday, loading, } = props; const stats = [ { label: t('stats.storage'), value: estimatedStorageBytes > 0 ? formatBytes(estimatedStorageBytes) : '--', sub: totalFiles > 0 ? `${formatNumber(totalFiles)} ${t('stats.files')}` : undefined, color: 'text-primary-500', }, { label: t('stats.documents'), value: formatNumber(totalDocs), sub: docsToday > 0 ? `+${docsToday} ${t('stats.today')}` : undefined, color: 'text-emerald-600 dark:text-emerald-300', }, { label: t('stats.namespaces'), value: formatNumber(totalNamespaces), sub: undefined, color: 'text-amber-600 dark:text-amber-300', }, { label: t('stats.relations'), value: formatNumber(totalRelations), sub: undefined, color: 'text-lavender-600 dark:text-lavender-300', }, { label: t('stats.firstMemory'), value: oldestDocTimestamp ? formatTimeAgo(oldestDocTimestamp) : '--', sub: newestDocTimestamp ? `${t('stats.latest')}: ${formatTimeAgo(newestDocTimestamp)}` : undefined, color: 'text-sky-600 dark:text-sky-300', }, { label: t('stats.sessions'), value: totalSessions !== null ? formatNumber(totalSessions) : '--', sub: totalTokens !== null ? `${formatNumber(totalTokens)} ${t('stats.tokens')}` : undefined, color: 'text-rose-600 dark:text-rose-300', }, ]; return (
{stats.map(stat => (
{stat.label}
{loading ? (
) : ( stat.value )}
{stat.sub &&
{stat.sub}
}
))}
); }