diff --git a/apps/backoffice/app/(app)/libraries/[id]/series/[name]/page.tsx b/apps/backoffice/app/(app)/libraries/[id]/series/[name]/page.tsx
index f5d3143..aab9c71 100644
--- a/apps/backoffice/app/(app)/libraries/[id]/series/[name]/page.tsx
+++ b/apps/backoffice/app/(app)/libraries/[id]/series/[name]/page.tsx
@@ -1,4 +1,4 @@
-import { fetchLibraries, fetchBooks, fetchSeriesMetadata, getBookCoverUrl, getMetadataLink, getMissingBooks, getReadingStatusLink, BookDto, SeriesMetadataDto, ExternalMetadataLinkDto, MissingBooksDto, AnilistSeriesLinkDto } from "@/lib/api";
+import { fetchLibraries, fetchBooks, fetchSeriesMetadata, getBookCoverUrl, getMetadataLink, getMissingBooks, getReadingStatusLink, apiFetch, BookDto, SeriesMetadataDto, ExternalMetadataLinkDto, MissingBooksDto, AnilistSeriesLinkDto } from "@/lib/api";
import { BooksGrid, EmptyState } from "@/app/components/BookCard";
import { MarkSeriesReadButton } from "@/app/components/MarkSeriesReadButton";
import { MarkBookReadButton } from "@/app/components/MarkBookReadButton";
@@ -41,7 +41,7 @@ export default async function SeriesDetailPage({
const seriesName = decodeURIComponent(name);
- const [library, booksPage, seriesMeta, metadataLinks, readingStatusLink] = await Promise.all([
+ const [library, booksPage, seriesMeta, metadataLinks, readingStatusLink, prowlarrConfigured, qbConfigured] = await Promise.all([
fetchLibraries().then((libs) => libs.find((l) => l.id === id)),
fetchBooks(id, seriesName, page, limit).catch(() => ({
items: [] as BookDto[],
@@ -52,6 +52,12 @@ export default async function SeriesDetailPage({
fetchSeriesMetadata(id, seriesName).catch(() => null as SeriesMetadataDto | null),
getMetadataLink(id, seriesName).catch(() => [] as ExternalMetadataLinkDto[]),
getReadingStatusLink(id, seriesName).catch(() => null as AnilistSeriesLinkDto | null),
+ apiFetch<{ api_key?: string }>("/settings/prowlarr")
+ .then(d => !!(d?.api_key?.trim()))
+ .catch(() => false),
+ apiFetch<{ url?: string; username?: string }>("/settings/qbittorrent")
+ .then(d => !!(d?.url?.trim() && d?.username?.trim()))
+ .catch(() => false),
]);
const existingLink = metadataLinks.find((l) => l.status === "approved") ?? metadataLinks[0] ?? null;
@@ -235,6 +241,8 @@ export default async function SeriesDetailPage({
(null);
+ const [isConfigured, setIsConfigured] = useState(initialProwlarrConfigured ?? null);
const [isSearching, setIsSearching] = useState(false);
const [results, setResults] = useState([]);
const [query, setQuery] = useState("");
const [error, setError] = useState(null);
// qBittorrent state
- const [isQbConfigured, setIsQbConfigured] = useState(false);
+ const [isQbConfigured, setIsQbConfigured] = useState(initialQbConfigured ?? false);
const [sendingGuid, setSendingGuid] = useState(null);
const [sentGuids, setSentGuids] = useState>(new Set());
const [sendError, setSendError] = useState(null);
- // Check if Prowlarr and qBittorrent are configured on mount
+ // Check if Prowlarr and qBittorrent are configured on mount (skip if server provided)
useEffect(() => {
- fetch("/api/settings/prowlarr")
- .then((r) => (r.ok ? r.json() : null))
- .then((data) => {
- setIsConfigured(!!(data && data.api_key && data.api_key.trim()));
- })
- .catch(() => setIsConfigured(false));
- fetch("/api/settings/qbittorrent")
- .then((r) => (r.ok ? r.json() : null))
- .then((data) => {
- setIsQbConfigured(!!(data && data.url && data.url.trim() && data.username && data.username.trim()));
- })
- .catch(() => setIsQbConfigured(false));
- }, []);
+ if (initialProwlarrConfigured !== undefined && initialQbConfigured !== undefined) return;
+ if (initialProwlarrConfigured === undefined) {
+ fetch("/api/settings/prowlarr")
+ .then((r) => (r.ok ? r.json() : null))
+ .then((data) => {
+ setIsConfigured(!!(data && data.api_key && data.api_key.trim()));
+ })
+ .catch(() => setIsConfigured(false));
+ }
+ if (initialQbConfigured === undefined) {
+ fetch("/api/settings/qbittorrent")
+ .then((r) => (r.ok ? r.json() : null))
+ .then((data) => {
+ setIsQbConfigured(!!(data && data.url && data.url.trim() && data.username && data.username.trim()));
+ })
+ .catch(() => setIsQbConfigured(false));
+ }
+ }, [initialProwlarrConfigured, initialQbConfigured]);
const [searchInput, setSearchInput] = useState(`"${seriesName}"`);