feat: optimize stats dashboard data and components

This commit is contained in:
2026-03-17 00:48:56 -07:00
parent 11710f20db
commit 390ae1b2f2
24 changed files with 837 additions and 174 deletions

View File

@@ -1,4 +1,6 @@
export type TabId = 'overview' | 'anime' | 'trends' | 'vocabulary' | 'sessions';
import { useRef, type KeyboardEvent } from 'react';
export type TabId = 'overview' | 'anime' | 'trends' | 'vocabulary' | 'sessions' | 'library';
interface Tab {
id: TabId;
@@ -9,6 +11,7 @@ const TABS: Tab[] = [
{ id: 'overview', label: 'Overview' },
{ id: 'anime', label: 'Anime' },
{ id: 'trends', label: 'Trends' },
{ id: 'library', label: 'Library' },
{ id: 'vocabulary', label: 'Vocabulary' },
{ id: 'sessions', label: 'Sessions' },
];
@@ -19,18 +22,58 @@ interface TabBarProps {
}
export function TabBar({ activeTab, onTabChange }: TabBarProps) {
const tabRefs = useRef<Array<HTMLButtonElement | null>>([]);
const activateAtIndex = (index: number) => {
const tab = TABS[index];
if (!tab) return;
tabRefs.current[index]?.focus();
onTabChange(tab.id);
};
const onTabKeyDown = (event: KeyboardEvent<HTMLButtonElement>, index: number) => {
if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
event.preventDefault();
activateAtIndex((index + 1) % TABS.length);
return;
}
if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
event.preventDefault();
activateAtIndex((index - 1 + TABS.length) % TABS.length);
return;
}
if (event.key === 'Home') {
event.preventDefault();
activateAtIndex(0);
return;
}
if (event.key === 'End') {
event.preventDefault();
activateAtIndex(TABS.length - 1);
}
};
return (
<nav className="flex border-b border-ctp-surface1" role="tablist" aria-label="Stats tabs">
{TABS.map((tab) => (
<nav
className="flex border-b border-ctp-surface1"
role="tablist"
aria-label="Stats tabs"
aria-orientation="horizontal"
>
{TABS.map((tab, index) => (
<button
key={tab.id}
id={`tab-${tab.id}`}
ref={(element) => {
tabRefs.current[index] = element;
}}
type="button"
role="tab"
aria-controls={`panel-${tab.id}`}
aria-selected={activeTab === tab.id}
tabIndex={activeTab === tab.id ? 0 : -1}
onClick={() => onTabChange(tab.id)}
onKeyDown={(event) => onTabKeyDown(event, index)}
className={`px-4 py-2.5 text-sm font-medium transition-colors
${
activeTab === tab.id