feat(frontend): library page, components, and schedule/add-to-block dialogs (tasks 11-14)
This commit is contained in:
68
k-tv-frontend/app/(main)/library/components/library-grid.tsx
Normal file
68
k-tv-frontend/app/(main)/library/components/library-grid.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
"use client";
|
||||
|
||||
import { LibraryItemCard } from "./library-item-card";
|
||||
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";
|
||||
|
||||
interface Props {
|
||||
items: LibraryItemFull[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
isLoading: boolean;
|
||||
selected: Set<string>;
|
||||
onToggleSelect: (id: string) => void;
|
||||
onPageChange: (page: number) => void;
|
||||
selectedItems: LibraryItemFull[];
|
||||
}
|
||||
|
||||
export function LibraryGrid({
|
||||
items, total, page, pageSize, isLoading,
|
||||
selected, onToggleSelect, onPageChange, selectedItems,
|
||||
}: Props) {
|
||||
const totalPages = Math.ceil(total / pageSize);
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col min-h-0">
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
{isLoading ? (
|
||||
<p className="text-sm text-zinc-500">Loading…</p>
|
||||
) : items.length === 0 ? (
|
||||
<p className="text-sm text-zinc-500">No items found. Run a library sync to populate the library.</p>
|
||||
) : (
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">
|
||||
{items.map(item => (
|
||||
<LibraryItemCard
|
||||
key={item.id}
|
||||
item={item}
|
||||
selected={selected.has(item.id)}
|
||||
onToggle={() => onToggleSelect(item.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{totalPages > 1 && (
|
||||
<div className="flex items-center justify-between border-t border-zinc-800 px-6 py-3">
|
||||
<p className="text-xs text-zinc-500">{total.toLocaleString()} items total</p>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" variant="outline" disabled={page === 0} onClick={() => onPageChange(page - 1)}>Prev</Button>
|
||||
<span className="flex items-center text-xs text-zinc-400">{page + 1} / {totalPages}</span>
|
||||
<Button size="sm" variant="outline" disabled={page >= totalPages - 1} onClick={() => onPageChange(page + 1)}>Next</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selected.size > 0 && (
|
||||
<div className="fixed bottom-6 left-1/2 -translate-x-1/2 flex items-center gap-3 rounded-full border border-zinc-700 bg-zinc-900 px-6 py-3 shadow-2xl">
|
||||
<span className="text-sm text-zinc-300">{selected.size} selected</span>
|
||||
<ScheduleFromLibraryDialog selectedItems={selectedItems} />
|
||||
<AddToBlockDialog selectedItems={selectedItems} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user