Files
ripster/frontend/src/components/MetadataSelectionDialog.jsx

173 lines
4.9 KiB
JavaScript

import { useEffect, useMemo, useState } from 'react';
import { Dialog } from 'primereact/dialog';
import { Button } from 'primereact/button';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { InputText } from 'primereact/inputtext';
export default function MetadataSelectionDialog({
visible,
context,
onHide,
onSubmit,
onSearch,
busy
}) {
const [selected, setSelected] = useState(null);
const [query, setQuery] = useState('');
const [manualTitle, setManualTitle] = useState('');
const [manualYear, setManualYear] = useState('');
const [manualImdb, setManualImdb] = useState('');
const [extraResults, setExtraResults] = useState([]);
useEffect(() => {
if (!visible) {
return;
}
const selectedMetadata = context?.selectedMetadata || {};
const defaultTitle = selectedMetadata.title || context?.detectedTitle || '';
const defaultYear = selectedMetadata.year ? String(selectedMetadata.year) : '';
const defaultImdb = selectedMetadata.imdbId || '';
setSelected(null);
setQuery(defaultTitle);
setManualTitle(defaultTitle);
setManualYear(defaultYear);
setManualImdb(defaultImdb);
setExtraResults([]);
}, [visible, context]);
const rows = useMemo(() => {
const base = context?.omdbCandidates || [];
const all = [...base, ...extraResults];
const map = new Map();
all.forEach((item) => {
if (item?.imdbId) {
map.set(item.imdbId, item);
}
});
return Array.from(map.values());
}, [context, extraResults]);
const titleWithPosterBody = (row) => (
<div className="omdb-row">
{row.poster && row.poster !== 'N/A' ? (
<img src={row.poster} alt={row.title} className="poster-thumb-lg" />
) : (
<div className="poster-thumb-lg poster-fallback">-</div>
)}
<div>
<div><strong>{row.title}</strong></div>
<small>{row.year} | {row.imdbId}</small>
</div>
</div>
);
const handleSearch = async () => {
if (!query.trim()) {
return;
}
const results = await onSearch(query.trim());
setExtraResults(results || []);
};
const handleSubmit = async () => {
const payload = selected
? {
jobId: context.jobId,
title: selected.title,
year: selected.year,
imdbId: selected.imdbId,
poster: selected.poster && selected.poster !== 'N/A' ? selected.poster : null,
fromOmdb: true
}
: {
jobId: context.jobId,
title: manualTitle,
year: manualYear,
imdbId: manualImdb,
poster: null,
fromOmdb: false
};
await onSubmit(payload);
};
return (
<Dialog
header="Metadaten auswählen"
visible={visible}
onHide={onHide}
style={{ width: '52rem', maxWidth: '95vw' }}
className="metadata-selection-dialog"
breakpoints={{ '1200px': '92vw', '768px': '96vw', '560px': '98vw' }}
modal
>
<div className="search-row">
<InputText
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder="Titel suchen"
/>
<Button label="OMDb Suche" icon="pi pi-search" onClick={handleSearch} loading={busy} />
</div>
<div className="table-scroll-wrap table-scroll-medium">
<DataTable
value={rows}
selectionMode="single"
selection={selected}
onSelectionChange={(event) => setSelected(event.value)}
dataKey="imdbId"
size="small"
scrollable
scrollHeight="22rem"
emptyMessage="Keine Treffer"
responsiveLayout="stack"
breakpoint="960px"
>
<Column header="Titel" body={titleWithPosterBody} />
<Column field="year" header="Jahr" style={{ width: '8rem' }} />
<Column field="imdbId" header="IMDb" style={{ width: '10rem' }} />
</DataTable>
</div>
<h4>Manuelle Eingabe</h4>
<div className="metadata-grid">
<InputText
value={manualTitle}
onChange={(event) => setManualTitle(event.target.value)}
placeholder="Titel"
disabled={!!selected}
/>
<InputText
value={manualYear}
onChange={(event) => setManualYear(event.target.value)}
placeholder="Jahr"
disabled={!!selected}
/>
<InputText
value={manualImdb}
onChange={(event) => setManualImdb(event.target.value)}
placeholder="IMDb-ID"
disabled={!!selected}
/>
</div>
<div className="dialog-actions">
<Button label="Abbrechen" severity="secondary" text onClick={onHide} />
<Button
label="Auswahl übernehmen"
icon="pi pi-play"
onClick={handleSubmit}
loading={busy}
disabled={!selected && !manualTitle.trim() && !manualImdb.trim()}
/>
</div>
</Dialog>
);
}