import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { Check, Plus } from "lucide-react"; import { api } from "../lib/api"; import Modal from "./Modal"; import { useConfirm } from "./ConfirmProvider"; // Admin-only "manage this movie's collections" dialog (Collections P2). Lists the library's EDITABLE // collections with an in/out toggle (add/remove the movie, written back to Plex), plus a field to // create a new collection seeded with this movie. Read-only collections (smart / IMDb-TMDb auto-lists) // never appear here. Opened from the movie info page. export default function PlexCollectionEditor({ item, library, memberOf, onClose, onChanged, }: { item: { id: string; title: string }; library: string; memberOf: string[]; // collection ids this movie is already in onClose: () => void; onChanged: () => void; }) { const { t } = useTranslation(); const qc = useQueryClient(); const confirm = useConfirm(); const [members, setMembers] = useState>(() => new Set(memberOf)); const [q, setQ] = useState(""); const [newName, setNewName] = useState(""); const [busy, setBusy] = useState(null); // collection id currently mutating const listQ = useQuery({ queryKey: ["plex-collections", library], queryFn: () => api.plexCollections(library), }); const editable = useMemo( () => (listQ.data?.collections ?? []).filter((c) => c.can_edit), [listQ.data], ); // Existing Plex collections you could take over (plain, non-smart, non-auto) but haven't marked yet. const eligible = useMemo( () => (listQ.data?.collections ?? []).filter((c) => !c.can_edit && !c.smart && c.source === "collection"), [listQ.data], ); const term = q.trim().toLowerCase(); const shown = term ? editable.filter((c) => c.title.toLowerCase().includes(term)) : editable; const refresh = () => { qc.invalidateQueries({ queryKey: ["plex-collections"] }); qc.invalidateQueries({ queryKey: ["plex-item", item.id] }); qc.invalidateQueries({ queryKey: ["plex-browse"] }); onChanged(); }; async function toggle(id: string) { if (busy) return; setBusy(id); const inIt = members.has(id); try { if (inIt) await api.plexCollectionRemoveItem(id, item.id); else await api.plexCollectionAddItem(id, item.id); setMembers((prev) => { const next = new Set(prev); if (inIt) next.delete(id); else next.add(id); return next; }); refresh(); } finally { setBusy(null); } } async function markEditable(id: string) { if (busy) return; setBusy(id); try { await api.plexSetCollectionEditable(id, true); refresh(); } finally { setBusy(null); } } async function createNew() { const title = newName.trim(); if (!title || busy) return; setBusy("__new__"); try { const col = await api.plexCreateCollection(library, title, item.id); setNewName(""); setMembers((prev) => new Set(prev).add(col.id)); refresh(); } finally { setBusy(null); } } async function removeCollection(id: string, title: string) { if (!(await confirm({ message: t("plex.collEditor.deleteConfirm", { title }), danger: true }))) return; setBusy(id); try { await api.plexDeleteCollection(id); setMembers((prev) => { const next = new Set(prev); next.delete(id); return next; }); refresh(); } finally { setBusy(null); } } return ( {/* Create new */}
setNewName(e.target.value)} onKeyDown={(e) => e.key === "Enter" && createNew()} placeholder={t("plex.collEditor.newPlaceholder")} className="glass-card min-w-0 flex-1 rounded-lg px-3 py-2 text-sm outline-none focus:border-accent" />
{/* Filter */} {editable.length > 6 && ( setQ(e.target.value)} placeholder={t("plex.collEditor.search")} className="glass-card mb-2 w-full rounded-lg px-3 py-1.5 text-sm outline-none focus:border-accent" /> )} {/* Editable collections with in/out toggle */}
{listQ.isLoading ? (

{t("plex.loading")}

) : shown.length === 0 ? (

{t("plex.collEditor.none")}

) : ( shown.map((c) => { const inIt = members.has(c.id); return (
); }) )}
{/* Take over an existing plain Plex collection (mark it "mine to edit"). */} {eligible.length > 0 && (
{t("plex.collEditor.others", { count: eligible.length })}
{eligible.map((c) => (
{c.title} {t("plex.collEditor.count", { count: c.child_count ?? 0 })}
))}
)}

{t("plex.collEditor.note")}

); }