fix: TV-Modal schwarze Seite (xREL skip), aufklappbare Sektionen, TV-Staffelinfo

This commit is contained in:
2026-06-18 11:27:10 +02:00
parent 5c4bbaa9ab
commit 4708f27912

View File

@@ -387,6 +387,47 @@ function DemnächstGrid({ onOpenModal, xrelIds, onXrelLoaded }) {
}
// ── Suche ────────────────────────────────────────────────────────────────────
// ── Aufklappbare Film/Serien-Sektion ─────────────────────────────────────────
function CollapsibleSection({ label, count, children, defaultOpen=true }) {
const [open, setOpen] = useState(defaultOpen);
return (
<div style={{marginBottom:16}}>
<button onClick={()=>setOpen(v=>!v)} style={{
display:'flex',alignItems:'center',gap:8,background:'none',border:'none',cursor:'pointer',
width:'100%',padding:'6px 0',marginBottom: open?10:0,
}}>
<span style={{color:'rgba(255,255,255,0.5)',fontFamily:'monospace',fontSize:11,letterSpacing:1}}>
{label} ({count})
</span>
<div style={{flex:1,height:1,background:'rgba(255,255,255,0.07)'}}/>
<span style={{color:'rgba(255,255,255,0.3)',fontSize:11,transform:open?'rotate(90deg)':'none',transition:'transform 0.2s'}}></span>
</button>
{open && children}
</div>
);
}
function SectionedGrid({ films, serien, favIds, xrelIds, onToggle, onOpenModal }) {
return (
<div>
{films.length > 0 && (
<CollapsibleSection label="🎬 FILME" count={films.length}>
<div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(130px,1fr))',gap:12}}>
{films.map(m => <MovieCard key={'m'+m.id} movie={m} favIds={favIds} hasXrel={xrelIds?.has(m.id)} onToggleFav={onToggle} onOpenModal={onOpenModal}/>)}
</div>
</CollapsibleSection>
)}
{serien.length > 0 && (
<CollapsibleSection label="📺 SERIEN" count={serien.length}>
<div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(130px,1fr))',gap:12}}>
{serien.map(m => <MovieCard key={'t'+m.id} movie={m} favIds={favIds} hasXrel={xrelIds?.has(m.id)} onToggleFav={onToggle} onOpenModal={onOpenModal}/>)}
</div>
</CollapsibleSection>
)}
</div>
);
}
function SucheGrid({ onOpenModal, xrelIds, onXrelLoaded }) {
const [query, setQuery] = useState('');
const [results, setResults] = useState(null); // null=noch nicht gesucht
@@ -465,26 +506,7 @@ function SucheGrid({ onOpenModal, xrelIds, onXrelLoaded }) {
{results !== null && results.length > 0 && (() => {
const films = results.filter(m => m.media_type !== 'tv');
const serien = results.filter(m => m.media_type === 'tv');
return (
<div>
{films.length > 0 && (
<div style={{marginBottom:20}}>
<div style={{color:'rgba(255,255,255,0.4)',fontFamily:'monospace',fontSize:10,letterSpacing:1,marginBottom:8}}>🎬 FILME ({films.length})</div>
<div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(130px,1fr))',gap:12}}>
{films.map(m => <MovieCard key={'m'+m.id} movie={m} favIds={favIds} hasXrel={xrelIds?.has(m.id)} onToggleFav={onToggle} onOpenModal={onOpenModal}/>)}
</div>
</div>
)}
{serien.length > 0 && (
<div>
<div style={{color:'rgba(255,255,255,0.4)',fontFamily:'monospace',fontSize:10,letterSpacing:1,marginBottom:8}}>📺 SERIEN ({serien.length})</div>
<div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(130px,1fr))',gap:12}}>
{serien.map(m => <MovieCard key={'t'+m.id} movie={m} favIds={favIds} hasXrel={xrelIds?.has(m.id)} onToggleFav={onToggle} onOpenModal={onOpenModal}/>)}
</div>
</div>
)}
</div>
);
return <SectionedGrid films={films} serien={serien} favIds={favIds} xrelIds={xrelIds} onToggle={onToggle} onOpenModal={onOpenModal}/>;
})()}
</div>
);
@@ -700,45 +722,38 @@ function FavoritenKalender({ onOpenModal }) {
)}
</button>
)}
{/* Filme */}
{filmeFavs.length > 0 && (
<div style={{color:'rgba(255,255,255,0.5)',fontFamily:'monospace',fontSize:11,fontWeight:700,letterSpacing:1,
borderBottom:'1px solid rgba(255,255,255,0.07)',paddingBottom:6}}>
🎬 FILME ({filmeFavs.length})
</div>
)}
<CollapsibleSection label="🎬 FILME" count={filmeFavs.length}>
{Object.entries(filmGroups).sort(([a],[b]) => a.localeCompare(b)).map(([key, group]) => (
<div key={'film-'+key}>
<div style={{ ...S.head, marginBottom:10, display:'flex', alignItems:'center', gap:8 }}>
<div key={'film-'+key} style={{marginBottom:12}}>
<div style={{...S.head,marginBottom:6,display:'flex',alignItems:'center',gap:6}}>
<span>{group.label.toUpperCase()}</span>
<div style={{flex:1,height:1,background:'rgba(255,255,255,0.07)'}}/>
<span style={{ fontSize:9 }}>{group.items.length} Film{group.items.length!==1?'e':''}</span>
<span style={{fontSize:9}}>{group.items.length}×</span>
</div>
<div style={{ display:'flex', flexDirection:'column', gap:8 }}>
<div style={{display:'flex',flexDirection:'column',gap:6}}>
{group.items.map(f => <FavCard key={f.tmdb_id} f={f} onOpenModal={onOpenModal} onRemove={removeFav}/>)}
</div>
</div>
))}
{/* Serien */}
{serienFavs.length > 0 && (
<div style={{color:'rgba(255,255,255,0.5)',fontFamily:'monospace',fontSize:11,fontWeight:700,letterSpacing:1,
borderBottom:'1px solid rgba(255,255,255,0.07)',paddingBottom:6,marginTop:8}}>
📺 SERIEN ({serienFavs.length})
</div>
</CollapsibleSection>
)}
{serienFavs.length > 0 && (
<CollapsibleSection label="📺 SERIEN" count={serienFavs.length}>
{Object.entries(serienGroups).sort(([a],[b]) => a.localeCompare(b)).map(([key, group]) => (
<div key={'serie-'+key}>
<div style={{ ...S.head, marginBottom:10, display:'flex', alignItems:'center', gap:8 }}>
<div key={'serie-'+key} style={{marginBottom:12}}>
<div style={{...S.head,marginBottom:6,display:'flex',alignItems:'center',gap:6}}>
<span>{group.label.toUpperCase()}</span>
<div style={{flex:1,height:1,background:'rgba(255,255,255,0.07)'}}/>
<span style={{ fontSize:9 }}>{group.items.length} Serie{group.items.length!==1?'n':''}</span>
<span style={{fontSize:9}}>{group.items.length}×</span>
</div>
<div style={{ display:'flex', flexDirection:'column', gap:8 }}>
<div style={{display:'flex',flexDirection:'column',gap:6}}>
{group.items.map(f => <FavCard key={f.tmdb_id} f={f} onOpenModal={onOpenModal} onRemove={removeFav}/>)}
</div>
</div>
))}
{/* Alte groups-Schleife entfernt DUMMY damit Code nicht bricht */}
</CollapsibleSection>
)}
{false && Object.entries({}).map(([key, group]) => (
<div key={key}>
<div style={{ ...S.head, marginBottom:10, display:'flex', alignItems:'center', gap:8 }}>
@@ -813,7 +828,8 @@ function MovieModal({ tmdbId, mediaType='movie', onClose, mobile, onHasXrel, noY
setDetail(d);
detailRef.current = d;
setLoading(false);
// Gecachte Releases laden sobald wir den Titel kennen
// Gecachte Releases laden (nur für Filme)
if (mediaType !== 'tv') {
const params = new URLSearchParams({
title: d.title || '',
orig: d.original_title || '',
@@ -826,12 +842,13 @@ function MovieModal({ tmdbId, mediaType='movie', onClose, mobile, onHasXrel, noY
if (r.xrel?.length > 0) { setXrel(r.xrel); onHasXrel?.(tmdbId); }
})
.catch(() => {});
}
})
.catch(e => { setError(e.message); setLoading(false); });
const prev = document.body.style.overflow;
document.body.style.overflow = 'hidden';
return () => { document.body.style.overflow = prev; };
}, [tmdbId]);
}, [tmdbId, mediaType]);
const searchXrel = async (forceRefresh = false) => {
const d = detailRef.current;
@@ -931,7 +948,9 @@ function MovieModal({ tmdbId, mediaType='movie', onClose, mobile, onHasXrel, noY
)}
<div style={{ display:'flex', gap:5, flexWrap:'wrap', marginBottom:8 }}>
{detail.vote_average > 0 && <Badge bg="rgba(255,230,109,0.15)" color="#ffe66d">★ {detail.vote_average.toFixed(1)}</Badge>}
{detail.runtime > 0 && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.7)">⏱ {detail.runtime} Min</Badge>}
{detail.media_type !== 'tv' && detail.runtime > 0 && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.7)">⏱ {detail.runtime} Min</Badge>}
{detail.media_type === 'tv' && detail.number_of_seasons > 0 && <Badge bg="rgba(96,165,250,0.15)" color="#60a5fa">📺 {detail.number_of_seasons} Staffel{detail.number_of_seasons!==1?'n':''}</Badge>}
{detail.media_type === 'tv' && detail.number_of_episodes > 0 && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.7)">{detail.number_of_episodes} Ep.</Badge>}
{fskStyle && <Badge bg={fskStyle.bg} color={fskStyle.color}>FSK {detail.fsk}</Badge>}
{detail.release_date && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.55)">🇩🇪 {fmtDE(detail.release_date)}</Badge>}
{detail.countries?.length > 0 && <Badge bg="rgba(255,255,255,0.07)" color="rgba(255,255,255,0.4)">🌍 {detail.countries.slice(0,2).join(' · ')}</Badge>}