diff --git a/k-tv-frontend/app/(main)/library/components/library-grid.tsx b/k-tv-frontend/app/(main)/library/components/library-grid.tsx index 1486d0f..3a60712 100644 --- a/k-tv-frontend/app/(main)/library/components/library-grid.tsx +++ b/k-tv-frontend/app/(main)/library/components/library-grid.tsx @@ -1,10 +1,18 @@ "use client"; +import { useLibraryShows } from "@/hooks/use-library-shows"; +import { useLibrarySeasons } from "@/hooks/use-library-seasons"; import { LibraryItemCard } from "./library-item-card"; +import { ShowTile } from "./show-tile"; +import { SeasonTile } from "./season-tile"; +import { BreadcrumbNav } from "./breadcrumb-nav"; import { ScheduleFromLibraryDialog } from "./schedule-from-library-dialog"; import { AddToBlockDialog } from "./add-to-block-dialog"; import { Button } from "@/components/ui/button"; -import type { LibraryItemFull } from "@/lib/types"; +import type { LibraryItemFull, ShowSummary } from "@/lib/types"; +import type { LibrarySearchParams } from "@/hooks/use-library-search"; + +type Drilldown = null | { series: string } | { series: string; season: number }; interface Props { items: LibraryItemFull[]; @@ -16,36 +24,142 @@ interface Props { onToggleSelect: (id: string) => void; onPageChange: (page: number) => void; selectedItems: LibraryItemFull[]; + viewMode: "grouped" | "flat"; + drilldown: Drilldown; + onDrilldown: (next: Drilldown) => void; + filter: LibrarySearchParams; + selectedShows: ShowSummary[]; + selectedShowNames: Set; + onToggleSelectShow: (show: ShowSummary) => void; } export function LibraryGrid({ items, total, page, pageSize, isLoading, selected, onToggleSelect, onPageChange, selectedItems, + viewMode, drilldown, onDrilldown, filter, + selectedShows, selectedShowNames, onToggleSelectShow, }: Props) { const totalPages = Math.ceil(total / pageSize); + // Hooks for grouped mode (called unconditionally per React rules) + const showsFilter = { + q: filter.q, + genres: filter.genres, + provider: filter.provider, + }; + const { data: showsData, isLoading: showsLoading } = useLibraryShows(showsFilter); + const seasonsSeries = (viewMode === "grouped" && drilldown !== null && !("season" in drilldown)) + ? drilldown.series + : null; + const { data: seasonsData, isLoading: seasonsLoading } = useLibrarySeasons( + seasonsSeries, + filter.provider, + ); + + const isGroupedTopLevel = viewMode === "grouped" && drilldown === null; + const isSeasonLevel = viewMode === "grouped" && drilldown !== null && !("season" in drilldown); + const isEpisodeLevel = viewMode === "flat" || (viewMode === "grouped" && drilldown !== null && "season" in drilldown); + + function renderContent() { + if (isGroupedTopLevel) { + const shows = showsData ?? []; + const nonEpisodes = items.filter(i => i.content_type !== "episode"); + const loading = showsLoading; + + if (loading && shows.length === 0 && nonEpisodes.length === 0) { + return

Loading…

; + } + if (shows.length === 0 && nonEpisodes.length === 0) { + return

No items found. Run a library sync to populate the library.

; + } + return ( +
+ {shows.map(show => ( + onToggleSelectShow(show)} + onClick={() => onDrilldown({ series: show.series_name })} + /> + ))} + {nonEpisodes.map(item => ( + onToggleSelect(item.id)} + /> + ))} +
+ ); + } + + if (isSeasonLevel && drilldown) { + const seasons = seasonsData ?? []; + if (seasonsLoading && seasons.length === 0) { + return

Loading…

; + } + if (seasons.length === 0) { + return

No seasons found.

; + } + return ( +
+ {seasons.map(season => ( + {}} + onClick={() => onDrilldown({ series: drilldown.series, season: season.season_number })} + /> + ))} +
+ ); + } + + // Flat mode or episode-level drilldown + if (isLoading) { + return

Loading…

; + } + if (items.length === 0) { + return

No items found. Run a library sync to populate the library.

; + } + return ( +
+ {items.map(item => ( + onToggleSelect(item.id)} + /> + ))} +
+ ); + } + + const showPagination = isEpisodeLevel && totalPages > 1; + const totalSelected = selected.size + selectedShows.length; + return (
- {isLoading ? ( -

Loading…

- ) : items.length === 0 ? ( -

No items found. Run a library sync to populate the library.

- ) : ( -
- {items.map(item => ( - onToggleSelect(item.id)} - /> - ))} -
+ {drilldown && ( + { + if (target === "root") onDrilldown(null); + else if (target === "series" && drilldown && "series" in drilldown) + onDrilldown({ series: drilldown.series }); + }} + /> )} + {renderContent()}
- {totalPages > 1 && ( + {showPagination && (

{total.toLocaleString()} items total

@@ -56,11 +170,11 @@ export function LibraryGrid({
)} - {selected.size > 0 && ( + {totalSelected > 0 && (
- {selected.size} selected - - + {totalSelected} selected + + {selected.size > 0 && }
)}