diff --git a/apps/admin-ui/src/app/live-lists/page.tsx b/apps/admin-ui/src/app/live-lists/page.tsx index 613e9b4..5cd1bb8 100644 --- a/apps/admin-ui/src/app/live-lists/page.tsx +++ b/apps/admin-ui/src/app/live-lists/page.tsx @@ -38,6 +38,7 @@ type LiveListDetail = { }; type EpgOption = { id: string; name: string }; +type LogoOption = { id: string; name: string; url: string }; type ChannelForm = { name: string; @@ -80,6 +81,8 @@ export default function LiveListsPage() { const [detail, setDetail] = useState(null); const [epgOptions, setEpgOptions] = useState([]); const [epgFilter, setEpgFilter] = useState(""); + const [logoOptions, setLogoOptions] = useState([]); + const [logoFilter, setLogoFilter] = useState(""); const [newListName, setNewListName] = useState(""); const [listName, setListName] = useState(""); const [listEnabled, setListEnabled] = useState(true); @@ -111,6 +114,10 @@ export default function LiveListsPage() { .then((r) => r.json()) .then((d) => setEpgOptions(d.channels ?? [])) .catch(() => undefined); + fetch("/api/v1/admin/live-lists/logo-options", { credentials: "include" }) + .then((r) => r.json()) + .then((d) => setLogoOptions(d.logos ?? [])) + .catch(() => undefined); }, [loadLists]); useEffect(() => { @@ -136,6 +143,24 @@ export default function LiveListsPage() { .slice(0, 200); }, [epgOptions, epgFilter]); + const filteredLogos = useMemo(() => { + const q = logoFilter.trim().toLowerCase(); + if (!q) return logoOptions.slice(0, 80); + return logoOptions + .filter( + (o) => + o.name.toLowerCase().includes(q) || + o.id.toLowerCase().includes(q) || + o.url.toLowerCase().includes(q) + ) + .slice(0, 80); + }, [logoOptions, logoFilter]); + + function pickLogo(logo: LogoOption) { + setChannelForm((f) => ({ ...f, logoUrl: logo.url })); + setLogoFilter(logo.name); + } + async function createList(e: FormEvent) { e.preventDefault(); const name = newListName.trim(); @@ -211,11 +236,16 @@ export default function LiveListsPage() { function startCreateChannel() { setEditingChannelId("new"); setChannelForm(emptyChannelForm()); + setLogoFilter(""); + setEpgFilter(""); } function startEditChannel(c: LiveChannelRow) { setEditingChannelId(c.id); setChannelForm(formFromChannel(c)); + const match = logoOptions.find((o) => o.url === c.logoUrl); + setLogoFilter(match?.name ?? ""); + setEpgFilter(""); } async function saveChannel(e: FormEvent) { @@ -446,14 +476,104 @@ export default function LiveListsPage() { autoComplete="off" /> + + + {filteredLogos.map((o) => ( + + ))} + + {filteredLogos.length > 0 && ( +
+ {filteredLogos.slice(0, 24).map((o) => ( + + ))} +
+ )} + {channelForm.logoUrl.trim() && ( +

+ {/* eslint-disable-next-line @next/next/no-img-element */} + Logo preview + Preview +

+ )}