🔧 🏗️ Massive tables refactor

Abstracted a table component that can be configured to display issues, volumes or pull list items
This commit is contained in:
2022-11-15 17:22:50 -08:00
parent 1e39daeda2
commit 7babf9f73d
11 changed files with 134 additions and 102 deletions

View File

@@ -32,6 +32,8 @@ import {
SS_SEARCH_FAILED, SS_SEARCH_FAILED,
SS_SEARCH_RESULTS_FETCHED_SPECIAL, SS_SEARCH_RESULTS_FETCHED_SPECIAL,
WANTED_COMICS_FETCHED, WANTED_COMICS_FETCHED,
VOLUMES_FETCHED,
CV_WEEKLY_PULLLIST_FETCHED,
} from "../constants/action-types"; } from "../constants/action-types";
import { success } from "react-notification-system-redux"; import { success } from "react-notification-system-redux";
import { removeLeadingPeriod } from "../shared/utils/formatting.utils"; import { removeLeadingPeriod } from "../shared/utils/formatting.utils";
@@ -316,12 +318,16 @@ export const searchIssue = (query, options) => async (dispatch) => {
data: response.data.body, data: response.data.body,
}); });
break; break;
case "volumesPage":
dispatch({
type: VOLUMES_FETCHED,
data: response.data.body,
});
break;
default: default:
break; break;
} }
}; };
export const analyzeImage = export const analyzeImage =
(imageFilePath: string | Buffer) => async (dispatch) => { (imageFilePath: string | Buffer) => async (dispatch) => {

View File

@@ -3,7 +3,7 @@ import Dashboard from "./Dashboard/Dashboard";
import Import from "./Import"; import Import from "./Import";
import { ComicDetailContainer } from "./ComicDetail/ComicDetailContainer"; import { ComicDetailContainer } from "./ComicDetail/ComicDetailContainer";
import LibraryContainer from "./Library/LibraryContainer"; import TabulatedContentContainer from "./Library/TabulatedContentContainer";
import LibraryGrid from "./Library/LibraryGrid"; import LibraryGrid from "./Library/LibraryGrid";
import Search from "./Search"; import Search from "./Search";
import Settings from "./Settings"; import Settings from "./Settings";
@@ -92,7 +92,7 @@ export const App = (): ReactElement => {
<Routes> <Routes>
<Route path="/" element={<Dashboard />} /> <Route path="/" element={<Dashboard />} />
<Route path="/import" element={<Import path={"./comics"} />} /> <Route path="/import" element={<Import path={"./comics"} />} />
<Route path="/library" element={<LibraryContainer />} /> <Route path="/library" element={<TabulatedContentContainer category="library" />} />
<Route path="/library-grid" element={<LibraryGrid />} /> <Route path="/library-grid" element={<LibraryGrid />} />
<Route path="/downloads" element={<Downloads data={{}} />} /> <Route path="/downloads" element={<Downloads data={{}} />} />
<Route path="/search" element={<Search />} /> <Route path="/search" element={<Search />} />
@@ -105,9 +105,9 @@ export const App = (): ReactElement => {
element={<VolumeDetail />} element={<VolumeDetail />}
/> />
<Route path="/settings" element={<Settings />} /> <Route path="/settings" element={<Settings />} />
<Route path="/pull-list/all" element={<PullList />} /> <Route path="/pull-list/all" element={<TabulatedContentContainer category="pullList" />} />
<Route path="/wanted/all" element={<WantedComics />} /> <Route path="/wanted/all" element={<TabulatedContentContainer category="wanted" />} />
<Route path="/volumes/all" element={<Volumes />} /> <Route path="/volumes/all" element={<TabulatedContentContainer category="volumes" />} />
</Routes> </Routes>
</div> </div>
</AirDCPPSocketContextProvider> </AirDCPPSocketContextProvider>

View File

@@ -20,7 +20,7 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
useEffect(() => { useEffect(() => {
dispatch( dispatch(
getWeeklyPullList({ getWeeklyPullList({
startDate: "2022-11-9", startDate: "2022-11-15",
pageSize: "15", pageSize: "15",
currentPage: "1", currentPage: "1",
}), }),
@@ -127,7 +127,7 @@ export const PullList = ({ issues }: PullListProps): ReactElement => {
<Slider {...settings} ref={(c) => (sliderRef = c)}> <Slider {...settings} ref={(c) => (sliderRef = c)}>
{!isNil(pullList) && {!isNil(pullList) &&
pullList && pullList &&
map(pullList, (issue, idx) => { map(pullList, ({issue}, idx) => {
return ( return (
<Card <Card
key={idx} key={idx}

View File

@@ -81,7 +81,7 @@ export const Library = (): ReactElement => {
const WantedStatus = ({ value }) => { const WantedStatus = ({ value }) => {
return !value ? <span className="tag is-info is-light">Wanted</span> : null; return !value ? <span className="tag is-info is-light">Wanted</span> : null;
}; };
const columns = React.useMemo(() => [ const columns = useMemo(() => [
{ {
header: "Comic Metadata", header: "Comic Metadata",
footer: 1, footer: 1,

View File

@@ -1,39 +1,34 @@
import { isEmpty, isUndefined } from "lodash"; import React, { ReactElement } from "react";
import React, { ReactElement, useEffect } from "react"; import PullList from "../PullList/PullList";
import { useDispatch, useSelector } from "react-redux"; import { Volumes } from "../Volumes/Volumes";
import { searchIssue } from "../../actions/fileops.actions"; import WantedComics from "../WantedComics/WantedComics";
import { Library } from "./Library"; import { Library } from "./Library";
const LibraryContainer = (): ReactElement => { interface ITabulatedContentContainerProps {
// const dispatch = useDispatch(); category: string;
// useEffect(() => { }
// dispatch(
// searchIssue(
// {
// query: {},
// },
// {
// pagination: {
// size: 25,
// from: 0,
// },
// type: "all",
// trigger: "libraryPage"
// },
// ),
// );
// }, []);
// const searchResults = useSelector( const TabulatedContentContainer = (
// (state: RootState) => state.fileOps.libraryComics, props: ITabulatedContentContainerProps,
// ); ): ReactElement => {
// const searchError = useSelector( const { category } = props;
// (state: RootState) => state.fileOps.librarySearchError, const renderTabulatedContent = () => {
// ); switch (category) {
case "library":
return <Library />;
case "pullList":
return <PullList />;
case "wanted":
return <WantedComics />;
case "volumes":
return <Volumes />;
default:
return <></>;
}
};
return ( return renderTabulatedContent();
<Library />) // : (
// : (
// <div className="container"> // <div className="container">
// <section className="section is-small"> // <section className="section is-small">
// <div className="columns"> // <div className="columns">
@@ -59,4 +54,4 @@ const LibraryContainer = (): ReactElement => {
// ); // );
}; };
export default LibraryContainer; export default TabulatedContentContainer;

View File

@@ -4,18 +4,19 @@ import { getWeeklyPullList } from "../../actions/comicinfo.actions";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import Card from "../Carda"; import Card from "../Carda";
import ellipsize from "ellipsize"; import ellipsize from "ellipsize";
import { isNil, isUndefined } from "lodash";
export const PullList = (): ReactElement => { export const PullList = (): ReactElement => {
const pullListComics = useSelector( const pullListComics = useSelector(
(state: RootState) => state.comicInfo.pullList, (state: RootState) => state.comicInfo.pullList,
); );
console.log(pullListComics);
const dispatch = useDispatch(); const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
dispatch( dispatch(
getWeeklyPullList({ getWeeklyPullList({
startDate: "2022-6-1", startDate: "2022-11-15",
pageSize: "100", pageSize: "15",
currentPage: "1", currentPage: "1",
}), }),
); );
@@ -25,24 +26,25 @@ export const PullList = (): ReactElement => {
const columnData = useMemo( const columnData = useMemo(
() => [ () => [
{ {
Header: "Comic Information", header: "Comic Information",
columns: [ columns: [
{ {
Header: "Details", header: "Details",
id: "comicDetails", id: "comicDetails",
minWidth: 450, minWidth: 450,
accessor: (row) => { accessorKey: "issue",
console.log(row); cell: (row) => {
const item = row.getValue();
return ( return (
<div className="columns"> <div className="columns">
<div className="column"> <div className="column is-three-quarters">
<div className="comic-detail issue-metadata"> <div className="comic-detail issue-metadata">
<dl> <dl>
<dd> <dd>
<div className="columns mt-2"> <div className="columns mt-2">
<div className="column is-3"> <div className="column is-3">
<Card <Card
imageUrl={row.cover} imageUrl={item.cover}
orientation={"vertical"} orientation={"vertical"}
hasDetails={false} hasDetails={false}
// cardContainerStyle={{ maxWidth: 200 }} // cardContainerStyle={{ maxWidth: 200 }}
@@ -52,18 +54,20 @@ export const PullList = (): ReactElement => {
<dl> <dl>
<dt> <dt>
<h6 className="name has-text-weight-medium mb-1"> <h6 className="name has-text-weight-medium mb-1">
{row.name} {item.name}
</h6> </h6>
</dt> </dt>
<dd className="is-size-7"> <dd className="is-size-7">
published by{" "} published by{" "}
<span className="has-text-weight-semibold"> <span className="has-text-weight-semibold">
{row.publisher} {item.publisher}
</span> </span>
</dd> </dd>
<dd className="is-size-7"> <dd className="is-size-7">
<span>{ellipsize(row.description, 190)}</span> <span>
{ellipsize(item.description, 190)}
</span>
</dd> </dd>
<dd className="is-size-7 mt-2"> <dd className="is-size-7 mt-2">
@@ -71,10 +75,10 @@ export const PullList = (): ReactElement => {
<div className="control"> <div className="control">
<span className="tags"> <span className="tags">
<span className="tag is-success is-light has-text-weight-semibold"> <span className="tag is-success is-light has-text-weight-semibold">
{row.price} {item.price}
</span> </span>
<span className="tag is-success is-light"> <span className="tag is-success is-light">
{row.pulls} {item.pulls}
</span> </span>
</span> </span>
</div> </div>
@@ -99,16 +103,24 @@ export const PullList = (): ReactElement => {
return ( return (
<section className="container"> <section className="container">
<div className="section"> <div className="section">
<h1 className="title">Weekly Pull List</h1> <div className="header-area">
<T2Table <h1 className="title">Weekly Pull List</h1>
rowData={pullListComics} </div>
columns={columnData} {!isNil(pullListComics) && (
totalPages={pullListComics.length} <div>
paginationHandlers={{ <div className="library">
nextPage: nextPageHandler, <T2Table
previousPage: previousPageHandler, sourceData={pullListComics}
}} columns={columnData}
/> totalPages={pullListComics.length}
paginationHandlers={{
nextPage: nextPageHandler,
previousPage: previousPageHandler,
}}
/>
</div>
</div>
)}
</div> </div>
</section> </section>
); );

View File

@@ -2,16 +2,13 @@ import React, { ReactElement, useEffect, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { searchIssue } from "../../actions/fileops.actions"; import { searchIssue } from "../../actions/fileops.actions";
import Card from "../Carda"; import Card from "../Carda";
import SearchBar from "../Library/SearchBar";
import T2Table from "../shared/T2Table"; import T2Table from "../shared/T2Table";
import ellipsize from "ellipsize"; import ellipsize from "ellipsize";
import { isUndefined } from "lodash";
import { convert } from "html-to-text"; import { convert } from "html-to-text";
import { isUndefined } from "lodash";
export const Volumes = (props): ReactElement => { export const Volumes = (props): ReactElement => {
const volumes = useSelector( const volumes = useSelector((state: RootState) => state.fileOps.volumes);
(state: RootState) => state.fileOps.librarySearchResults,
);
const dispatch = useDispatch(); const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
dispatch( dispatch(
@@ -25,19 +22,20 @@ export const Volumes = (props): ReactElement => {
from: 0, from: 0,
}, },
type: "volumes", type: "volumes",
trigger: "volumesPage",
}, },
), ),
); );
}, []); }, []);
console.log(volumes);
const columnData = useMemo( const columnData = useMemo(
() => [ () => [
{ {
Header: "Volume Details", header: "Volume Details",
id: "volumeDetails", id: "volumeDetails",
minWidth: 450, minWidth: 450,
accessor: (row) => { accessorKey: "_source",
cell: (row) => {
const foo = row.getValue();
return ( return (
<div className="columns"> <div className="columns">
<div className="column"> <div className="column">
@@ -45,11 +43,11 @@ export const Volumes = (props): ReactElement => {
<dl> <dl>
<dd> <dd>
<div className="columns mt-2"> <div className="columns mt-2">
<div className="column is-3"> <div className="">
<Card <Card
imageUrl={ imageUrl={
row._source.sourcedMetadata.comicvine foo.sourcedMetadata.comicvine.volumeInformation
.volumeInformation.image.thumb_url .image.thumb_url
} }
orientation={"vertical"} orientation={"vertical"}
hasDetails={false} hasDetails={false}
@@ -61,7 +59,7 @@ export const Volumes = (props): ReactElement => {
<dt> <dt>
<h6 className="name has-text-weight-medium mb-1"> <h6 className="name has-text-weight-medium mb-1">
{ {
row._source.sourcedMetadata.comicvine foo.sourcedMetadata.comicvine
.volumeInformation.name .volumeInformation.name
} }
</h6> </h6>
@@ -70,7 +68,7 @@ export const Volumes = (props): ReactElement => {
published by{" "} published by{" "}
<span className="has-text-weight-semibold"> <span className="has-text-weight-semibold">
{ {
row._source.sourcedMetadata.comicvine foo.sourcedMetadata.comicvine
.volumeInformation.publisher.name .volumeInformation.publisher.name
} }
</span> </span>
@@ -80,7 +78,7 @@ export const Volumes = (props): ReactElement => {
<span> <span>
{ellipsize( {ellipsize(
convert( convert(
row._source.sourcedMetadata.comicvine foo.sourcedMetadata.comicvine
.volumeInformation.description, .volumeInformation.description,
{ {
baseElements: { baseElements: {
@@ -102,7 +100,7 @@ export const Volumes = (props): ReactElement => {
</span> </span>
<span className="tag is-success is-light"> <span className="tag is-success is-light">
{ {
row._source.sourcedMetadata.comicvine foo.sourcedMetadata.comicvine
.volumeInformation.count_of_issues .volumeInformation.count_of_issues
} }
</span> </span>
@@ -122,36 +120,35 @@ export const Volumes = (props): ReactElement => {
}, },
}, },
{ {
Header: "Download Status", header: "Download Status",
columns: [ columns: [
{ {
Header: "Files", header: "Files",
accessor: "_source.acquisition.directconnect", accessorKey: "_source.acquisition.directconnect",
align: "right", align: "right",
Cell: (props) => { cell: (props) => {
const row = props.getValue();
return ( return (
<div <div
style={{ style={{
display: "flex", display: "flex",
// flexDirection: "column", flexDirection: "column",
justifyContent: "center", justifyContent: "center",
}} }}
> >
{props.cell.value.length > 0 ? ( {row.length > 0 ? (
<span className="tag is-warning"> <span className="tag is-warning">{row.length}</span>
{props.cell.value.length}
</span>
) : null} ) : null}
</div> </div>
); );
}, },
}, },
{ {
Header: "Type", header: "Type",
id: "Air", id: "Air",
}, },
{ {
Header: "Type", header: "Type",
id: "dcc", id: "dcc",
}, },
], ],
@@ -162,16 +159,24 @@ export const Volumes = (props): ReactElement => {
return ( return (
<section className="container"> <section className="container">
<div className="section"> <div className="section">
{volumes.hits ? ( <div className="header-area">
<h1 className="title">Volumes</h1>
</div>
{!isUndefined(volumes.hits) && (
<div> <div>
<div className="library"> <div className="library">
<h1 className="title">Volumes</h1> <T2Table
{/* Search bar */} sourceData={volumes?.hits?.hits}
<SearchBar /> totalPages={volumes.hits.hits.length}
<T2Table rowData={volumes.hits.hits} columns={columnData} /> paginationHandlers={{
nextPage: () => {},
previousPage: () => {},
}}
columns={columnData}
/>
</div> </div>
</div> </div>
) : null} )}
</div> </div>
</section> </section>
); );

View File

@@ -20,6 +20,7 @@ export const T2Table = (tableOptions): ReactElement => {
pageIndex: 1, pageIndex: 1,
pageSize: 15, pageSize: 15,
}); });
console.log(sourceData)
const pagination = useMemo( const pagination = useMemo(

View File

@@ -51,6 +51,7 @@ export const IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS =
"IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS"; "IMS_COMIC_BOOK_GROUPS_CALL_IN_PROGRESS";
export const IMS_COMIC_BOOK_GROUPS_CALL_FAILED = export const IMS_COMIC_BOOK_GROUPS_CALL_FAILED =
"IMS_COMIC_BOOK_GROUPS_CALL_FAILED"; "IMS_COMIC_BOOK_GROUPS_CALL_FAILED";
export const VOLUMES_FETCHED="VOLUMES_FETCHED";
// search results from the Search service // search results from the Search service
export const SS_SEARCH_RESULTS_FETCHED = "SS_SEARCH_RESULTS_FETCHED"; export const SS_SEARCH_RESULTS_FETCHED = "SS_SEARCH_RESULTS_FETCHED";

View File

@@ -109,10 +109,14 @@ function comicinfoReducer(state = initialState, action) {
...state, ...state,
}; };
case CV_WEEKLY_PULLLIST_FETCHED: { case CV_WEEKLY_PULLLIST_FETCHED: {
const foo = [];
action.data.map((item) => {
foo.push({issue: item})
});
return { return {
...state, ...state,
inProgress: false, inProgress: false,
pullList: [...action.data], pullList: foo,
}; };
} }
case LIBRARY_STATISTICS_CALL_IN_PROGRESS: case LIBRARY_STATISTICS_CALL_IN_PROGRESS:

View File

@@ -28,6 +28,7 @@ import {
LS_IMPORT_CALL_IN_PROGRESS, LS_IMPORT_CALL_IN_PROGRESS,
SS_SEARCH_FAILED, SS_SEARCH_FAILED,
SS_SEARCH_RESULTS_FETCHED_SPECIAL, SS_SEARCH_RESULTS_FETCHED_SPECIAL,
VOLUMES_FETCHED,
} from "../constants/action-types"; } from "../constants/action-types";
const initialState = { const initialState = {
IMSCallInProgress: false, IMSCallInProgress: false,
@@ -45,6 +46,7 @@ const initialState = {
recentComics: [], recentComics: [],
wantedComics: [], wantedComics: [],
libraryComics: [], libraryComics: [],
volumes: [],
librarySearchResultsFormatted: [], librarySearchResultsFormatted: [],
librarySearchResultCount: 0, librarySearchResultCount: 0,
libraryQueueResults: [], libraryQueueResults: [],
@@ -188,7 +190,6 @@ function fileOpsReducer(state = initialState, action) {
} }
case SS_SEARCH_RESULTS_FETCHED: { case SS_SEARCH_RESULTS_FETCHED: {
console.log(action.data);
return { return {
...state, ...state,
libraryComics: action.data, libraryComics: action.data,
@@ -222,6 +223,13 @@ function fileOpsReducer(state = initialState, action) {
}; };
} }
case VOLUMES_FETCHED:
return {
...state,
volumes: action.data,
SSCallInProgress: false,
};
case SS_SEARCH_FAILED: { case SS_SEARCH_FAILED: {
return { return {
...state, ...state,