🔧 Library page table pagination
This commit is contained in:
@@ -411,10 +411,18 @@ pre {
|
||||
}
|
||||
// Library
|
||||
.sticky {
|
||||
position: sticky;
|
||||
top: 57px;
|
||||
z-index: 2;
|
||||
background: #fffffc;
|
||||
position: sticky;
|
||||
top: 107px;
|
||||
z-index: 5;
|
||||
|
||||
.title {
|
||||
background: #fffffc;
|
||||
position: relative;
|
||||
top: -56px;
|
||||
z-index: 99999;
|
||||
padding-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.library {
|
||||
@@ -423,7 +431,7 @@ pre {
|
||||
width: 100%;
|
||||
thead {
|
||||
position: sticky;
|
||||
top: 146px;
|
||||
top: 176px;
|
||||
z-index: 1;
|
||||
background: #fffffc;
|
||||
min-height: 130px;
|
||||
|
||||
@@ -20,7 +20,7 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
getWeeklyPullList({
|
||||
startDate: "2022-9-9",
|
||||
startDate: "2022-10-9",
|
||||
pageSize: "15",
|
||||
currentPage: "1",
|
||||
}),
|
||||
|
||||
@@ -1,22 +1,169 @@
|
||||
import React, { useMemo, ReactElement, useCallback } from "react";
|
||||
import React, { useMemo, ReactElement, useCallback, useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import T2Table from "../shared/T2Table";
|
||||
import { isEmpty, isNil, isUndefined } from "lodash";
|
||||
import MetadataPanel from "../shared/MetadataPanel";
|
||||
import SearchBar from "./SearchBar";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { searchIssue } from "../../actions/fileops.actions";
|
||||
import ellipsize from "ellipsize";
|
||||
|
||||
interface IComicBookLibraryProps {
|
||||
data: {
|
||||
searchResults: any;
|
||||
};
|
||||
}
|
||||
|
||||
export const Library = (data: IComicBookLibraryProps): ReactElement => {
|
||||
const { searchResults } = data.data;
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
useReactTable,
|
||||
PaginationState,
|
||||
} from "@tanstack/react-table";
|
||||
|
||||
|
||||
export const Library = (): ReactElement => {
|
||||
const searchResults = useSelector(
|
||||
(state: RootState) => state.fileOps.libraryComics,
|
||||
);
|
||||
const searchError = useSelector(
|
||||
(state: RootState) => state.fileOps.librarySearchError,
|
||||
);
|
||||
const dispatch = useDispatch();
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
searchIssue(
|
||||
{
|
||||
query: {},
|
||||
},
|
||||
{
|
||||
pagination: {
|
||||
size: 25,
|
||||
from: 0,
|
||||
},
|
||||
type: "all",
|
||||
trigger: "libraryPage"
|
||||
},
|
||||
),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const T2Table = (tableOptions): ReactElement => {
|
||||
const { columns, totalPages, rowClickHandler } =
|
||||
tableOptions;
|
||||
|
||||
|
||||
// pagination methods
|
||||
const goToNextPage = useCallback((pageIndex, pageSize) => {
|
||||
dispatch(
|
||||
searchIssue(
|
||||
{
|
||||
query: {},
|
||||
},
|
||||
{
|
||||
pagination: {
|
||||
size: pageSize,
|
||||
from: pageSize * pageIndex + 1,
|
||||
},
|
||||
type: "all",
|
||||
trigger: "libraryPage",
|
||||
},
|
||||
),
|
||||
);
|
||||
}, []);
|
||||
|
||||
|
||||
const goToPreviousPage = useCallback((pageIndex, pageSize) => {
|
||||
let from = 0;
|
||||
if (pageIndex === 2) {
|
||||
from = (pageIndex - 1) * pageSize + 2 - 27;
|
||||
} else {
|
||||
from = (pageIndex - 1) * pageSize + 2 - 26;
|
||||
}
|
||||
dispatch(
|
||||
searchIssue(
|
||||
{
|
||||
query: {},
|
||||
},
|
||||
{
|
||||
pagination: {
|
||||
size: pageSize,
|
||||
from,
|
||||
},
|
||||
type: "all",
|
||||
trigger: "libraryPage"
|
||||
},
|
||||
),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const table = useReactTable({
|
||||
data: searchResults.hits.hits,
|
||||
columns,
|
||||
manualPagination: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
pageCount: totalPages,
|
||||
// getPaginationRowModel: getPaginationRowModel(),
|
||||
|
||||
debugTable: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<table className="table is-hoverable">
|
||||
<thead>
|
||||
{table.getHeaderGroups().map((headerGroup, idx) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header, idx) => (
|
||||
<th
|
||||
key={header.id}
|
||||
colSpan={header.colSpan}
|
||||
>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map((row, idx) => {
|
||||
return (
|
||||
<tr
|
||||
key={row.id}
|
||||
onClick={() => rowClickHandler(row)}
|
||||
>
|
||||
{row.getVisibleCells().map(cell => (
|
||||
<td key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{/* pagination control */}
|
||||
<nav className="pagination">
|
||||
{table.getState().pagination.pageIndex + 1}
|
||||
<div className="field has-addons">
|
||||
<p className="control">
|
||||
<div className="button" onClick={() => goToNextPage(table.getState().pagination.pageIndex + 1, 25)}> Next Page </div>
|
||||
</p>
|
||||
<p className="control">
|
||||
<div className="button" > Previous Page</div>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// programatically navigate to comic detail
|
||||
const navigate = useNavigate();
|
||||
@@ -65,124 +212,81 @@ export const Library = (data: IComicBookLibraryProps): ReactElement => {
|
||||
const WantedStatus = ({ value }) => {
|
||||
return !value ? <span className="tag is-info is-light">Wanted</span> : null;
|
||||
};
|
||||
const columns = [
|
||||
{
|
||||
header: "Comic Metadata",
|
||||
footer: 1,
|
||||
columns: [
|
||||
{
|
||||
header: "File Details",
|
||||
id: "fileDetails",
|
||||
minWidth: 400,
|
||||
accessorKey: "_source",
|
||||
cell: info => {
|
||||
return <MetadataPanel data={info.getValue()} />;
|
||||
},
|
||||
const columns = [
|
||||
{
|
||||
header: "Comic Metadata",
|
||||
footer: 1,
|
||||
columns: [
|
||||
{
|
||||
header: "File Details",
|
||||
id: "fileDetails",
|
||||
minWidth: 400,
|
||||
accessorKey: "_source",
|
||||
cell: info => {
|
||||
return <MetadataPanel data={info.getValue()} />;
|
||||
},
|
||||
{
|
||||
header: "ComicInfo.xml",
|
||||
accessorKey: "_source.sourcedMetadata.comicInfo",
|
||||
align: "center",
|
||||
minWidth: 250,
|
||||
cell: info =>
|
||||
!isEmpty(info.getValue()) ? (
|
||||
<ComicInfoXML data={info.getValue()} />
|
||||
) : (
|
||||
<span className="tag">No ComicInfo.xml</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: "ComicInfo.xml",
|
||||
accessorKey: "_source.sourcedMetadata.comicInfo",
|
||||
align: "center",
|
||||
minWidth: 250,
|
||||
cell: info =>
|
||||
!isEmpty(info.getValue()) ? (
|
||||
<ComicInfoXML data={info.getValue()} />
|
||||
) : (
|
||||
<span className="tag">No ComicInfo.xml</span>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
header: "Additional Metadata",
|
||||
columns: [
|
||||
{
|
||||
header: "Publisher",
|
||||
accessorKey:
|
||||
"_source.sourcedMetadata.comicvine.volumeInformation",
|
||||
cell: info => {
|
||||
return (
|
||||
!isNil(info.getValue()) && (
|
||||
<h6 className="is-size-7 has-text-weight-bold">
|
||||
{info.getValue().publisher.name}
|
||||
</h6>
|
||||
)
|
||||
);
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
header: "Additional Metadata",
|
||||
columns: [
|
||||
{
|
||||
header: "Publisher",
|
||||
accessorKey:
|
||||
"_source.sourcedMetadata.comicvine.volumeInformation",
|
||||
cell: info => {
|
||||
return (
|
||||
!isNil(info.getValue()) && (
|
||||
<h6 className="is-size-7 has-text-weight-bold">
|
||||
{ info.getValue().publisher.name }
|
||||
</h6>
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Something",
|
||||
accessorKey: "_source.acquisition.source.wanted",
|
||||
cell: info => {
|
||||
!isUndefined(info.getValue()) ?
|
||||
},
|
||||
{
|
||||
header: "Something",
|
||||
accessorKey: "_source.acquisition.source.wanted",
|
||||
cell: info => {
|
||||
!isUndefined(info.getValue()) ?
|
||||
<WantedStatus value={info.getValue().toString()} /> : "Nothing";
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
// ImportStatus.propTypes = {
|
||||
// value: PropTypes.bool.isRequired,
|
||||
// };
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const goToNextPage = useCallback((pageIndex, pageSize) => {
|
||||
dispatch(
|
||||
searchIssue(
|
||||
{
|
||||
query: {},
|
||||
},
|
||||
{
|
||||
pagination: {
|
||||
size: pageSize,
|
||||
from: pageSize * pageIndex + 1,
|
||||
},
|
||||
type: "all",
|
||||
},
|
||||
),
|
||||
);
|
||||
}, []);
|
||||
|
||||
const goToPreviousPage = useCallback((pageIndex, pageSize) => {
|
||||
let from = 0;
|
||||
if (pageIndex === 2) {
|
||||
from = (pageIndex - 1) * pageSize + 2 - 27;
|
||||
} else {
|
||||
from = (pageIndex - 1) * pageSize + 2 - 26;
|
||||
}
|
||||
dispatch(
|
||||
searchIssue(
|
||||
{
|
||||
query: {},
|
||||
},
|
||||
{
|
||||
pagination: {
|
||||
size: pageSize,
|
||||
from,
|
||||
},
|
||||
type: "all",
|
||||
},
|
||||
),
|
||||
);
|
||||
}, []);
|
||||
return (
|
||||
<section className="container">
|
||||
<div className="section">
|
||||
<h1 className="title">Library</h1>
|
||||
<div className="sticky"><h1 className="title">Library</h1></div>
|
||||
{/* Search bar */}
|
||||
<SearchBar />
|
||||
{!isUndefined(searchResults) && (
|
||||
{!isUndefined(searchResults.hits) && (
|
||||
<div>
|
||||
<div className="library">
|
||||
<T2Table
|
||||
rowData={searchResults.hits.hits}
|
||||
totalPages={searchResults.hits.total.value}
|
||||
columns={columns}
|
||||
paginationHandlers={{
|
||||
nextPage: goToNextPage,
|
||||
previousPage: goToPreviousPage,
|
||||
}}
|
||||
|
||||
rowClickHandler={navigateToComicDetail}
|
||||
/>
|
||||
{/* pagination controls */}
|
||||
|
||||
@@ -5,59 +5,58 @@ import { searchIssue } from "../../actions/fileops.actions";
|
||||
import { Library } from "./Library";
|
||||
|
||||
const LibraryContainer = (): ReactElement => {
|
||||
const dispatch = useDispatch();
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
searchIssue(
|
||||
{
|
||||
query: {},
|
||||
},
|
||||
{
|
||||
pagination: {
|
||||
size: 25,
|
||||
from: 0,
|
||||
},
|
||||
type: "all",
|
||||
trigger: "libraryPage"
|
||||
},
|
||||
),
|
||||
);
|
||||
}, []);
|
||||
// const dispatch = useDispatch();
|
||||
// useEffect(() => {
|
||||
// dispatch(
|
||||
// searchIssue(
|
||||
// {
|
||||
// query: {},
|
||||
// },
|
||||
// {
|
||||
// pagination: {
|
||||
// size: 25,
|
||||
// from: 0,
|
||||
// },
|
||||
// type: "all",
|
||||
// trigger: "libraryPage"
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }, []);
|
||||
|
||||
const searchResults = useSelector(
|
||||
(state: RootState) => state.fileOps.libraryComics,
|
||||
);
|
||||
const searchError = useSelector(
|
||||
(state: RootState) => state.fileOps.librarySearchError,
|
||||
);
|
||||
// const searchResults = useSelector(
|
||||
// (state: RootState) => state.fileOps.libraryComics,
|
||||
// );
|
||||
// const searchError = useSelector(
|
||||
// (state: RootState) => state.fileOps.librarySearchError,
|
||||
// );
|
||||
|
||||
return !isEmpty(searchResults) ? (
|
||||
<Library data={{ searchResults }} />
|
||||
) : (
|
||||
<div className="container">
|
||||
<section className="section is-small">
|
||||
<div className="columns">
|
||||
<div className="column is-two-thirds">
|
||||
<article className="message is-link">
|
||||
<div className="message-body">
|
||||
No comics were found in the library, Elasticsearch reports no
|
||||
indices. Try importing a few comics into the library and come
|
||||
back.
|
||||
</div>
|
||||
</article>
|
||||
<pre>
|
||||
{!isUndefined(searchError.data) &&
|
||||
JSON.stringify(
|
||||
searchError.data.meta.body.error.root_cause,
|
||||
null,
|
||||
4,
|
||||
)}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Library />)
|
||||
// : (
|
||||
// <div className="container">
|
||||
// <section className="section is-small">
|
||||
// <div className="columns">
|
||||
// <div className="column is-two-thirds">
|
||||
// <article className="message is-link">
|
||||
// <div className="message-body">
|
||||
// No comics were found in the library, and Elasticsearch doesn't have any
|
||||
// indices. Try resetting the library from <code>Settings > Flush DB & Temporary Folders</code> and then import your library again.
|
||||
// </div>
|
||||
// </article>
|
||||
// <pre>
|
||||
// {!isUndefined(searchError.data) &&
|
||||
// JSON.stringify(
|
||||
// searchError.data.meta.body.error.root_cause,
|
||||
// null,
|
||||
// 4,
|
||||
// )}
|
||||
// </pre>
|
||||
// </div>
|
||||
// </div>
|
||||
// </section>
|
||||
// </div>
|
||||
// );
|
||||
};
|
||||
|
||||
export default LibraryContainer;
|
||||
|
||||
@@ -26,14 +26,14 @@ export const SearchBar = (): ReactElement => {
|
||||
);
|
||||
}, []);
|
||||
return (
|
||||
<div className="box sticky">
|
||||
<div className="sticky">
|
||||
<Form
|
||||
onSubmit={handleSubmit}
|
||||
initialValues={{}}
|
||||
render={({ handleSubmit, form, submitting, pristine, values }) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="field is-grouped">
|
||||
<div className="control is-expanded search">
|
||||
<div className="control search">
|
||||
<Field name="search">
|
||||
{({ input, meta }) => {
|
||||
return (
|
||||
|
||||
@@ -77,14 +77,9 @@ export const MetadataPanel = (props: IMetadatPanelProps): ReactElement => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div className="control">
|
||||
<div className="tags has-addons">
|
||||
<span className="tag is-light is-success">Path</span>
|
||||
<span className="tag is-warning is-light">{rawFileDetails.containedIn}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
),
|
||||
@@ -218,7 +213,7 @@ export const MetadataPanel = (props: IMetadatPanelProps): ReactElement => {
|
||||
orientation={"vertical"}
|
||||
hasDetails={false}
|
||||
imageStyle={props.imageStyle}
|
||||
// cardContainerStyle={{ maxWidth: 200 }}
|
||||
// cardContainerStyle={{ maxWidth: 200 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="column">{metadataPanel.content()}</div>
|
||||
|
||||
@@ -4,22 +4,39 @@ import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
useReactTable,
|
||||
PaginationState,
|
||||
} from "@tanstack/react-table";
|
||||
|
||||
export const T2Table = (tableOptions): ReactElement => {
|
||||
const { rowData, columns, paginationHandlers, totalPages, rowClickHandler } =
|
||||
const { rowData, columns, paginationHandlers: { nextPage, previousPage }, totalPages, rowClickHandler } =
|
||||
tableOptions;
|
||||
const [isPageSizeDropdownCollapsed, collapsePageSizeDropdown] =
|
||||
useState(false);
|
||||
const togglePageSizeDropdown = () =>
|
||||
collapsePageSizeDropdown(!isPageSizeDropdownCollapsed);
|
||||
|
||||
const table = useReactTable({
|
||||
data: rowData,
|
||||
columns,
|
||||
manualPagination: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
pageCount: totalPages,
|
||||
// getPaginationRowModel: getPaginationRowModel(),
|
||||
});
|
||||
|
||||
const [{ pageIndex, pageSize }, setPagination] =
|
||||
React.useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
})
|
||||
|
||||
const pagination = React.useMemo(
|
||||
() => ({
|
||||
pageIndex,
|
||||
pageSize,
|
||||
}),
|
||||
[pageIndex, pageSize]
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<table className="table is-hoverable">
|
||||
@@ -62,6 +79,12 @@ export const T2Table = (tableOptions): ReactElement => {
|
||||
</table>
|
||||
|
||||
{/* pagination control */}
|
||||
<nav className="pagination">
|
||||
{table.getState().pagination.pageIndex + 1}
|
||||
|
||||
<div className="button" onClick={() => table.nextPage()}> Next Page </div>
|
||||
<div className="button" onClick={previousPage}> Previous Page</div>
|
||||
</nav>
|
||||
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -188,6 +188,7 @@ function fileOpsReducer(state = initialState, action) {
|
||||
}
|
||||
|
||||
case SS_SEARCH_RESULTS_FETCHED: {
|
||||
console.log(action.data);
|
||||
return {
|
||||
...state,
|
||||
libraryComics: action.data,
|
||||
@@ -196,7 +197,6 @@ function fileOpsReducer(state = initialState, action) {
|
||||
}
|
||||
case SS_SEARCH_RESULTS_FETCHED_SPECIAL: {
|
||||
const foo = [];
|
||||
console.log(action.data.hits)
|
||||
if (!isUndefined(action.data.hits)) {
|
||||
map(action.data.hits.hits, ({ _source }) => {
|
||||
foo.push(_source);
|
||||
|
||||
Reference in New Issue
Block a user