🔧 Fixed table pagination controls and counts

This commit is contained in:
2021-08-16 22:11:44 -07:00
parent d92b2246cb
commit afdff65b6b
5 changed files with 141 additions and 99 deletions

View File

@@ -137,15 +137,18 @@ $border-color: red;
// Library // Library
.library { .library {
table { table {
td { tr{
border: 0 none; td {
.card { border: 0 none;
margin: 8px 0 7px 0; .card {
.name { margin: 8px 0 7px 0;
margin: 0 0 4px 0; .name {
margin: 0 0 4px 0;
}
} }
} }
}
}
tbody { tbody {
padding: 10px 0 0 0; padding: 10px 0 0 0;
} }

View File

@@ -65,14 +65,15 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
return { __html: html }; return { __html: html };
}; };
const isComicBookMetadataAvailable = const isComicBookMetadataAvailable =
!isNil(comicBookDetailData.sourcedMetadata) && comicBookDetailData.sourcedMetadata &&
!isUndefined(comicBookDetailData.sourcedMetadata.comicvine) &&
!isEmpty(comicBookDetailData.sourcedMetadata); !isEmpty(comicBookDetailData.sourcedMetadata);
// Tab groups for ComicVine metadata // Tab groups for ComicVine metadata
const tabGroup = [ const tabGroup = [
{ {
id: 0, id: 0,
name: "Volume Information", name: "Volume Information",
content: isComicBookMetadataAvailable && ( content: isComicBookMetadataAvailable ? (
<> <>
<div className="columns"> <div className="columns">
<div className="column is-narrow"> <div className="column is-narrow">
@@ -126,7 +127,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
></div> ></div>
</div> </div>
</> </>
), ) : null,
}, },
{ {
id: 1, id: 1,
@@ -249,7 +250,7 @@ export const ComicDetail = ({}: ComicDetailProps): ReactElement => {
</div> </div>
</div> </div>
<MetadataTabGroup /> {isComicBookMetadataAvailable ? <MetadataTabGroup /> : null}
<Drawer <Drawer
title="ComicVine Search Results" title="ComicVine Search Results"

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect, useMemo, ReactElement } from "react"; import React, { useState, useEffect, useMemo, ReactElement } from "react";
import PropTypes from 'prop-types'; import PropTypes from "prop-types";
import { useHistory } from "react-router";
import { import {
removeLeadingPeriod, removeLeadingPeriod,
escapePoundSymbol, escapePoundSymbol,
@@ -16,27 +17,68 @@ interface IComicBookLibraryProps {
} }
export const Library = ({}: IComicBookLibraryProps): ReactElement => { export const Library = ({}: IComicBookLibraryProps): ReactElement => {
const [comicPage, setComicPage] = useState(1);
const [isPageSizeDropdownCollapsed, collapsePageSizeDropdown] = const [isPageSizeDropdownCollapsed, collapsePageSizeDropdown] =
useState(false); useState(false);
const dispatch = useDispatch();
useEffect(() => {
dispatch(
getComicBooks({
paginationOptions: {
page: comicPage,
limit: 15,
},
}),
);
}, [comicPage, dispatch]);
const data = useSelector( const data = useSelector(
(state: RootState) => state.fileOps.recentComics.docs, (state: RootState) => state.fileOps.recentComics.docs,
); );
const pageTotal = useSelector(
(state: RootState) => state.fileOps.recentComics.totalDocs,
);
const togglePageSizeDropdown = () => const togglePageSizeDropdown = () =>
collapsePageSizeDropdown(!isPageSizeDropdownCollapsed); collapsePageSizeDropdown(!isPageSizeDropdownCollapsed);
// programatically navigate to comic detail
const history = useHistory();
const navigateToComicDetail = (id) => {
history.push(`/comic/details/${id}`);
};
// raw file details
const RawFileDetails = ({ value }) => {
const encodedFilePath = encodeURI(
"http://localhost:3000" + removeLeadingPeriod(value.path),
);
const filePath = escapePoundSymbol(encodedFilePath);
return (
<div className="card-container">
<div className="card">
<div className="is-horizontal">
<div className="card-image">
<figure>
<img className="image" src={filePath} />
</figure>
</div>
<ul className="card-content">
<li className="name has-text-weight-medium">
{ellipsize(value.name, 18)}
</li>
<li>
<div className="control">
<div className="tags has-addons">
<span className="tag is-primary is-light">
{value.extension}
</span>
<span className="tag is-info is-light">
{prettyBytes(value.fileSize)}
</span>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
);
};
const ImportStatus = ({ value }) => {
return `${value.toString()}` ? (
<span className="tag is-info is-light">Imported</span>
) : (
"Not Imported"
);
};
const columns = useMemo( const columns = useMemo(
() => [ () => [
{ {
@@ -45,55 +87,12 @@ export const Library = ({}: IComicBookLibraryProps): ReactElement => {
{ {
Header: "File Details", Header: "File Details",
accessor: "rawFileDetails", accessor: "rawFileDetails",
// eslint-disable-next-line react/display-name Cell: RawFileDetails,
Cell(props) {
const encodedFilePath = encodeURI(
"http://localhost:3000" +
removeLeadingPeriod(props.cell.value.path),
);
const filePath = escapePoundSymbol(encodedFilePath);
return (
<div className="card-container">
<div className="card">
<div className="is-horizontal">
<div className="card-image">
<figure>
<img className="image" src={filePath} />
</figure>
</div>
<ul className="card-content">
<li className="name has-text-weight-medium">
{ellipsize(props.cell.value.name, 18)}
</li>
<li>
<div className="control">
<div className="tags has-addons">
<span className="tag is-primary is-light">
{props.cell.value.extension}
</span>
<span className="tag is-info is-light">
{prettyBytes(props.cell.value.fileSize)}
</span>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
);
},
}, },
{ {
Header: "Import Status", Header: "Import Status",
accessor: "importStatus.isImported", accessor: "importStatus.isImported",
Cell(props) { Cell: ImportStatus,
return `${props.cell.value.toString()}` ? (
<span className="tag is-info is-light">Imported</span>
) : (
"Not Imported"
);
},
}, },
], ],
}, },
@@ -165,29 +164,57 @@ export const Library = ({}: IComicBookLibraryProps): ReactElement => {
[], [],
); );
columns[0].columns[0].Cell.propTypes = { RawFileDetails.propTypes = {
value: PropTypes.object.isRequired, name: PropTypes.string,
path: PropTypes.string,
fileSize: PropTypes.number,
extension: PropTypes.string,
}; };
ImportStatus.propTypes = {
value: PropTypes.bool.isRequired,
};
const { const {
getTableProps, getTableProps,
getTableBodyProps, getTableBodyProps,
headerGroups, headerGroups,
page,
prepareRow, prepareRow,
pageOptions, page,
pageCount,
state: { pageIndex, pageSize },
gotoPage,
previousPage,
nextPage,
setPageSize,
canPreviousPage, canPreviousPage,
canNextPage, canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state: { pageIndex, pageSize },
} = useTable( } = useTable(
{ columns, data, initialState: { pageIndex: 0 } }, {
columns,
data,
manualPagination: true,
initialState: {
pageIndex: 1,
pageSize: 15,
},
pageCount: pageTotal,
},
usePagination, usePagination,
); );
const dispatch = useDispatch();
useEffect(() => {
dispatch(
getComicBooks({
paginationOptions: {
page: pageIndex,
limit: pageSize,
},
}),
);
}, [pageIndex, pageSize]);
const comicBookLibraryItems = React.useMemo(() => {}); const comicBookLibraryItems = React.useMemo(() => {});
return ( return (
@@ -216,7 +243,11 @@ export const Library = ({}: IComicBookLibraryProps): ReactElement => {
{page.map((row, idx) => { {page.map((row, idx) => {
prepareRow(row); prepareRow(row);
return ( return (
<tr key={idx} {...row.getRowProps()}> <tr
key={idx}
{...row.getRowProps()}
onClick={() => navigateToComicDetail(row.original._id)}
>
{row.cells.map((cell, idx) => { {row.cells.map((cell, idx) => {
return ( return (
<td <td
@@ -240,7 +271,8 @@ export const Library = ({}: IComicBookLibraryProps): ReactElement => {
aria-label="pagination" aria-label="pagination"
> >
<div> <div>
Page {pageIndex + 1} of {pageOptions.length} Page {pageIndex} of {Math.ceil(pageTotal / pageSize)}
(Total resources: {pageTotal})
</div> </div>
<div className="field has-addons"> <div className="field has-addons">
<p className="control"> <p className="control">
@@ -288,11 +320,9 @@ export const Library = ({}: IComicBookLibraryProps): ReactElement => {
<input <input
type="number" type="number"
className="input" className="input"
defaultValue={pageIndex + 1} defaultValue={pageIndex}
onChange={(e) => { onChange={(e) => {
const page = e.target.value const page = e.target.value ? Number(e.target.value) : 0;
? Number(e.target.value) - 1
: 0;
gotoPage(page); gotoPage(page);
}} }}
style={{ width: "100px" }} style={{ width: "100px" }}

View File

@@ -59,7 +59,7 @@ export const Search = ({}: ISearchProps): ReactElement => {
onSubmit={() => onSubmit={() =>
getDCPPSearchResults({ getDCPPSearchResults({
query: { query: {
pattern: "secret wars", pattern: "wolverine",
// file_type: "compressed", // file_type: "compressed",
extensions: ["cbz", "cbr"], extensions: ["cbz", "cbr"],
}, },

View File

@@ -2304,15 +2304,15 @@ aggregate-error@^3.0.0:
clean-stack "^2.0.0" clean-stack "^2.0.0"
indent-string "^4.0.0" indent-string "^4.0.0"
airdcpp-apisocket@^2.4.1: airdcpp-apisocket@^2.4.2:
version "2.4.1" version "2.4.2"
resolved "https://registry.yarnpkg.com/airdcpp-apisocket/-/airdcpp-apisocket-2.4.1.tgz#eb2dc28eecd9d6306b25ef8239545d90d6ad005f" resolved "https://registry.yarnpkg.com/airdcpp-apisocket/-/airdcpp-apisocket-2.4.2.tgz#884d02ff7768cb452cd92e1083c05f618b892fc0"
integrity sha512-3mkEvbChG4aRe8F2ZFTi2Z0FG319toex/HquRxWlo6s6Rg69s5bPo0VwGte3NlZ4fnHoBD9vk5KavHgOMkJgZg== integrity sha512-OxrrYe/iNOfle4RhqgaoUNJrgc7LDWCK+WhMQyfprDRxRqkvgPDuWjOavlmf89cAMIgwJ7x5DESpJKWvNGaS/w==
dependencies: dependencies:
chalk "^4.1.0" chalk "^4.1.2"
events "^3.2.0" events "^3.3.0"
invariant "^2.2.4" invariant "^2.2.4"
is-in-browser "^1.1.3" is-in-browser "^2.0.0"
promise "^8.1.0" promise "^8.1.0"
ajv-errors@^1.0.0: ajv-errors@^1.0.0:
@@ -3661,6 +3661,14 @@ chalk@^4.0.0, chalk@^4.1.0:
ansi-styles "^4.1.0" ansi-styles "^4.1.0"
supports-color "^7.1.0" supports-color "^7.1.0"
chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
char-regex@^1.0.2: char-regex@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
@@ -5450,7 +5458,7 @@ eventemitter3@^4.0.0:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
events@^3.2.0: events@^3.2.0, events@^3.3.0:
version "3.3.0" version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
@@ -7281,10 +7289,10 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
dependencies: dependencies:
is-extglob "^2.1.1" is-extglob "^2.1.1"
is-in-browser@^1.1.3: is-in-browser@^2.0.0:
version "1.1.3" version "2.0.0"
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-2.0.0.tgz#a2343a18d8f8a600e8a20cb3022183a251e30355"
integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= integrity sha512-/NUv5pqj+krUJalhGpj0lyy+x7vrD9jt1PlAfkoIDEXqE+xZgFJ4FU8e9m99WuHbCqsBZVf+nzvAjNso+SO80A==
is-installed-globally@^0.1.0: is-installed-globally@^0.1.0:
version "0.1.0" version "0.1.0"