import { useState, useEffect } from 'react'; import { useAnimeDetail } from '../../hooks/useAnimeDetail'; import { getStatsClient } from '../../hooks/useStatsApi'; import { formatDuration, formatNumber, epochDayToDate } from '../../lib/formatters'; import { StatCard } from '../layout/StatCard'; import { AnimeHeader } from './AnimeHeader'; import { EpisodeList } from './EpisodeList'; import { AnimeWordList } from './AnimeWordList'; import { CHART_THEME } from '../../lib/chart-theme'; import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'; import type { DailyRollup } from '../../types/stats'; interface AnimeDetailViewProps { animeId: number; onBack: () => void; onNavigateToWord?: (wordId: number) => void; } type Range = 14 | 30 | 90; function formatActiveMinutes(value: number | string) { const minutes = Number(value); return [`${Number.isFinite(minutes) ? minutes : 0} min`, 'Active Time']; } function AnimeWatchChart({ animeId }: { animeId: number }) { const [rollups, setRollups] = useState([]); const [range, setRange] = useState(30); useEffect(() => { let cancelled = false; getStatsClient() .getAnimeRollups(animeId, 90) .then((data) => { if (!cancelled) setRollups(data); }) .catch(() => { if (!cancelled) setRollups([]); }); return () => { cancelled = true; }; }, [animeId]); const byDay = new Map(); for (const r of rollups) { byDay.set(r.rollupDayOrMonth, (byDay.get(r.rollupDayOrMonth) ?? 0) + r.totalActiveMin); } const chartData = Array.from(byDay.entries()) .sort(([a], [b]) => a - b) .map(([day, mins]) => ({ date: epochDayToDate(day).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }), minutes: Math.round(mins), })) .slice(-range); const ranges: Range[] = [14, 30, 90]; if (chartData.length === 0) return null; return (

Watch Time

{ranges.map((r) => ( ))}
); } export function AnimeDetailView({ animeId, onBack, onNavigateToWord }: AnimeDetailViewProps) { const { data, loading, error } = useAnimeDetail(animeId); if (loading) return
Loading...
; if (error) return
Error: {error}
; if (!data?.detail) return
Anime not found
; const { detail, episodes, anilistEntries } = data; const avgSessionMs = detail.totalSessions > 0 ? Math.round(detail.totalActiveMs / detail.totalSessions) : 0; return (
); }