mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-13 16:40:01 +00:00
Add sorting to tables in LibraryManagementView
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
Pagination,
|
||||
Select,
|
||||
SelectItem,
|
||||
SortDescriptor,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
@@ -36,6 +37,7 @@ export default function LibraryManagementGames({library}: LibraryManagementGames
|
||||
const games = state.gamesByLibraryId[library.id] ? state.gamesByLibraryId[library.id] as GameDto[] : [];
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [filter, setFilter] = useState<"all" | "confirmed" | "nonConfirmed">("all");
|
||||
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({column: "title", direction: "ascending"});
|
||||
|
||||
const [selectedGame, setSelectedGame] = useState<GameDto>(games[0]);
|
||||
const editGameModal = useDisclosure();
|
||||
@@ -46,12 +48,41 @@ export default function LibraryManagementGames({library}: LibraryManagementGames
|
||||
return Math.ceil(getFilteredGames().length / rowsPerPage);
|
||||
}, [games, filter]);
|
||||
|
||||
const items = useMemo(() => {
|
||||
const filteredItems = useMemo(() => {
|
||||
return getFilteredGames();
|
||||
}, [games, filter, searchTerm]);
|
||||
|
||||
const sortedItems = useMemo(() => {
|
||||
return filteredItems.slice().sort((a, b) => {
|
||||
let cmp: number;
|
||||
|
||||
switch (sortDescriptor.column) {
|
||||
case "title":
|
||||
cmp = a.title.localeCompare(b.title);
|
||||
break;
|
||||
case "addedToLibrary":
|
||||
cmp = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
||||
break;
|
||||
case "downloadCount":
|
||||
cmp = a.metadata.downloadCount - b.metadata.downloadCount;
|
||||
break;
|
||||
default:
|
||||
return 0; // No sorting if the column is not recognized
|
||||
}
|
||||
|
||||
if (sortDescriptor.direction === "descending") {
|
||||
cmp *= -1; // Reverse the comparison if sorting in descending order
|
||||
}
|
||||
|
||||
return cmp;
|
||||
});
|
||||
}, [filteredItems, sortDescriptor]);
|
||||
|
||||
const pagedItems = useMemo(() => {
|
||||
const start = (page - 1) * rowsPerPage;
|
||||
const end = start + rowsPerPage;
|
||||
|
||||
return getFilteredGames().slice(start, end);
|
||||
}, [page, games, filter, searchTerm]);
|
||||
return sortedItems.slice(start, end);
|
||||
}, [page, sortedItems]);
|
||||
|
||||
|
||||
function getFilteredGames() {
|
||||
@@ -107,9 +138,11 @@ export default function LibraryManagementGames({library}: LibraryManagementGames
|
||||
</Select>
|
||||
</div>
|
||||
<Table removeWrapper isStriped
|
||||
sortDescriptor={sortDescriptor}
|
||||
onSortChange={setSortDescriptor}
|
||||
bottomContent={
|
||||
<div className="flex w-full justify-center sticky">
|
||||
{items.length > 0 &&
|
||||
{pagedItems.length > 0 &&
|
||||
<Pagination
|
||||
isCompact
|
||||
showControls
|
||||
@@ -122,14 +155,14 @@ export default function LibraryManagementGames({library}: LibraryManagementGames
|
||||
</div>
|
||||
}>
|
||||
<TableHeader>
|
||||
<TableColumn allowsSorting>Game</TableColumn>
|
||||
<TableColumn allowsSorting>Added to library</TableColumn>
|
||||
<TableColumn allowsSorting>Download count</TableColumn>
|
||||
<TableColumn key="title" allowsSorting>Game</TableColumn>
|
||||
<TableColumn key="addedToLibrary" allowsSorting>Added to library</TableColumn>
|
||||
<TableColumn key="downloadCount" allowsSorting>Download count</TableColumn>
|
||||
<TableColumn>Path</TableColumn>
|
||||
{/* width={1} keeps the column as far to the right as possible*/}
|
||||
<TableColumn width={1}>Actions</TableColumn>
|
||||
</TableHeader>
|
||||
<TableBody emptyContent="Your filter did not match any games." items={items}>
|
||||
<TableBody emptyContent="Your filter did not match any games." items={pagedItems}>
|
||||
{(item) => (
|
||||
<TableRow key={item.id}>
|
||||
<TableCell>
|
||||
|
||||
+43
-12
@@ -3,6 +3,7 @@ import {
|
||||
Button,
|
||||
Input,
|
||||
Pagination,
|
||||
SortDescriptor,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
@@ -25,23 +26,45 @@ interface LibraryManagementUnmatchedPathsProps {
|
||||
|
||||
export default function LibraryManagementUnmatchedPaths({library}: LibraryManagementUnmatchedPathsProps) {
|
||||
const matchGameModal = useDisclosure();
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [page, setPage] = useState(1);
|
||||
const rowsPerPage = 25;
|
||||
|
||||
const filteredItems = useMemo(() => {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [selectedPath, setSelectedPath] = useState(library.unmatchedPaths ? library.unmatchedPaths[0] : null);
|
||||
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({column: "path", direction: "ascending"});
|
||||
|
||||
const pages = useMemo(() => {
|
||||
return Math.ceil(getFilteredPaths().length / rowsPerPage);
|
||||
}, [library.unmatchedPaths, searchTerm]);
|
||||
|
||||
const filteredPaths = useMemo(() => {
|
||||
return library.unmatchedPaths!
|
||||
.filter((path) => path.toLowerCase().includes(searchTerm.toLowerCase()))
|
||||
.map((path) => ({key: hashCode(path), path}));
|
||||
}, [searchTerm, library]);
|
||||
}, [library, searchTerm]);
|
||||
|
||||
const pages = useMemo(() => Math.ceil(filteredItems.length / rowsPerPage), [filteredItems]);
|
||||
const items = useMemo(() => {
|
||||
const sortedPaths = useMemo(() => {
|
||||
return filteredPaths.slice().sort((a, b) => {
|
||||
let cmp: number;
|
||||
switch (sortDescriptor.column) {
|
||||
case "path":
|
||||
cmp = a.path.localeCompare(b.path);
|
||||
break;
|
||||
default:
|
||||
cmp = 0;
|
||||
}
|
||||
if (sortDescriptor.direction === "descending") {
|
||||
cmp *= -1;
|
||||
}
|
||||
return cmp;
|
||||
});
|
||||
}, [filteredPaths, sortDescriptor]);
|
||||
|
||||
const pagedPaths = useMemo(() => {
|
||||
const start = (page - 1) * rowsPerPage;
|
||||
return filteredItems.slice(start, start + rowsPerPage);
|
||||
}, [page, filteredItems]);
|
||||
|
||||
const [selectedPath, setSelectedPath] = useState(library.unmatchedPaths ? library.unmatchedPaths[0] : null);
|
||||
const end = start + rowsPerPage;
|
||||
return sortedPaths.slice(start, end);
|
||||
}, [page, sortedPaths]);
|
||||
|
||||
async function deleteUnmatchedPath(unmatchedPath: string) {
|
||||
const libraryUpdateDto: LibraryUpdateDto = {
|
||||
@@ -51,6 +74,12 @@ export default function LibraryManagementUnmatchedPaths({library}: LibraryManage
|
||||
await LibraryEndpoint.updateLibrary(libraryUpdateDto);
|
||||
}
|
||||
|
||||
function getFilteredPaths() {
|
||||
return library.unmatchedPaths!!.filter((path) =>
|
||||
path.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
return <div className="flex flex-col gap-4">
|
||||
<h1 className="text-2xl font-bold">Manage unmatched paths</h1>
|
||||
<Input
|
||||
@@ -62,9 +91,11 @@ export default function LibraryManagementUnmatchedPaths({library}: LibraryManage
|
||||
onClear={() => setSearchTerm("")}
|
||||
/>
|
||||
<Table removeWrapper isStriped isHeaderSticky
|
||||
sortDescriptor={sortDescriptor}
|
||||
onSortChange={setSortDescriptor}
|
||||
bottomContent={
|
||||
<div className="flex w-full justify-center">
|
||||
{items.length > 0 &&
|
||||
{pagedPaths.length > 0 &&
|
||||
<Pagination
|
||||
isCompact
|
||||
showControls
|
||||
@@ -77,10 +108,10 @@ export default function LibraryManagementUnmatchedPaths({library}: LibraryManage
|
||||
</div>
|
||||
}>
|
||||
<TableHeader>
|
||||
<TableColumn allowsSorting>Path</TableColumn>
|
||||
<TableColumn key="path" allowsSorting>Path</TableColumn>
|
||||
<TableColumn width={1}>Actions</TableColumn>
|
||||
</TableHeader>
|
||||
<TableBody emptyContent="This library has no unmatched paths." items={items}>
|
||||
<TableBody emptyContent="This library has no unmatched paths." items={pagedPaths}>
|
||||
{(item) => (
|
||||
<TableRow key={item.key}>
|
||||
<TableCell>
|
||||
|
||||
Reference in New Issue
Block a user