/**
* QuickStats Component
*
* Displays quick statistics about the knowledge base including
* total memories, connections, memories added this week, and hub nodes.
* Uses glassmorphism styling consistent with the dark cosmic theme.
*
* Requirements: 39.2, 42.3
*/
import { useMemo, type ReactElement } from 'react';
// ============================================================================
// Types
// ============================================================================
export interface QuickStatsData {
/** Total number of memories */
totalMemories: number;
/** Total number of connections */
totalConnections: number;
/** Memories added this week */
memoriesThisWeek: number;
/** Number of hub nodes (>5 connections) */
hubNodes: number;
}
export interface QuickStatsProps {
/** Stats data to display */
stats: QuickStatsData;
/** Display variant */
variant?: 'grid' | 'compact' | 'inline';
/** Show trend indicators */
showTrends?: boolean;
/** Additional CSS classes */
className?: string;
}
interface StatCardProps {
/** Stat label */
label: string;
/** Stat value */
value: number;
/** Icon emoji */
icon: string;
/** Trend direction */
trend?: 'up' | 'down' | 'neutral' | undefined;
/** Trend display value */
trendValue?: string | undefined;
/** Compact display mode */
compact?: boolean | undefined;
}
// ============================================================================
// Constants
// ============================================================================
const DEFAULT_STATS: QuickStatsData = {
totalMemories: 0,
totalConnections: 0,
memoriesThisWeek: 0,
hubNodes: 0,
};
// ============================================================================
// Sub-Components
// ============================================================================
/**
* Individual stat card with glassmorphism styling
*/
function StatCard({
label,
value,
icon,
trend,
trendValue,
compact = false,
}: StatCardProps): ReactElement {
if (compact) {
return (
<div className="flex items-center gap-2 px-3 py-2 rounded-lg bg-ui-surface/30 backdrop-blur-sm">
<span className="text-lg" aria-hidden="true">
{icon}
</span>
<div className="flex flex-col">
<span className="text-lg font-bold text-ui-text-primary">{value.toLocaleString()}</span>
<span className="text-xs text-ui-text-muted">{label}</span>
</div>
</div>
);
}
return (
<div className="glass-panel-glow p-4 flex flex-col gap-2 animate-fade-in">
<div className="flex items-center justify-between">
<span className="text-2xl" aria-hidden="true">
{icon}
</span>
{trend !== undefined && trendValue !== undefined && (
<span
className={`
text-xs px-2 py-0.5 rounded-full
${trend === 'up' ? 'bg-status-success/20 text-status-success' : ''}
${trend === 'down' ? 'bg-status-error/20 text-status-error' : ''}
${trend === 'neutral' ? 'bg-ui-border/50 text-ui-text-secondary' : ''}
`}
>
{trend === 'up' && '↑'}
{trend === 'down' && '↓'}
{trendValue}
</span>
)}
</div>
<div className="flex flex-col">
<span className="text-3xl font-bold text-ui-text-primary">{value.toLocaleString()}</span>
<span className="text-sm text-ui-text-secondary">{label}</span>
</div>
</div>
);
}
/**
* Inline stat item for compact displays
*/
function InlineStat({
label,
value,
icon,
}: {
label: string;
value: number;
icon: string;
}): ReactElement {
return (
<div className="flex items-center gap-1.5" title={`${label}: ${String(value)}`}>
<span className="text-sm" aria-hidden="true">
{icon}
</span>
<span className="text-ui-accent-primary text-sm font-semibold">{value.toLocaleString()}</span>
</div>
);
}
// ============================================================================
// Main Component
// ============================================================================
/**
* QuickStats - Display quick statistics about the knowledge base
*
* Features:
* - Total memories count (Requirement 39.2)
* - Total connections count (Requirement 39.2)
* - Memories added this week (Requirement 39.2)
* - Hub nodes count (>5 connections) (Requirement 39.2, 42.3)
* - Multiple display variants (grid, compact, inline)
* - Trend indicators for activity tracking
* - Glassmorphism styling
*
* Variants:
* - grid: Full-size cards in a responsive grid (default)
* - compact: Smaller cards for sidebar use
* - inline: Single-line display for minimal space
*
* Requirements: 39.2, 42.3
*/
export function QuickStats({
stats = DEFAULT_STATS,
variant = 'grid',
showTrends = true,
className = '',
}: QuickStatsProps): ReactElement {
// Calculate trends based on this week's activity
const trends = useMemo(() => {
const weeklyActive = stats.memoriesThisWeek > 0;
const highActivity = stats.memoriesThisWeek > 5;
return {
memories: {
trend: weeklyActive ? ('up' as const) : ('neutral' as const),
value: weeklyActive ? `+${String(stats.memoriesThisWeek)}` : '—',
},
thisWeek: {
trend: highActivity ? ('up' as const) : ('neutral' as const),
value: highActivity ? 'Active' : '—',
},
};
}, [stats.memoriesThisWeek]);
// Inline variant - single row of stats
if (variant === 'inline') {
return (
<div
className={`flex items-center gap-4 ${className}`}
role="region"
aria-label="Quick Statistics"
>
<InlineStat label="Memories" value={stats.totalMemories} icon="🧠" />
<InlineStat label="Connections" value={stats.totalConnections} icon="🔗" />
<InlineStat label="Hub Nodes" value={stats.hubNodes} icon="⭐" />
<InlineStat label="This Week" value={stats.memoriesThisWeek} icon="📅" />
</div>
);
}
// Compact variant - smaller cards for sidebar
if (variant === 'compact') {
return (
<div
className={`grid grid-cols-2 gap-2 ${className}`}
role="region"
aria-label="Quick Statistics"
>
<StatCard label="Memories" value={stats.totalMemories} icon="🧠" compact />
<StatCard label="Connections" value={stats.totalConnections} icon="🔗" compact />
<StatCard label="Hub Nodes" value={stats.hubNodes} icon="⭐" compact />
<StatCard label="This Week" value={stats.memoriesThisWeek} icon="📅" compact />
</div>
);
}
// Grid variant (default) - full-size cards
return (
<div
className={`grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 ${className}`}
role="region"
aria-label="Quick Statistics"
>
<StatCard
label="Total Memories"
value={stats.totalMemories}
icon="🧠"
trend={showTrends ? trends.memories.trend : undefined}
trendValue={showTrends ? trends.memories.value : undefined}
/>
<StatCard label="Connections" value={stats.totalConnections} icon="🔗" />
<StatCard label="Hub Nodes" value={stats.hubNodes} icon="⭐" />
<StatCard
label="This Week"
value={stats.memoriesThisWeek}
icon="📅"
trend={showTrends ? trends.thisWeek.trend : undefined}
trendValue={showTrends ? trends.thisWeek.value : undefined}
/>
</div>
);
}
export default QuickStats;