📃 Fixed comicvine matching UI

This commit is contained in:
2026-02-26 13:51:53 -05:00
parent 59afeded6a
commit 92992449a9
5 changed files with 63 additions and 17 deletions

View File

@@ -85,6 +85,8 @@ interface ComicDetailProps {
updatedAt: string; updatedAt: string;
}; };
userSettings?: any; userSettings?: any;
queryClient?: any;
comicObjectId?: string;
} }
interface ComicVineSearchQuery { interface ComicVineSearchQuery {
@@ -132,8 +134,10 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
updatedAt, updatedAt,
}, },
userSettings, userSettings,
queryClient,
comicObjectId: comicObjectIdProp,
} = data; } = data;
const [page, setPage] = useState(1); const [activeTab, setActiveTab] = useState<number | undefined>(undefined);
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [slidingPanelContentId, setSlidingPanelContentId] = useState(""); const [slidingPanelContentId, setSlidingPanelContentId] = useState("");
const [modalIsOpen, setIsOpen] = useState(false); const [modalIsOpen, setIsOpen] = useState(false);
@@ -188,6 +192,11 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
props={{ props={{
comicVineMatches, comicVineMatches,
comicObjectId, comicObjectId,
queryClient,
onMatchApplied: () => {
setVisible(false);
setActiveTab(1); // Switch to Volume Information tab (id: 1)
},
}} }}
/> />
</> </>
@@ -521,6 +530,8 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
<TabControls <TabControls
filteredTabs={filteredTabs} filteredTabs={filteredTabs}
downloadCount={acquisition?.directconnect?.downloads?.length || 0} downloadCount={acquisition?.directconnect?.downloads?.length || 0}
activeTab={activeTab}
setActiveTab={setActiveTab}
/> />
<StyledSlidingPanel <StyledSlidingPanel

View File

@@ -1,18 +1,20 @@
import React, { ReactElement } from "react"; import React, { ReactElement } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { ComicDetail } from "../ComicDetail/ComicDetail"; import { ComicDetail } from "../ComicDetail/ComicDetail";
import { useQuery } from "@tanstack/react-query"; import { useQuery, useQueryClient } from "@tanstack/react-query";
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints"; import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
import axios from "axios"; import axios from "axios";
export const ComicDetailContainer = (): ReactElement | null => { export const ComicDetailContainer = (): ReactElement | null => {
const { comicObjectId } = useParams<{ comicObjectId: string }>(); const { comicObjectId } = useParams<{ comicObjectId: string }>();
const queryClient = useQueryClient();
const { const {
data: comicBookDetailData, data: comicBookDetailData,
isLoading, isLoading,
isError, isError,
} = useQuery({ } = useQuery({
queryKey: ["comicBookMetadata"], queryKey: ["comicBookMetadata", comicObjectId],
queryFn: async () => queryFn: async () =>
await axios({ await axios({
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`, url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`,
@@ -30,6 +32,12 @@ export const ComicDetailContainer = (): ReactElement | null => {
isLoading && <>Loading...</>; isLoading && <>Loading...</>;
} }
return ( return (
comicBookDetailData?.data && <ComicDetail data={comicBookDetailData.data} /> comicBookDetailData?.data && (
<ComicDetail
data={comicBookDetailData.data}
queryClient={queryClient}
comicObjectId={comicObjectId}
/>
)
); );
}; };

View File

@@ -6,7 +6,7 @@ import { useStore } from "../../store";
import { useShallow } from "zustand/react/shallow"; import { useShallow } from "zustand/react/shallow";
export const ComicVineMatchPanel = (comicVineData): ReactElement => { export const ComicVineMatchPanel = (comicVineData): ReactElement => {
const { comicObjectId, comicVineMatches } = comicVineData.props; const { comicObjectId, comicVineMatches, queryClient, onMatchApplied } = comicVineData.props;
const { comicvine } = useStore( const { comicvine } = useStore(
useShallow((state) => ({ useShallow((state) => ({
comicvine: state.comicvine, comicvine: state.comicvine,
@@ -19,6 +19,8 @@ export const ComicVineMatchPanel = (comicVineData): ReactElement => {
<MatchResult <MatchResult
matchData={comicVineMatches} matchData={comicVineMatches}
comicObjectId={comicObjectId} comicObjectId={comicObjectId}
queryClient={queryClient}
onMatchApplied={onMatchApplied}
/> />
) : ( ) : (
<> <>

View File

@@ -8,6 +8,8 @@ import axios from "axios";
interface MatchResultProps { interface MatchResultProps {
matchData: any; matchData: any;
comicObjectId: string; comicObjectId: string;
queryClient?: any;
onMatchApplied?: () => void;
} }
const handleBrokenImage = (e) => { const handleBrokenImage = (e) => {
@@ -16,14 +18,33 @@ const handleBrokenImage = (e) => {
export const MatchResult = (props: MatchResultProps) => { export const MatchResult = (props: MatchResultProps) => {
const applyCVMatch = async (match, comicObjectId) => { const applyCVMatch = async (match, comicObjectId) => {
return await axios.request({ try {
url: `${LIBRARY_SERVICE_BASE_URI}/applyComicVineMetadata`, const response = await axios.request({
method: "POST", url: `${LIBRARY_SERVICE_BASE_URI}/applyComicVineMetadata`,
data: { method: "POST",
match, data: {
comicObjectId, match,
}, comicObjectId,
}); },
});
// Invalidate and refetch the comic book metadata
if (props.queryClient) {
await props.queryClient.invalidateQueries({
queryKey: ["comicBookMetadata", comicObjectId],
});
}
// Call the callback to close panel and switch tabs
if (props.onMatchApplied) {
props.onMatchApplied();
}
return response;
} catch (error) {
console.error("Error applying ComicVine match:", error);
throw error;
}
}; };
return ( return (
<> <>

View File

@@ -2,8 +2,12 @@ import React, { ReactElement, useState } from "react";
import { isNil } from "lodash"; import { isNil } from "lodash";
export const TabControls = (props): ReactElement => { export const TabControls = (props): ReactElement => {
const { filteredTabs, downloadCount } = props; const { filteredTabs, downloadCount, activeTab, setActiveTab } = props;
const [active, setActive] = useState(filteredTabs[0].id); const [active, setActive] = useState(filteredTabs[0].id);
// Use controlled state if provided, otherwise use internal state
const currentActive = activeTab !== undefined ? activeTab : active;
const handleSetActive = activeTab !== undefined ? setActiveTab : setActive;
return ( return (
<> <>
@@ -14,12 +18,12 @@ export const TabControls = (props): ReactElement => {
<a <a
key={id} 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 ${ 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 currentActive === id
? "border-b border-cyan-50 dark:text-slate-200" ? "border-b border-cyan-50 dark:text-slate-200"
: "border-b border-transparent" : "border-b border-transparent"
}`} }`}
aria-current="page" aria-current="page"
onClick={() => setActive(id)} onClick={() => handleSetActive(id)}
> >
{/* Downloads tab and count badge */} {/* Downloads tab and count badge */}
<> <>
@@ -44,7 +48,7 @@ export const TabControls = (props): ReactElement => {
</div> </div>
</div> </div>
{filteredTabs.map(({ id, content }) => { {filteredTabs.map(({ id, content }) => {
return active === id ? content : null; return currentActive === id ? content : null;
})} })}
</> </>
); );