diff --git a/apps/backoffice/app/(app)/jobs/[id]/page.tsx b/apps/backoffice/app/(app)/jobs/[id]/page.tsx index c4eacae..61b3343 100644 --- a/apps/backoffice/app/(app)/jobs/[id]/page.tsx +++ b/apps/backoffice/app/(app)/jobs/[id]/page.tsx @@ -8,6 +8,7 @@ import { StatusBadge, JobTypeBadge, StatBox, ProgressBar } from "@/app/components/ui"; import { JobDetailLive } from "@/app/components/JobDetailLive"; +import { QbittorrentProvider, QbittorrentDownloadButton } from "@/app/components/QbittorrentDownloadButton"; import { getServerTranslations } from "@/lib/i18n/server"; interface JobDetailPageProps { @@ -984,6 +985,7 @@ export default async function JobDetailPage({ params }: JobDetailPageProps) { {/* Download detection — available releases per series */} {isDownloadDetection && downloadDetectionResults.length > 0 && ( + {t("jobDetail.downloadAvailableReleases")} @@ -1010,7 +1012,7 @@ export default async function JobDetailPage({ params }: JobDetailPageProps) { {r.available_releases && r.available_releases.length > 0 && ( {r.available_releases.map((release, idx) => ( - + {release.title} @@ -1032,6 +1034,9 @@ export default async function JobDetailPage({ params }: JobDetailPageProps) { + {release.download_url && ( + + )} ))} @@ -1040,6 +1045,7 @@ export default async function JobDetailPage({ params }: JobDetailPageProps) { ))} + )} {/* Metadata batch results */} diff --git a/apps/backoffice/app/components/QbittorrentDownloadButton.tsx b/apps/backoffice/app/components/QbittorrentDownloadButton.tsx new file mode 100644 index 0000000..7a64a75 --- /dev/null +++ b/apps/backoffice/app/components/QbittorrentDownloadButton.tsx @@ -0,0 +1,82 @@ +"use client"; + +import { useState, useEffect, createContext, useContext, type ReactNode } from "react"; +import { Icon } from "./ui"; +import { useTranslation } from "@/lib/i18n/context"; + +const QbConfigContext = createContext(false); + +export function QbittorrentProvider({ children }: { children: ReactNode }) { + const [configured, setConfigured] = useState(false); + + useEffect(() => { + fetch("/api/settings/qbittorrent") + .then((r) => (r.ok ? r.json() : null)) + .then((data) => { + setConfigured(!!(data && data.url && data.url.trim() && data.username && data.username.trim())); + }) + .catch(() => setConfigured(false)); + }, []); + + return {children}; +} + +export function QbittorrentDownloadButton({ downloadUrl, releaseId }: { downloadUrl: string; releaseId: string }) { + const { t } = useTranslation(); + const configured = useContext(QbConfigContext); + const [sending, setSending] = useState(false); + const [sent, setSent] = useState(false); + const [error, setError] = useState(null); + + if (!configured) return null; + + async function handleSend() { + setSending(true); + setError(null); + try { + const resp = await fetch("/api/qbittorrent/add", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ url: downloadUrl }), + }); + const data = await resp.json(); + if (data.error) { + setError(data.error); + } else if (data.success) { + setSent(true); + } else { + setError(data.message || t("prowlarr.sentError")); + } + } catch { + setError(t("prowlarr.sentError")); + } finally { + setSending(false); + } + } + + return ( + + {sending ? ( + + ) : sent ? ( + + + + ) : ( + + )} + + ); +}
{release.title}