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

View File

@@ -1,18 +1,20 @@
import React, { ReactElement } from "react";
import { useParams } from "react-router-dom";
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 axios from "axios";
export const ComicDetailContainer = (): ReactElement | null => {
const { comicObjectId } = useParams<{ comicObjectId: string }>();
const queryClient = useQueryClient();
const {
data: comicBookDetailData,
isLoading,
isError,
} = useQuery({
queryKey: ["comicBookMetadata"],
queryKey: ["comicBookMetadata", comicObjectId],
queryFn: async () =>
await axios({
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`,
@@ -30,6 +32,12 @@ export const ComicDetailContainer = (): ReactElement | null => {
isLoading && <>Loading...</>;
}
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";
export const ComicVineMatchPanel = (comicVineData): ReactElement => {
const { comicObjectId, comicVineMatches } = comicVineData.props;
const { comicObjectId, comicVineMatches, queryClient, onMatchApplied } = comicVineData.props;
const { comicvine } = useStore(
useShallow((state) => ({
comicvine: state.comicvine,
@@ -19,6 +19,8 @@ export const ComicVineMatchPanel = (comicVineData): ReactElement => {
<MatchResult
matchData={comicVineMatches}
comicObjectId={comicObjectId}
queryClient={queryClient}
onMatchApplied={onMatchApplied}
/>
) : (
<>

View File

@@ -8,6 +8,8 @@ import axios from "axios";
interface MatchResultProps {
matchData: any;
comicObjectId: string;
queryClient?: any;
onMatchApplied?: () => void;
}
const handleBrokenImage = (e) => {
@@ -16,14 +18,33 @@ const handleBrokenImage = (e) => {
export const MatchResult = (props: MatchResultProps) => {
const applyCVMatch = async (match, comicObjectId) => {
return await axios.request({
url: `${LIBRARY_SERVICE_BASE_URI}/applyComicVineMetadata`,
method: "POST",
data: {
match,
comicObjectId,
},
});
try {
const response = await axios.request({
url: `${LIBRARY_SERVICE_BASE_URI}/applyComicVineMetadata`,
method: "POST",
data: {
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 (
<>

View File

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