mirror of
https://github.com/myronblair/kino-app
synced 2026-07-29 05:54:01 -05:00
auto-commit for b082c1c8-f6e3-4cfd-82ee-0396c70d1a00
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import api from "../lib/api";
|
||||
import { Play, ArrowLeft } from "lucide-react";
|
||||
|
||||
export default function ShowDetail() {
|
||||
const { id } = useParams();
|
||||
const nav = useNavigate();
|
||||
const [show, setShow] = useState(null);
|
||||
const [episodes, setEpisodes] = useState([]);
|
||||
const [season, setSeason] = useState(1);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const [s, e] = await Promise.all([api.get(`/shows/${id}`), api.get(`/shows/${id}/episodes`)]);
|
||||
setShow(s.data); setEpisodes(e.data);
|
||||
const firstS = e.data[0]?.season_number || 1;
|
||||
setSeason(firstS);
|
||||
})();
|
||||
}, [id]);
|
||||
|
||||
const seasons = useMemo(() => [...new Set(episodes.map((e) => e.season_number))].sort((a, b) => a - b), [episodes]);
|
||||
const inSeason = useMemo(() => episodes.filter((e) => e.season_number === season), [episodes, season]);
|
||||
|
||||
if (!show) return <div className="min-h-screen bg-[#050505] flex items-center justify-center text-[#8A8A8A]">Loading…</div>;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#050505]" data-testid={`show-detail-${show.id}`}>
|
||||
<div className="relative h-[60vh] min-h-[400px] overflow-hidden">
|
||||
<img src={show.backdrop_url || show.poster_url} alt="" className="absolute inset-0 w-full h-full object-cover" />
|
||||
<div className="absolute inset-0 hero-fade" />
|
||||
<button onClick={() => nav(-1)} className="absolute top-24 left-6 md:left-12 flex items-center gap-2 text-white/80 hover:text-white bg-black/40 hover:bg-black/60 px-4 py-2 backdrop-blur transition-colors" data-testid="show-back">
|
||||
<ArrowLeft size={16} strokeWidth={1.5} /> <span className="text-xs uppercase tracking-[0.2em]">Back</span>
|
||||
</button>
|
||||
<div className="absolute bottom-12 left-6 md:left-12 max-w-2xl">
|
||||
<span className="text-xs uppercase tracking-[0.3em] text-[#D9381E]">Series</span>
|
||||
<h1 className="font-display text-5xl md:text-6xl font-black tracking-tighter text-white mt-2" data-testid="show-title">{show.title}</h1>
|
||||
<div className="flex items-center gap-3 mt-3 text-xs uppercase tracking-[0.2em] text-[#8A8A8A]">
|
||||
<span>{show.year}</span><span>·</span><span>{show.rating}</span><span>·</span>
|
||||
<span>{show.season_count} seasons · {show.episode_count} episodes</span>
|
||||
</div>
|
||||
<p className="text-[#C8C8C8] mt-5 max-w-xl leading-relaxed">{show.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-6 md:px-12 max-w-[1500px] mx-auto py-12">
|
||||
<div className="flex items-center gap-3 flex-wrap mb-6">
|
||||
<span className="text-[10px] uppercase tracking-[0.3em] text-[#8A8A8A]">Seasons</span>
|
||||
{seasons.map((n) => (
|
||||
<button key={n} onClick={() => setSeason(n)}
|
||||
className={`text-sm px-4 py-2 border transition-colors ${season === n ? "border-[#D9381E] text-white bg-[#D9381E]/10" : "border-[#222] text-[#8A8A8A] hover:text-white"}`}
|
||||
data-testid={`season-${n}`}>
|
||||
Season {n}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{inSeason.map((ep) => (
|
||||
<button key={ep.id} onClick={() => nav(`/watch/episode/${ep.id}`)}
|
||||
className="w-full flex items-center gap-4 p-4 border border-[#222] hover:border-[#D9381E] hover:bg-[#0F0F0F] transition-colors text-left group"
|
||||
data-testid={`episode-${ep.id}`}>
|
||||
<div className="w-12 h-12 flex items-center justify-center bg-[#0F0F0F] group-hover:bg-[#D9381E] transition-colors shrink-0">
|
||||
<Play size={16} strokeWidth={1.5} fill="currentColor" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-baseline gap-3">
|
||||
<span className="text-[10px] uppercase tracking-[0.3em] text-[#8A8A8A]">E{ep.episode_number}</span>
|
||||
<h3 className="font-display text-lg text-white truncate">{ep.title || `Episode ${ep.episode_number}`}</h3>
|
||||
{ep.duration_minutes > 0 && <span className="text-xs text-[#8A8A8A] ml-auto">{ep.duration_minutes}m</span>}
|
||||
</div>
|
||||
{ep.description && <p className="text-sm text-[#8A8A8A] mt-1 line-clamp-2">{ep.description}</p>}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user