Spaces:
Sleeping
Sleeping
| 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; | |