import { Fragment, useState } from 'react'; import { formatDuration, formatNumber, formatRelativeDate } from '../../lib/formatters'; import { apiClient } from '../../lib/api-client'; import { EpisodeDetail } from './EpisodeDetail'; import type { AnimeEpisode } from '../../types/stats'; interface EpisodeListProps { episodes: AnimeEpisode[]; } export function EpisodeList({ episodes: initialEpisodes }: EpisodeListProps) { const [expandedVideoId, setExpandedVideoId] = useState(null); const [episodes, setEpisodes] = useState(initialEpisodes); if (episodes.length === 0) return null; const sorted = [...episodes].sort((a, b) => { if (a.episode != null && b.episode != null) return a.episode - b.episode; if (a.episode != null) return -1; if (b.episode != null) return 1; return 0; }); const toggleWatched = async (videoId: number, currentWatched: number) => { const newWatched = currentWatched ? 0 : 1; setEpisodes((prev) => prev.map((ep) => (ep.videoId === videoId ? { ...ep, watched: newWatched } : ep)), ); try { await apiClient.setVideoWatched(videoId, newWatched === 1); } catch { setEpisodes((prev) => prev.map((ep) => (ep.videoId === videoId ? { ...ep, watched: currentWatched } : ep)), ); } }; const watchedCount = episodes.filter((ep) => ep.watched).length; return (

Episodes

{watchedCount}/{episodes.length} watched
{sorted.map((ep, idx) => ( setExpandedVideoId(expandedVideoId === ep.videoId ? null : ep.videoId)} className="border-b border-ctp-surface1 last:border-0 cursor-pointer hover:bg-ctp-surface1/50 transition-colors" > {expandedVideoId === ep.videoId && ( )} ))}
# Title Progress Watch Time Cards Last Watched
{expandedVideoId === ep.videoId ? '\u25BC' : '\u25B6'} {ep.episode ?? idx + 1} {ep.canonicalTitle} {ep.durationMs > 0 ? ( = ep.durationMs * 0.85 ? 'text-ctp-green' : ep.totalActiveMs >= ep.durationMs * 0.5 ? 'text-ctp-peach' : 'text-ctp-overlay2' }> {Math.min(100, Math.round((ep.totalActiveMs / ep.durationMs) * 100))}% ) : ( {'\u2014'} )} {formatDuration(ep.totalActiveMs)} {formatNumber(ep.totalCards)} {ep.lastWatchedMs > 0 ? formatRelativeDate(ep.lastWatchedMs) : '\u2014'}
); }