⚡️ Refactored VolumeDetail page to use react-query
This commit is contained in:
@@ -1,26 +1,24 @@
|
||||
import { isEmpty, isUndefined, map, partialRight, pick } from "lodash";
|
||||
import React, { useEffect, ReactElement, useState, useCallback } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useParams } from "react-router";
|
||||
import {
|
||||
getComicBookDetailById,
|
||||
getIssuesForSeries,
|
||||
analyzeLibrary,
|
||||
} from "../../actions/comicinfo.actions";
|
||||
import { useQuery, useMutation, QueryClient } from "@tanstack/react-query";
|
||||
import PotentialLibraryMatches from "./PotentialLibraryMatches";
|
||||
import Masonry from "react-masonry-css";
|
||||
import { Card } from "../shared/Carda";
|
||||
import SlidingPane from "react-sliding-pane";
|
||||
import { convert } from "html-to-text";
|
||||
import ellipsize from "ellipsize";
|
||||
import {
|
||||
COMICVINE_SERVICE_URI,
|
||||
LIBRARY_SERVICE_BASE_URI,
|
||||
} from "../../constants/endpoints";
|
||||
import axios from "axios";
|
||||
|
||||
const VolumeDetails = (props): ReactElement => {
|
||||
const breakpointColumnsObj = {
|
||||
default: 6,
|
||||
1100: 4,
|
||||
700: 3,
|
||||
500: 2,
|
||||
};
|
||||
// sliding panel config
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [slidingPanelContentId, setSlidingPanelContentId] = useState("");
|
||||
@@ -33,7 +31,9 @@ const VolumeDetails = (props): ReactElement => {
|
||||
content: () => {
|
||||
const ids = map(matches, partialRight(pick, "_id"));
|
||||
const matchIds = ids.map((id: any) => id._id);
|
||||
return <PotentialLibraryMatches matches={matchIds} />;
|
||||
{
|
||||
/* return <PotentialLibraryMatches matches={matchIds} />; */
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -45,68 +45,85 @@ const VolumeDetails = (props): ReactElement => {
|
||||
setVisible(true);
|
||||
}, []);
|
||||
|
||||
const analyzeIssues = useCallback((issues) => {
|
||||
dispatch(analyzeLibrary(issues));
|
||||
}, []);
|
||||
// const analyzeIssues = useCallback((issues) => {
|
||||
// dispatch(analyzeLibrary(issues));
|
||||
// }, []);
|
||||
//
|
||||
// const comicObject = useSelector(
|
||||
// (state: RootState) => state.comicInfo.comicBookDetail,
|
||||
// );
|
||||
|
||||
const comicBookDetails = useSelector(
|
||||
(state: RootState) => state.comicInfo.comicBookDetail,
|
||||
);
|
||||
const issuesForVolume = useSelector(
|
||||
(state: RootState) => state.comicInfo.issuesForVolume,
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
useEffect(() => {
|
||||
dispatch(getIssuesForSeries(comicObjectId));
|
||||
dispatch(getComicBookDetailById(comicObjectId));
|
||||
// dispatch(getIssuesForSeries(comicObjectId));
|
||||
// dispatch(getComicBookDetailById(comicObjectId));
|
||||
}, []);
|
||||
|
||||
const { comicObjectId } = useParams<{ comicObjectId: string }>();
|
||||
|
||||
const { data: comicObject, isSuccess: isComicObjectFetchedSuccessfully } =
|
||||
useQuery({
|
||||
queryFn: async () =>
|
||||
axios({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`,
|
||||
method: "POST",
|
||||
data: {
|
||||
id: comicObjectId,
|
||||
},
|
||||
}),
|
||||
queryKey: ["comicObject"],
|
||||
});
|
||||
|
||||
// get issues for a series
|
||||
const {
|
||||
data: issuesForSeries,
|
||||
isSuccess,
|
||||
isFetching,
|
||||
} = useQuery({
|
||||
queryFn: async () =>
|
||||
await axios({
|
||||
url: `${COMICVINE_SERVICE_URI}/getIssuesForSeries`,
|
||||
method: "POST",
|
||||
data: {
|
||||
comicObjectId,
|
||||
},
|
||||
}),
|
||||
queryKey: ["issuesForSeries"],
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
console.log("jihya", issuesForSeries);
|
||||
const IssuesInVolume = () => (
|
||||
<>
|
||||
{!isUndefined(issuesForVolume) ? (
|
||||
<div className="button" onClick={() => analyzeIssues(issuesForVolume)}>
|
||||
{!isUndefined(issuesForSeries) ? (
|
||||
<div className="button" onClick={() => analyzeIssues(issuesForSeries)}>
|
||||
Analyze Library
|
||||
</div>
|
||||
) : null}
|
||||
<Masonry
|
||||
breakpointCols={breakpointColumnsObj}
|
||||
className="issues-container"
|
||||
columnClassName="issues-column"
|
||||
>
|
||||
{!isUndefined(issuesForVolume) && !isEmpty(issuesForVolume)
|
||||
? issuesForVolume.map((issue) => {
|
||||
return (
|
||||
<>
|
||||
{isSuccess &&
|
||||
issuesForSeries.data.map((issue) => {
|
||||
return (
|
||||
<>
|
||||
<Card
|
||||
key={issue.id}
|
||||
imageUrl={issue.image.thumb_url}
|
||||
orientation={"vertical"}
|
||||
hasDetails
|
||||
borderColorClass={
|
||||
!isEmpty(issue.matches) ? "green-border" : ""
|
||||
}
|
||||
backgroundColor={!isEmpty(issue.matches) ? "beige" : ""}
|
||||
onClick={() =>
|
||||
openPotentialLibraryMatchesPanel(issue.matches)
|
||||
}
|
||||
>
|
||||
<span className="tag is-warning mr-1">
|
||||
{issue.issue_number}
|
||||
</span>
|
||||
{!isEmpty(issue.matches) ? (
|
||||
<>
|
||||
<span className="icon has-text-success">
|
||||
<i className="fa-regular fa-asterisk"></i>
|
||||
</span>
|
||||
</>
|
||||
) : null}
|
||||
</Card>
|
||||
);
|
||||
})
|
||||
: "loading"}
|
||||
</Masonry>
|
||||
imageUrl={issue.image.small_url}
|
||||
orientation={"cover-only"}
|
||||
hasDetails={false}
|
||||
/>
|
||||
<span className="tag is-warning mr-1">
|
||||
{issue.issue_number}
|
||||
</span>
|
||||
{!isEmpty(issue.matches) ? (
|
||||
<>
|
||||
<span className="icon has-text-success">
|
||||
<i className="fa-regular fa-asterisk"></i>
|
||||
</span>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -122,13 +139,13 @@ const VolumeDetails = (props): ReactElement => {
|
||||
id: 2,
|
||||
icon: <i className="fa-regular fa-mask"></i>,
|
||||
name: "Characters",
|
||||
content: <div key={2}>asdasd</div>,
|
||||
content: <div key={2}>Characters</div>,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
icon: <i className="fa-solid fa-scroll"></i>,
|
||||
name: "Arcs",
|
||||
content: <div key={3}>asdasd</div>,
|
||||
name: "Story Arcs",
|
||||
content: <div key={3}>Story Arcs</div>,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -136,21 +153,26 @@ const VolumeDetails = (props): ReactElement => {
|
||||
const MetadataTabGroup = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="tabs">
|
||||
<ul>
|
||||
{tabGroup.map(({ id, name, icon }) => (
|
||||
<li
|
||||
key={id}
|
||||
className={id === active ? "is-active" : ""}
|
||||
onClick={() => setActive(id)}
|
||||
>
|
||||
<a>
|
||||
<span className="icon is-small">{icon}</span>
|
||||
<div className="hidden sm:block mt-7 mb-3 w-fit">
|
||||
<div className="border-b border-gray-200">
|
||||
<nav className="flex gap-6" aria-label="Tabs">
|
||||
{tabGroup.map(({ id, name, icon }) => (
|
||||
<a
|
||||
key={id}
|
||||
className={`inline-flex shrink-0 items-center gap-2 px-1 py-1 text-md font-medium text-gray-500 dark:text-gray-400 hover:border-gray-300 hover:border-b hover:dark:text-slate-200 ${
|
||||
active === id
|
||||
? "border-b border-cyan-50 dark:text-slate-200"
|
||||
: "border-b border-transparent"
|
||||
}`}
|
||||
aria-current="page"
|
||||
onClick={() => setActive(id)}
|
||||
>
|
||||
<span className="w-5 h-5">{icon}</span>
|
||||
{name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
{tabGroup.map(({ id, content }) => {
|
||||
return active === id ? content : null;
|
||||
@@ -158,43 +180,33 @@ const VolumeDetails = (props): ReactElement => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
if (
|
||||
!isUndefined(comicBookDetails.sourcedMetadata) &&
|
||||
!isUndefined(comicBookDetails.sourcedMetadata.comicvine.volumeInformation)
|
||||
) {
|
||||
if (isComicObjectFetchedSuccessfully && !isUndefined(comicObject.data)) {
|
||||
const { sourcedMetadata } = comicObject.data;
|
||||
return (
|
||||
<div className="container volume-details">
|
||||
<div className="section">
|
||||
{/* Title */}
|
||||
<h1 className="title">
|
||||
{comicBookDetails.sourcedMetadata.comicvine.volumeInformation.name}
|
||||
</h1>
|
||||
<div className="columns is-multiline">
|
||||
<div className="container mx-auto">
|
||||
<div>
|
||||
<div className="flex flex-row gap-4">
|
||||
{/* Volume cover */}
|
||||
<div className="column is-narrow">
|
||||
<Card
|
||||
imageUrl={
|
||||
comicBookDetails.sourcedMetadata.comicvine.volumeInformation
|
||||
.image.small_url
|
||||
}
|
||||
cardContainerStyle={{ maxWidth: 275 }}
|
||||
orientation={"vertical"}
|
||||
hasDetails={false}
|
||||
/>
|
||||
</div>
|
||||
<Card
|
||||
imageUrl={
|
||||
sourcedMetadata.comicvine.volumeInformation.image.small_url
|
||||
}
|
||||
orientation={"cover-only"}
|
||||
hasDetails={false}
|
||||
/>
|
||||
|
||||
<div className="column is-three-fifths">
|
||||
<div>
|
||||
<div className="field is-grouped mt-2">
|
||||
{/* Title */}
|
||||
<span className="text-xl">
|
||||
{sourcedMetadata.comicvine.volumeInformation.name}
|
||||
</span>
|
||||
{/* Comicvine Id */}
|
||||
<div className="control">
|
||||
<div className="tags has-addons">
|
||||
<span className="tag">ComicVine Id</span>
|
||||
<span className="tag is-info is-light">
|
||||
{
|
||||
comicBookDetails.sourcedMetadata.comicvine
|
||||
.volumeInformation.id
|
||||
}
|
||||
{sourcedMetadata.comicvine.volumeInformation.id}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -204,8 +216,8 @@ const VolumeDetails = (props): ReactElement => {
|
||||
<span className="tag is-warning is-light">Publisher</span>
|
||||
<span className="tag is-volume-related">
|
||||
{
|
||||
comicBookDetails.sourcedMetadata.comicvine
|
||||
.volumeInformation.publisher.name
|
||||
sourcedMetadata.comicvine.volumeInformation.publisher
|
||||
.name
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
@@ -215,13 +227,11 @@ const VolumeDetails = (props): ReactElement => {
|
||||
{/* Deck */}
|
||||
<div>
|
||||
{!isEmpty(
|
||||
comicBookDetails.sourcedMetadata.comicvine.volumeInformation
|
||||
.description,
|
||||
sourcedMetadata.comicvine.volumeInformation.description,
|
||||
)
|
||||
? ellipsize(
|
||||
convert(
|
||||
comicBookDetails.sourcedMetadata.comicvine
|
||||
.volumeInformation.description,
|
||||
sourcedMetadata.comicvine.volumeInformation.description,
|
||||
{
|
||||
baseElements: {
|
||||
selectors: ["p"],
|
||||
|
||||
@@ -4,6 +4,7 @@ import Card from "../shared/Carda";
|
||||
import T2Table from "../shared/T2Table";
|
||||
import ellipsize from "ellipsize";
|
||||
import { convert } from "html-to-text";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import { SEARCH_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||
@@ -39,28 +40,32 @@ export const Volumes = (props): ReactElement => {
|
||||
header: "Volume Details",
|
||||
id: "volumeDetails",
|
||||
minWidth: 450,
|
||||
accessorKey: "_source",
|
||||
accessorFn: (row) => row,
|
||||
cell: (row): any => {
|
||||
const foo = row.getValue();
|
||||
const comicObject = row.getValue();
|
||||
const {
|
||||
_source: { sourcedMetadata },
|
||||
} = comicObject;
|
||||
console.log("jaggu", row.getValue());
|
||||
return (
|
||||
<div className="flex flex-row gap-3 mt-5">
|
||||
<Card
|
||||
imageUrl={
|
||||
foo.sourcedMetadata.comicvine.volumeInformation.image
|
||||
.small_url
|
||||
}
|
||||
orientation={"cover-only"}
|
||||
hasDetails={false}
|
||||
/>
|
||||
<Link to={`/volume/details/${comicObject._id}`}>
|
||||
<Card
|
||||
imageUrl={
|
||||
sourcedMetadata.comicvine.volumeInformation.image.small_url
|
||||
}
|
||||
orientation={"cover-only"}
|
||||
hasDetails={false}
|
||||
/>
|
||||
</Link>
|
||||
<div className="dark:bg-[#647587] bg-slate-200 p-3 rounded-lg h-fit">
|
||||
<span className="text-xl mb-1">
|
||||
{foo.sourcedMetadata.comicvine.volumeInformation.name}
|
||||
{sourcedMetadata.comicvine.volumeInformation.name}
|
||||
</span>
|
||||
<p>
|
||||
{ellipsize(
|
||||
convert(
|
||||
foo.sourcedMetadata.comicvine.volumeInformation
|
||||
.description,
|
||||
sourcedMetadata.comicvine.volumeInformation.description,
|
||||
{
|
||||
baseElements: {
|
||||
selectors: ["p"],
|
||||
@@ -162,6 +167,7 @@ export const Volumes = (props): ReactElement => {
|
||||
nextPage: () => {},
|
||||
previousPage: () => {},
|
||||
}}
|
||||
rowClickHandler={() => {}}
|
||||
columns={columnData}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ import Search from "./components/Search/Search";
|
||||
import TabulatedContentContainer from "./components/Library/TabulatedContentContainer";
|
||||
import { ComicDetailContainer } from "./components/ComicDetail/ComicDetailContainer";
|
||||
import Volumes from "./components/Volumes/Volumes";
|
||||
import VolumeDetails from "./components/VolumeDetail/VolumeDetail";
|
||||
import WantedComics from "./components/WantedComics/WantedComics";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
@@ -37,6 +38,7 @@ const router = createBrowserRouter([
|
||||
},
|
||||
{ path: "import", element: <Import path={"./comics"} /> },
|
||||
{ path: "search", element: <Search /> },
|
||||
{ path: "volume/details/:comicObjectId", element: <VolumeDetails /> },
|
||||
{ path: "volumes", element: <Volumes /> },
|
||||
{ path: "wanted", element: <WantedComics /> },
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user