🔧 Fixed table pagination controls and counts
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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" }}
|
||||||
|
|||||||
@@ -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"],
|
||||||
},
|
},
|
||||||
|
|||||||
32
yarn.lock
32
yarn.lock
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user