import { useMemo, useState } from 'react'; import { Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; import type { LibrarySummaryRow } from '../../types/stats'; import { CHART_DEFAULTS, CHART_THEME, TOOLTIP_CONTENT_STYLE } from '../../lib/chart-theme'; import { epochDayToDate, formatDuration, formatNumber } from '../../lib/formatters'; interface LibrarySummarySectionProps { rows: LibrarySummaryRow[]; hiddenTitles: ReadonlySet; } const LEADERBOARD_LIMIT = 10; const LEADERBOARD_HEIGHT = 260; const LEADERBOARD_BAR_COLOR = '#8aadf4'; const TABLE_MAX_HEIGHT = 480; type SortColumn = | 'title' | 'watchTimeMin' | 'videos' | 'sessions' | 'cards' | 'words' | 'lookups' | 'lookupsPerHundred' | 'firstWatched'; type SortDirection = 'asc' | 'desc'; interface ColumnDef { id: SortColumn; label: string; align: 'left' | 'right'; } const COLUMNS: ColumnDef[] = [ { id: 'title', label: 'Title', align: 'left' }, { id: 'watchTimeMin', label: 'Watch Time', align: 'right' }, { id: 'videos', label: 'Videos', align: 'right' }, { id: 'sessions', label: 'Sessions', align: 'right' }, { id: 'cards', label: 'Cards', align: 'right' }, { id: 'words', label: 'Words', align: 'right' }, { id: 'lookups', label: 'Lookups', align: 'right' }, { id: 'lookupsPerHundred', label: 'Lookups/100w', align: 'right' }, { id: 'firstWatched', label: 'Date Range', align: 'right' }, ]; function truncateTitle(title: string, maxChars: number): string { if (title.length <= maxChars) return title; return `${title.slice(0, maxChars - 1)}…`; } function formatDateRange(firstEpochDay: number, lastEpochDay: number): string { const fmt = (epochDay: number) => epochDayToDate(epochDay).toLocaleDateString(undefined, { month: 'short', day: 'numeric', }); if (firstEpochDay === lastEpochDay) return fmt(firstEpochDay); return `${fmt(firstEpochDay)} → ${fmt(lastEpochDay)}`; } function formatWatchTime(min: number): string { return formatDuration(min * 60_000); } function compareRows( a: LibrarySummaryRow, b: LibrarySummaryRow, column: SortColumn, direction: SortDirection, ): number { const sign = direction === 'asc' ? 1 : -1; if (column === 'title') { return a.title.localeCompare(b.title) * sign; } if (column === 'firstWatched') { return (a.firstWatched - b.firstWatched) * sign; } if (column === 'lookupsPerHundred') { const aVal = a.lookupsPerHundred; const bVal = b.lookupsPerHundred; if (aVal === null && bVal === null) return 0; if (aVal === null) return 1; if (bVal === null) return -1; return (aVal - bVal) * sign; } const aVal = a[column] as number; const bVal = b[column] as number; return (aVal - bVal) * sign; } export function LibrarySummarySection({ rows, hiddenTitles }: LibrarySummarySectionProps) { const [sortColumn, setSortColumn] = useState('watchTimeMin'); const [sortDirection, setSortDirection] = useState('desc'); const visibleRows = useMemo( () => rows.filter((row) => !hiddenTitles.has(row.title)), [rows, hiddenTitles], ); const sortedRows = useMemo( () => [...visibleRows].sort((a, b) => compareRows(a, b, sortColumn, sortDirection)), [visibleRows, sortColumn, sortDirection], ); const leaderboard = useMemo( () => [...visibleRows] .sort((a, b) => b.watchTimeMin - a.watchTimeMin) .slice(0, LEADERBOARD_LIMIT) .map((row) => ({ title: row.title, displayTitle: truncateTitle(row.title, 24), watchTimeMin: row.watchTimeMin, })), [visibleRows], ); if (visibleRows.length === 0) { return (
No library activity in the selected window.
); } const handleHeaderClick = (column: SortColumn) => { if (column === sortColumn) { setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc')); } else { setSortColumn(column); setSortDirection(column === 'title' ? 'asc' : 'desc'); } }; return ( <>

Top Titles by Watch Time (min)

[`${value} min`, 'Watch Time']} labelFormatter={(_label, payload) => { const datum = payload?.[0]?.payload as { title?: string } | undefined; return datum?.title ?? ''; }} />

Per-Title Summary

{COLUMNS.map((column) => { const isActive = column.id === sortColumn; const indicator = isActive ? (sortDirection === 'asc' ? ' ▲' : ' ▼') : ''; return ( ); })} {sortedRows.map((row) => ( ))}
handleHeaderClick(column.id)} > {column.label} {indicator}
{row.title} {formatWatchTime(row.watchTimeMin)} {formatNumber(row.videos)} {formatNumber(row.sessions)} {formatNumber(row.cards)} {formatNumber(row.words)} {formatNumber(row.lookups)} {row.lookupsPerHundred === null ? '—' : row.lookupsPerHundred.toFixed(1)} {formatDateRange(row.firstWatched, row.lastWatched)}
); }