feat: Suchen-Tab mit Filmsuche, Such-Endpoint mit Sprach-/Duplikat-Filter
This commit is contained in:
@@ -9,6 +9,7 @@ const TOOL_DEFS = [
|
||||
{ id: 'kino', label: '🎬 Kino', desc: 'Aktuell im Kino (DE) – alle laufenden Filme', ready: true },
|
||||
{ id: 'demnächst', label: '🗓 Demnächst', desc: 'Neustarts wochenweise – nächste Wochen', ready: true },
|
||||
{ id: 'favoriten', label: '❤ Favoriten', desc: 'Deine gespeicherten Lieblingsfilme', ready: true },
|
||||
{ id: 'suchen', label: '🔍 Suchen', desc: 'Filme suchen und Infos abrufen', ready: true },
|
||||
];
|
||||
|
||||
const getIcon = lbl => [...lbl][0] ?? '🎬';
|
||||
@@ -128,6 +129,7 @@ export default function Media({ toast, mobile }) {
|
||||
{activeTool === 'kino' && <KinoGrid onOpenModal={setModal} xrelIds={xrelIds} onXrelLoaded={ids => setXrelIds(prev => new Set([...prev, ...ids]))} />}
|
||||
{activeTool === 'demnächst' && <DemnächstGrid onOpenModal={setModal} xrelIds={xrelIds} onXrelLoaded={ids => setXrelIds(prev => new Set([...prev, ...ids]))} />}
|
||||
{activeTool === 'favoriten' && <FavoritenKalender onOpenModal={setModal} />}
|
||||
{activeTool === 'suchen' && <SucheGrid onOpenModal={setModal} xrelIds={xrelIds} onXrelLoaded={ids => setXrelIds(prev => new Set([...prev, ...ids]))} />}
|
||||
</div>
|
||||
|
||||
{modal && createPortal(
|
||||
@@ -367,6 +369,93 @@ function DemnächstGrid({ onOpenModal, xrelIds, onXrelLoaded }) {
|
||||
);
|
||||
}
|
||||
|
||||
// ── Suche ────────────────────────────────────────────────────────────────────
|
||||
function SucheGrid({ onOpenModal, xrelIds, onXrelLoaded }) {
|
||||
const [query, setQuery] = useState('');
|
||||
const [results, setResults] = useState(null); // null=noch nicht gesucht
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [favIds, toggleFav] = useFavIds();
|
||||
const inputRef = useRef(null);
|
||||
|
||||
const doSearch = async (q = query) => {
|
||||
const trimmed = q.trim();
|
||||
if (!trimmed || trimmed.length < 2) return;
|
||||
setLoading(true); setError(''); setResults(null);
|
||||
try {
|
||||
const list = await api(`/tools/media/search?q=${encodeURIComponent(trimmed)}`);
|
||||
setResults(list);
|
||||
// xREL-Badges aus Cache
|
||||
if (list.length) {
|
||||
const ids = list.map(m => m.id).join(',');
|
||||
api(`/tools/media/xrel-cached?ids=${ids}`)
|
||||
.then(r => {
|
||||
const hits = Object.entries(r).filter(([,v])=>v).map(([k])=>Number(k));
|
||||
if (hits.length) onXrelLoaded?.(hits);
|
||||
}).catch(()=>{});
|
||||
}
|
||||
} catch(e) { setError(e.message); }
|
||||
finally { setLoading(false); }
|
||||
};
|
||||
|
||||
const onToggle = async (movie, isFav) => {
|
||||
try { await toggleFav(movie, isFav); } catch(e) { alert(e.message); }
|
||||
};
|
||||
|
||||
const onKey = e => { if (e.key === 'Enter') doSearch(); };
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Suchfeld */}
|
||||
<div style={{ display:'flex', gap:8, marginBottom:20 }}>
|
||||
<input
|
||||
ref={inputRef}
|
||||
value={query}
|
||||
onChange={e => setQuery(e.target.value)}
|
||||
onKeyDown={onKey}
|
||||
placeholder="Filmtitel eingeben…"
|
||||
style={{
|
||||
flex:1, background:'rgba(255,255,255,0.06)', border:'1px solid rgba(255,255,255,0.15)',
|
||||
borderRadius:8, padding:'9px 14px', color:'#fff', fontFamily:"'Space Mono',monospace",
|
||||
fontSize:13, outline:'none',
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
onClick={() => doSearch()}
|
||||
disabled={loading || query.trim().length < 2}
|
||||
style={{
|
||||
background:'rgba(78,205,196,0.15)', border:'1px solid rgba(78,205,196,0.3)',
|
||||
color:'#4ecdc4', borderRadius:8, padding:'9px 18px', cursor:'pointer',
|
||||
fontFamily:"'Space Mono',monospace", fontSize:12, fontWeight:700,
|
||||
opacity: loading || query.trim().length < 2 ? 0.5 : 1,
|
||||
}}
|
||||
>
|
||||
{loading ? '⏳' : '🔍 Suchen'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{error && <ErrorBox msg={error} />}
|
||||
|
||||
{results === null && !loading && (
|
||||
<StatusBox>Filmtitel eingeben und Suchen drücken</StatusBox>
|
||||
)}
|
||||
|
||||
{results !== null && results.length === 0 && (
|
||||
<StatusBox>Keine Filme gefunden für „{query}"</StatusBox>
|
||||
)}
|
||||
|
||||
{results !== null && results.length > 0 && (
|
||||
<div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(130px,1fr))', gap:12 }}>
|
||||
{results.map(m => (
|
||||
<MovieCard key={m.id} movie={m} favIds={favIds} hasXrel={xrelIds?.has(m.id)}
|
||||
onToggleFav={onToggle} onOpenModal={onOpenModal} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Favoriten Kalender ────────────────────────────────────────────────────────
|
||||
function FavoritenKalender({ onOpenModal }) {
|
||||
const [favs, setFavs] = useState([]);
|
||||
|
||||
Reference in New Issue
Block a user