From c469550fa0ba6a9e86cc55066a3a46aa08613447 Mon Sep 17 00:00:00 2001 From: zZebrahz Date: Sun, 7 Jun 2026 09:51:45 -0700 Subject: [PATCH] fix(libraries): collapse admin library diagnostics (#61) * Fix troubleshooting visibility on admin libraries * Remove metadata matcher badge from admin libraries * test(libraries): cover hidden matcher queue badge --- web/src/pages/AdminLibraries.test.tsx | 32 ++- web/src/pages/AdminLibraries.tsx | 298 ++++++++++++-------------- 2 files changed, 163 insertions(+), 167 deletions(-) diff --git a/web/src/pages/AdminLibraries.test.tsx b/web/src/pages/AdminLibraries.test.tsx index 1a3b458a..01116045 100644 --- a/web/src/pages/AdminLibraries.test.tsx +++ b/web/src/pages/AdminLibraries.test.tsx @@ -220,7 +220,7 @@ describe("AdminLibraries", () => { expect(markup).toContain("Scanner roots that stay visible"); }); - it("surfaces pending metadata matcher queue work", () => { + it("does not show metadata matcher queue counts in the library status", () => { mocks.useLibraryMetadataMatchQueues.mockReturnValue({ data: [ { @@ -236,7 +236,8 @@ describe("AdminLibraries", () => { const markup = renderPage(); - expect(markup).toContain("3 matching"); + expect(markup).toContain("Enabled"); + expect(markup).not.toContain("3 matching"); expect(markup).not.toContain("View backlog"); }); @@ -337,6 +338,33 @@ describe("AdminLibraries", () => { expect(markup).toContain("Items that could not be matched to any metadata provider."); }); + it("renders the Troubleshooting section collapsed by default when skipped roots exist", () => { + mocks.useSkippedLibraryRoots.mockReturnValue({ + data: [ + { + library_id: 1, + library_name: "Movies", + root_path: "/media/movies/Unknown Movie", + reason: "missing_provider_ids", + file_count: 2, + sample_file_path: "/media/movies/Unknown Movie/movie.mkv", + first_seen_at: "2026-03-23T20:00:00Z", + last_seen_at: "2026-03-23T21:00:00Z", + }, + ], + isLoading: false, + }); + + const markup = renderPage(); + + expect(markup).toContain("Troubleshooting"); + expect(markup).toContain( + "Roots where the inferred canonical folder lacks embedded provider IDs.", + ); + expect(markup).not.toContain("Filter by path, library, or reason"); + expect(markup).not.toContain("Unknown Movie"); + }); + it("hides unmatched items section when no unmatched items exist", () => { const markup = renderPage(); diff --git a/web/src/pages/AdminLibraries.tsx b/web/src/pages/AdminLibraries.tsx index 780d6830..14170626 100644 --- a/web/src/pages/AdminLibraries.tsx +++ b/web/src/pages/AdminLibraries.tsx @@ -5,7 +5,6 @@ import { useEventChannel } from "@/components/realtimeEventsContext"; import type { AdminJob, Library, - LibraryMetadataMatchQueueStatus, LibraryMountCheckResponse, LibraryRoot, LibrarySkippedRoot, @@ -30,7 +29,6 @@ import { useRefreshLibraryMetadata, useCancelAdminJob, useConfirmEmptyRootCleanup, - useLibraryMetadataMatchQueues, useUploadLibraryPoster, useDeleteLibraryPoster, useUnmatchedLibraryItems, @@ -133,7 +131,6 @@ export default function AdminLibraries() { const { data: libraries = [], isLoading } = useAdminLibraries(); const { data: activeScans = [] } = useActiveScans(); const { data: libraryRefreshJobs = [] } = useLibraryRefreshJobs(); - const { data: metadataMatchQueues = [] } = useLibraryMetadataMatchQueues(); const { data: skippedRoots = [] } = useSkippedLibraryRoots(); const { data: staleIDs = [] } = useStaleMediaIDs(); const [dialogOpen, setDialogOpen] = useState(false); @@ -226,15 +223,6 @@ export default function AdminLibraries() { } return scansByLibraryID; }, [activeScans]); - const metadataMatchQueueByLibraryId = useMemo(() => { - const queuesByLibraryID = new Map(); - for (const queue of metadataMatchQueues) { - if (queue.total_count > 0) { - queuesByLibraryID.set(queue.library_id, queue); - } - } - return queuesByLibraryID; - }, [metadataMatchQueues]); const activeScanGroups = useMemo(() => { return Array.from(activeScansByLibraryId.entries()) .map(([libraryID, scans]) => { @@ -434,8 +422,6 @@ export default function AdminLibraries() { const isScanning = scanMutation.isPending && scanMutation.variables === lib.id; const activeRefreshJob = activeRefreshJobsByLibraryId.get(lib.id); const activeLibraryScans = activeScansByLibraryId.get(lib.id) ?? []; - const metadataMatchQueue = metadataMatchQueueByLibraryId.get(lib.id); - const hasMetadataMatchQueue = (metadataMatchQueue?.total_count ?? 0) > 0; const runningLibraryScans = activeLibraryScans.filter( (scan) => scan.status === "running", ).length; @@ -487,11 +473,6 @@ export default function AdminLibraries() { {queuedLibraryScans > 0 ? ( {queuedLibraryScans} queued ) : null} - {hasMetadataMatchQueue ? ( - - {metadataMatchQueue?.total_count.toLocaleString()} matching - - ) : null} {lib.scan_warning_code === "empty_root" ? ( Empty root guarded ) : null} @@ -1615,6 +1596,7 @@ function RootOverrideDialog({ type SkippedSortField = "root_path" | "library" | "reason" | "first_seen" | "last_seen"; function SkippedRootsSection({ skippedRoots }: { skippedRoots: LibrarySkippedRoot[] }) { + const [open, setOpen] = useState(false); const [search, setSearch] = useState(""); const [expandedKey, setExpandedKey] = useState(null); const { sortField, sortDir, toggle } = useSort("last_seen", "desc"); @@ -1653,157 +1635,143 @@ function SkippedRootsSection({ skippedRoots }: { skippedRoots: LibrarySkippedRoo const pag = usePagination(sorted); return ( -
-
-
- -
-
-

Troubleshooting

-

- Roots where the inferred canonical folder lacks embedded provider IDs. -

-
- - {skippedRoots.length} - + } + open={open} + onOpenChange={setOpen} + > +
+ + { + setSearch(e.target.value); + pag.setPage(0); + }} + className="h-8 pl-8 text-xs" + />
- -
-
- - { - setSearch(e.target.value); - pag.setPage(0); - }} - className="h-8 pl-8 text-xs" - /> -
-
- - - - - Item - - - Library - - - Reason - - Files - - First seen - - - Last seen - - - - - {pag.rows.map((root) => { - const rowKey = `${root.library_id}:${root.root_path}`; - const isExpanded = expandedKey === rowKey; - return ( - - setExpandedKey(isExpanded ? null : rowKey)} - > - -
- - - {root.root_path.split("/").filter(Boolean).pop()} - +
+
+ + + + Item + + + Library + + + Reason + + Files + + First seen + + + Last seen + + + + + {pag.rows.map((root) => { + const rowKey = `${root.library_id}:${root.root_path}`; + const isExpanded = expandedKey === rowKey; + return ( + + setExpandedKey(isExpanded ? null : rowKey)} + > + +
+ + + {root.root_path.split("/").filter(Boolean).pop()} + +
+
+ {root.library_name} + + + {root.reason} + + + + {root.file_count} + + + {new Date(root.first_seen_at).toLocaleString()} + + + {new Date(root.last_seen_at).toLocaleString()} + +
+ {isExpanded && ( + + +
+ Root path + {root.root_path} + {root.sample_file_path && ( + <> + Sample file + + {root.sample_file_path} + + + )} + Files affected + {root.file_count}
- {root.library_name} - - - {root.reason} - - - - {root.file_count} - - - {new Date(root.first_seen_at).toLocaleString()} - - - {new Date(root.last_seen_at).toLocaleString()} -
- {isExpanded && ( - - -
- Root path - {root.root_path} - {root.sample_file_path && ( - <> - - Sample file - - - {root.sample_file_path} - - - )} - - Files affected - - {root.file_count} -
-
-
- )} -
- ); - })} -
-
-
- + )} + + ); + })} + +
-
+ + ); }