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