Spaces:
Running
Running
import React from 'react'; | |
import { Check, Play, X } from 'lucide-react'; | |
interface Episode { | |
episode_number: number; | |
name: string; | |
fileName?: string; | |
} | |
interface Season { | |
season_number: number; | |
name: string; | |
episodes: Episode[]; | |
} | |
interface PlaybackProgress { | |
[key: string]: { | |
currentTime: number; | |
duration: number; | |
lastPlayed: string; | |
completed: boolean; | |
}; | |
} | |
interface EpisodesPanelProps { | |
seasons: Season[]; | |
selectedSeason: string; | |
selectedEpisode: string; | |
playbackProgress: PlaybackProgress; | |
onSelectEpisode: (seasonName: string, episode: Episode) => void; | |
onClose: () => void; | |
showTitle?: string; | |
} | |
const EpisodesPanel: React.FC<EpisodesPanelProps> = ({ | |
seasons, | |
selectedSeason, | |
selectedEpisode, | |
playbackProgress, | |
onSelectEpisode, | |
onClose, | |
showTitle = 'Episodes' | |
}) => { | |
// Helper function to get episode progress | |
const getEpisodeProgress = (seasonName: string, episodeFileName: string) => { | |
const episodeId = `${seasonName}-${episodeFileName}`; | |
return playbackProgress[episodeId] || null; | |
}; | |
return ( | |
<div className="h-full flex flex-col"> | |
<div className="p-6 flex justify-between items-center border-b border-gray-800"> | |
<h2 className="text-2xl font-bold text-white">{showTitle}</h2> | |
<button | |
onClick={onClose} | |
className="text-white hover:text-gray-300 transition-colors" | |
aria-label="Close episodes panel" | |
> | |
<X className="h-6 w-6" /> | |
</button> | |
</div> | |
<div className="flex-1 overflow-y-auto p-6 space-y-8"> | |
{seasons.map((season) => ( | |
<div key={season.name} className="mb-6"> | |
<h3 className="text-xl font-bold mb-3 text-white">{season.name}</h3> | |
<div className="space-y-2"> | |
{season.episodes.map((episode) => { | |
const progress = getEpisodeProgress(season.name, episode.fileName || ''); | |
const progressPercent = progress | |
? Math.min(100, Math.floor((progress.currentTime / progress.duration) * 100)) | |
: 0; | |
return ( | |
<div | |
key={episode.fileName} | |
className={`p-3 rounded-md flex items-start hover:bg-gray-800 cursor-pointer transition-colors ${selectedEpisode === episode.fileName ? 'bg-gray-800' : 'bg-gray-900/60'}`} | |
onClick={() => onSelectEpisode(season.name, episode)} | |
> | |
<div className="flex-shrink-0 mr-3"> | |
{selectedEpisode === episode.fileName ? ( | |
<div className="w-4 h-4 rounded-full bg-red-600 flex items-center justify-center"> | |
<Play size={8} className="text-white ml-0.5" /> | |
</div> | |
) : ( | |
<div className="w-4 h-4 rounded-full bg-gray-700 flex items-center justify-center"> | |
<span className="text-xs text-white">{episode.episode_number}</span> | |
</div> | |
)} | |
</div> | |
<div className="flex-1"> | |
<div className="flex justify-between"> | |
<h4 className="font-medium text-white">{episode.name}</h4> | |
{progress?.completed && ( | |
<Check size={16} className="text-green-500" /> | |
)} | |
</div> | |
<div className="relative w-full h-1 bg-gray-700 mt-2 rounded overflow-hidden"> | |
<div | |
className="absolute left-0 top-0 h-full bg-red-600" | |
style={{ width: `${progressPercent}%` }} | |
/> | |
</div> | |
</div> | |
</div> | |
); | |
})} | |
</div> | |
</div> | |
))} | |
</div> | |
</div> | |
); | |
}; | |
export default EpisodesPanel; | |