163 lines
4.1 KiB
TypeScript
163 lines
4.1 KiB
TypeScript
import { getBooks } from "@/lib/api";
|
|
import { DetailedBook } from "@/lib/book";
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeadCell,
|
|
TableRow,
|
|
} from "flowbite-react";
|
|
|
|
import { useState, useEffect, Dispatch, SetStateAction } from "react";
|
|
|
|
export function BookTable() {
|
|
const [books, setBooks] = useState([] as DetailedBook[]);
|
|
const [nameFilter, setNameFilter] = useState(null as string | null);
|
|
const [catFilter, setCatFilter] = useState([] as string[]);
|
|
|
|
const [isLoading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
getBooks().then((data) => {
|
|
if (!data) {
|
|
return setLoading(false);
|
|
}
|
|
setBooks(data);
|
|
setLoading(false);
|
|
});
|
|
}, []);
|
|
|
|
if (isLoading) return <p>Loading...</p>;
|
|
if (books.length <= 0) return <p>No library data</p>;
|
|
|
|
return (
|
|
<div className="w-full">
|
|
{renderOptions(setNameFilter, setCatFilter)}
|
|
<div className="overflow-x-auto">
|
|
{renderTable(books, nameFilter, catFilter)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
let searchNameTo: NodeJS.Timeout;
|
|
|
|
function renderOptions(
|
|
setNameFilter: Dispatch<SetStateAction<string | null>>,
|
|
setCatFilter: Dispatch<SetStateAction<string[]>>
|
|
) {
|
|
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
setCatFilter; //TODO: implement category selector
|
|
|
|
clearTimeout(searchNameTo);
|
|
searchNameTo = setTimeout(() => {
|
|
setNameFilter(e.target.value || null);
|
|
}, 500);
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<div className="mx-auto max-w-md">
|
|
<label
|
|
htmlFor="default-search"
|
|
className="sr-only mb-2 text-sm font-medium text-gray-900 dark:text-white"
|
|
>
|
|
Search
|
|
</label>
|
|
<div className="relative">
|
|
<div className="pointer-events-none absolute inset-y-0 start-0 flex items-center ps-3">
|
|
<svg
|
|
className="h-4 w-4 text-gray-500 dark:text-gray-400"
|
|
aria-hidden="true"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
fill="none"
|
|
viewBox="0 0 20 20"
|
|
>
|
|
<path
|
|
stroke="currentColor"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth="2"
|
|
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<input
|
|
type="search"
|
|
id="default-search"
|
|
className="block w-full rounded-lg border border-gray-300 bg-gray-50 p-4 ps-10 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500"
|
|
placeholder="Search book titles or authors..."
|
|
onChange={(e) => handleNameChange(e)}
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function renderTable(
|
|
books: DetailedBook[],
|
|
nameFilter: string | null,
|
|
categoryFilter: string[]
|
|
) {
|
|
console.log("namef", nameFilter);
|
|
console.log("catf", categoryFilter);
|
|
|
|
if (nameFilter || categoryFilter.length > 0) {
|
|
books = books.filter((book) => {
|
|
let pass = true;
|
|
|
|
if (categoryFilter.length > 0) {
|
|
if (book.category.length <= 0) return false;
|
|
for (const category of book.category) {
|
|
pass = pass && categoryFilter.includes(category);
|
|
}
|
|
}
|
|
|
|
if (nameFilter) {
|
|
const namePresent =
|
|
book.title.toLowerCase().includes(nameFilter.toLowerCase()) ||
|
|
book.authorsList.findIndex((author) =>
|
|
author.toLowerCase().includes(nameFilter.toLowerCase())
|
|
) >= 0;
|
|
|
|
pass = pass && namePresent;
|
|
}
|
|
return pass;
|
|
});
|
|
}
|
|
|
|
let tableRows = books.map((book) => (
|
|
<TableRow key={book.id} className="hover:bg-zinc-400 hover:text-white">
|
|
<TableCell>{book.title}</TableCell>
|
|
<TableCell>{book.authors}</TableCell>
|
|
<TableCell>{book.category.join(", ")}</TableCell>
|
|
<TableCell>{book.isbn}</TableCell>
|
|
</TableRow>
|
|
));
|
|
|
|
if (tableRows.length <= 0) {
|
|
tableRows = [
|
|
<TableRow key="no-results">
|
|
<TableCell colSpan={4} className="text-center">
|
|
No results
|
|
</TableCell>
|
|
</TableRow>,
|
|
];
|
|
}
|
|
|
|
return (
|
|
<Table>
|
|
<TableHead className="bg-zinc-400">
|
|
<TableHeadCell className="w-2/5 ">Title</TableHeadCell>
|
|
<TableHeadCell>Authors</TableHeadCell>
|
|
<TableHeadCell>Category</TableHeadCell>
|
|
<TableHeadCell>ISBN</TableHeadCell>
|
|
</TableHead>
|
|
<TableBody>{tableRows}</TableBody>
|
|
</Table>
|
|
);
|
|
}
|