diff --git a/web/index.html b/web/index.html index ffd5f61..d915b03 100644 --- a/web/index.html +++ b/web/index.html @@ -499,14 +499,20 @@ - library + + root + + ("#series-actions"); const statusEl = must("#series-status"); const seasonsList = must("#rows-seasons"); + const rootSelect = must("#series-root"); const remove = must("#series-remove"); const removeWrap = must("#series-remove-panel"); @@ -3431,6 +3433,49 @@ function seriesMain(tvDeck: TvReleasesView, views: HideableView[]): SeriesView { } } + function paintRootControl() { + const current = series; + if (!current) { + return; + } + rootSelect.replaceChildren(); + for (const root of roots.filter((candidate) => candidate.kind === "tv")) { + const option = document.createElement("option"); + option.value = String(root.id); + option.textContent = `${root.audience} · ${root.policy_name}`; + rootSelect.append(option); + } + if (rootSelect.value !== String(current.root_id)) { + const option = document.createElement("option"); + option.value = String(current.root_id); + option.textContent = `root ${current.root_id}`; + rootSelect.append(option); + } + rootSelect.value = String(current.root_id); + } + + rootSelect.addEventListener("change", () => { + const current = series; + if (!current) { + return; + } + const next = Number(rootSelect.value); + if (!Number.isInteger(next) || next === current.root_id) { + return; + } + rootSelect.disabled = true; + void updateSeries(current.id, { root_id: next }).then((outcome) => { + rootSelect.disabled = false; + if (outcome.kind === "error") { + rootSelect.value = String(current.root_id); + setStatus(`root change failed — ${outcome.detail}`, "fault"); + return; + } + focusKey = "series-root"; + void load(); + }); + }); + /* ---- §9.6 rich detail: one request, images hotlinked ---- */ function clearRichDetail() { @@ -3984,6 +4029,7 @@ function seriesMain(tvDeck: TvReleasesView, views: HideableView[]): SeriesView { : [], ); paintHeader(); + paintRootControl(); clearRichDetail(); renderSeasons(); syncRefreshWatch(ticket); diff --git a/web/src/series.ts b/web/src/series.ts index 1e0e58e..701fa36 100644 --- a/web/src/series.ts +++ b/web/src/series.ts @@ -72,6 +72,26 @@ export async function fetchSeries(seriesId: number): Promise { } } +/** Move a series to another TV root through its partial update endpoint. */ +export async function updateSeries( + seriesId: number, + patch: { root_id: number }, +): Promise { + try { + const response = await fetch(`/api/series/${seriesId}`, { + method: "PATCH", + headers: { "content-type": "application/json" }, + body: JSON.stringify(patch), + }); + if (!response.ok) { + return { kind: "error", detail: await errorDetail(response) }; + } + return { kind: "done" }; + } catch { + return { kind: "error", detail: "daemon unreachable" }; + } +} + /** One episode by row id — the `/episodes/{id}/releases` deep link's lookup. */ export async function fetchEpisode(episodeId: number): Promise { try {