Update DetailView.tsx

This commit is contained in:
Lars Behrends
2026-04-11 01:27:58 +02:00
parent 0d530ea99c
commit 6c316fbf84

View File

@@ -1,6 +1,6 @@
import { Media, Staff } from '@/types'; import { Media, Staff } from '@/types';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useState } from 'react'; import { useState, useMemo, useEffect } from 'react';
import { import {
Play, Play,
Bookmark, Bookmark,
@@ -27,6 +27,44 @@ export default function DetailView({ media, onPersonClick }: DetailViewProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const [castLimit, setCastLimit] = useState(6); const [castLimit, setCastLimit] = useState(6);
const [showAllCast, setShowAllCast] = useState(false); const [showAllCast, setShowAllCast] = useState(false);
const [expandedSeasons, setExpandedSeasons] = useState<Set<number>>(new Set());
// Group episodes by season
const episodesBySeason = useMemo(() => {
if (!media.episodes) return {};
const grouped: Record<number, typeof media.episodes> = {};
media.episodes.forEach(episode => {
if (!grouped[episode.season]) {
grouped[episode.season] = [];
}
grouped[episode.season].push(episode);
});
// Sort episodes within each season by episode number
Object.keys(grouped).forEach(season => {
grouped[Number(season)].sort((a, b) => a.episode_number - b.episode_number);
});
return grouped;
}, [media.episodes]);
// Expand first season by default on mount
useEffect(() => {
const seasons = Object.keys(episodesBySeason).map(Number).sort((a, b) => a - b);
if (seasons.length > 0) {
setExpandedSeasons(new Set([seasons[0]]));
}
}, [episodesBySeason]);
const toggleSeason = (season: number) => {
setExpandedSeasons(prev => {
const newSet = new Set(prev);
if (newSet.has(season)) {
newSet.delete(season);
} else {
newSet.add(season);
}
return newSet;
});
};
const displayedCast = showAllCast ? media.staff : (media.staff?.slice(0, castLimit) || []); const displayedCast = showAllCast ? media.staff : (media.staff?.slice(0, castLimit) || []);
const hasMoreCast = (media.staff?.length || 0) > castLimit; const hasMoreCast = (media.staff?.length || 0) > castLimit;
@@ -254,6 +292,9 @@ export default function DetailView({ media, onPersonClick }: DetailViewProps) {
<div className="flex items-center gap-2 text-[#6d28d9] font-black text-xl"> <div className="flex items-center gap-2 text-[#6d28d9] font-black text-xl">
<span className="opacity-40">{media.episodes.length}</span> Episode{media.episodes.length !== 1 ? 's' : ''} <span className="opacity-40">{media.episodes.length}</span> Episode{media.episodes.length !== 1 ? 's' : ''}
</div> </div>
<div className="text-sm font-bold text-muted-foreground">
{Object.keys(episodesBySeason).length} Season{Object.keys(episodesBySeason).length !== 1 ? 's' : ''}
</div>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="relative"> <div className="relative">
@@ -269,29 +310,57 @@ export default function DetailView({ media, onPersonClick }: DetailViewProps) {
</div> </div>
</div> </div>
<div className="space-y-6"> <div className="space-y-4">
{media.episodes.map(episode => ( {Object.keys(episodesBySeason)
<div key={episode.id} className="group cursor-pointer"> .map(Number)
<div className="flex flex-col md:flex-row gap-6"> .sort((a, b) => a - b)
<div className="w-full md:w-[240px] shrink-0 aspect-video rounded-xl overflow-hidden shadow-sm relative"> .map(season => (
<img src={episode.thumbnail} alt={episode.title} className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105" referrerPolicy="no-referrer" /> <div key={season} className="border border-border rounded-2xl overflow-hidden">
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors" /> <button
</div> onClick={() => toggleSeason(season)}
<div className="flex-1 py-1"> className="w-full flex items-center justify-between p-6 bg-card hover:bg-muted/50 transition-colors"
<div className="flex items-center justify-between mb-2"> >
<h3 className="font-black text-foreground group-hover:text-[#6d28d9] transition-colors"> <div className="flex items-center gap-4">
S{episode.season}:E{episode.episode_number} {episode.title} <h3 className="text-2xl font-black text-foreground">Season {season}</h3>
</h3> <Badge variant="outline" className="border-[#6d28d9]/30 text-[#6d28d9] font-bold">
<span className="text-xs font-bold text-muted-foreground">{episode.air_date} {episode.duration}m</span> {episodesBySeason[season].length} Episode{episodesBySeason[season].length !== 1 ? 's' : ''}
</Badge>
</div> </div>
<p className="text-sm text-muted-foreground leading-relaxed line-clamp-3"> <ChevronDown
{episode.description} size={24}
</p> className={`transition-transform duration-300 text-muted-foreground ${
</div> expandedSeasons.has(season) ? 'rotate-180' : ''
}`}
/>
</button>
{expandedSeasons.has(season) && (
<div className="p-6 pt-0 space-y-6">
{episodesBySeason[season].map(episode => (
<div key={episode.id} className="group cursor-pointer">
<div className="flex flex-col md:flex-row gap-6">
<div className="w-full md:w-[240px] shrink-0 aspect-video rounded-xl overflow-hidden shadow-sm relative">
<img src={episode.thumbnail} alt={episode.title} className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105" referrerPolicy="no-referrer" />
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors" />
</div>
<div className="flex-1 py-1">
<div className="flex items-center justify-between mb-2">
<h3 className="font-black text-foreground group-hover:text-[#6d28d9] transition-colors">
E{episode.episode_number} {episode.title}
</h3>
<span className="text-xs font-bold text-muted-foreground">{episode.air_date} {episode.duration}m</span>
</div>
<p className="text-sm text-muted-foreground leading-relaxed line-clamp-3">
{episode.description}
</p>
</div>
</div>
<Separator className="mt-6 bg-border" />
</div>
))}
</div>
)}
</div> </div>
<Separator className="mt-6 bg-border" /> ))}
</div>
))}
</div> </div>
</section> </section>
)} )}