🐘 Massive refactor for graphql changes
This commit is contained in:
16
codegen.yml
Normal file
16
codegen.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
schema: http://localhost:3000/graphql
|
||||
documents: 'src/client/graphql/**/*.graphql'
|
||||
generates:
|
||||
src/client/graphql/generated.ts:
|
||||
plugins:
|
||||
- typescript
|
||||
- typescript-operations
|
||||
- typescript-react-query
|
||||
config:
|
||||
fetcher:
|
||||
func: './fetcher#fetcher'
|
||||
isReactHook: false
|
||||
exposeFetcher: true
|
||||
exposeQueryKeys: true
|
||||
addInfiniteQuery: true
|
||||
reactQueryVersion: 5
|
||||
4601
package-lock.json
generated
4601
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -13,11 +13,14 @@
|
||||
"test:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build"
|
||||
"build-storybook": "storybook build",
|
||||
"codegen": "graphql-codegen --config codegen.yml",
|
||||
"codegen:watch": "graphql-codegen --config codegen.yml --watch"
|
||||
},
|
||||
"author": "Rishi Ghan",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apollo/client": "^4.1.6",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
@@ -45,6 +48,7 @@
|
||||
"final-form": "^5.0.0",
|
||||
"final-form-arrays": "^4.0.0",
|
||||
"focus-trap-react": "^12.0.0",
|
||||
"graphql": "^16.13.1",
|
||||
"history": "^5.3.0",
|
||||
"html-to-text": "^9.0.5",
|
||||
"i18next": "^25.8.13",
|
||||
@@ -74,6 +78,7 @@
|
||||
"react-sliding-pane": "^7.3.0",
|
||||
"react-textarea-autosize": "^8.5.9",
|
||||
"react-toastify": "^11.0.5",
|
||||
"rxjs": "^7.8.2",
|
||||
"socket.io-client": "^4.8.3",
|
||||
"styled-components": "^6.3.11",
|
||||
"threetwo-ui-typings": "^1.0.14",
|
||||
@@ -83,6 +88,10 @@
|
||||
"zustand": "^5.0.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/cli": "^6.1.2",
|
||||
"@graphql-codegen/typescript": "^5.0.8",
|
||||
"@graphql-codegen/typescript-operations": "^5.0.8",
|
||||
"@graphql-codegen/typescript-react-query": "^6.1.2",
|
||||
"@iconify-json/solar": "^1.2.5",
|
||||
"@iconify/json": "^2.2.443",
|
||||
"@iconify/tailwind": "^1.2.0",
|
||||
|
||||
@@ -55,7 +55,6 @@ export const toggleAirDCPPSocketConnectionStatus =
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log("Can't set AirDC++ socket status.");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ export const getWeeklyPullList = (options) => async (dispatch) => {
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
// Error handling could be added here if needed
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,10 +73,9 @@ export const comicinfoAPICall = (options) => async (dispatch) => {
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log("Could not complete request.");
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
dispatch({
|
||||
type: CV_API_GENERIC_FAILURE,
|
||||
error,
|
||||
@@ -99,7 +98,6 @@ export const getIssuesForSeries =
|
||||
comicObjectID,
|
||||
},
|
||||
});
|
||||
console.log(issues);
|
||||
dispatch({
|
||||
type: CV_ISSUES_FOR_VOLUME_IN_LIBRARY_SUCCESS,
|
||||
issues: issues.data.results,
|
||||
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
LS_SET_QUEUE_STATUS,
|
||||
LS_IMPORT_JOB_STATISTICS_FETCHED,
|
||||
} from "../constants/action-types";
|
||||
import { success } from "react-notification-system-redux";
|
||||
|
||||
import { isNil } from "lodash";
|
||||
|
||||
@@ -151,7 +150,7 @@ export const getComicBooks = (options) => async (dispatch) => {
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.log("Unrecognized comic status.");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -219,12 +218,11 @@ export const fetchVolumeGroups = () => async (dispatch) => {
|
||||
data: response.data,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
// Error handling could be added here if needed
|
||||
}
|
||||
};
|
||||
export const fetchComicVineMatches =
|
||||
(searchPayload, issueSearchQuery, seriesSearchQuery?) => async (dispatch) => {
|
||||
console.log(issueSearchQuery);
|
||||
try {
|
||||
dispatch({
|
||||
type: CV_API_CALL_IN_PROGRESS,
|
||||
@@ -273,7 +271,7 @@ export const fetchComicVineMatches =
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
// Error handling could be added here if needed
|
||||
}
|
||||
|
||||
dispatch({
|
||||
|
||||
@@ -7,8 +7,6 @@ export const fetchMetronResource = async (options) => {
|
||||
`${METRON_SERVICE_URI}/fetchResource`,
|
||||
options,
|
||||
);
|
||||
console.log(metronResourceResults);
|
||||
console.log("has more? ", !isNil(metronResourceResults.data.next));
|
||||
const results = metronResourceResults.data.results.map((result) => {
|
||||
return {
|
||||
label: result.name || result.__str__,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React, { ReactElement, useEffect } from "react";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { ApolloProvider } from "@apollo/client/react";
|
||||
import { Navbar2 } from "./shared/Navbar2";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
import "../assets/scss/App.css";
|
||||
import { useStore } from "../store";
|
||||
import { apolloClient } from "../graphql/client";
|
||||
|
||||
export const App = (): ReactElement => {
|
||||
useEffect(() => {
|
||||
@@ -11,11 +13,11 @@ export const App = (): ReactElement => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ApolloProvider client={apolloClient}>
|
||||
<Navbar2 />
|
||||
<Outlet />
|
||||
<ToastContainer stacked hideProgressBar />
|
||||
</>
|
||||
</ApolloProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -160,7 +160,9 @@ export const AcquisitionPanel = (
|
||||
type,
|
||||
config,
|
||||
},
|
||||
(data: any) => console.log(data),
|
||||
(data: any) => {
|
||||
// Download initiated
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import "react-sliding-pane/dist/react-sliding-pane.css";
|
||||
import SlidingPane from "react-sliding-pane";
|
||||
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
||||
import { styled } from "styled-components";
|
||||
import { RawFileDetails as RawFileDetailsType } from "../../graphql/generated";
|
||||
|
||||
// Extracted modules
|
||||
import { useComicVineMatching } from "./useComicVineMatching";
|
||||
@@ -22,45 +23,32 @@ const StyledSlidingPanel = styled(SlidingPane)`
|
||||
background: #ccc;
|
||||
`;
|
||||
|
||||
interface RawFileDetails {
|
||||
name: string;
|
||||
cover?: {
|
||||
filePath?: string;
|
||||
};
|
||||
containedIn?: string;
|
||||
fileSize?: number;
|
||||
path?: string;
|
||||
extension?: string;
|
||||
mimeType?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface InferredIssue {
|
||||
type InferredIssue = {
|
||||
name?: string;
|
||||
number?: number;
|
||||
year?: string;
|
||||
subtitle?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
};
|
||||
|
||||
interface ComicVineMetadata {
|
||||
type ComicVineMetadata = {
|
||||
name?: string;
|
||||
volumeInformation?: any;
|
||||
[key: string]: any;
|
||||
}
|
||||
};
|
||||
|
||||
interface Acquisition {
|
||||
type Acquisition = {
|
||||
directconnect?: {
|
||||
downloads?: any[];
|
||||
};
|
||||
torrent?: any[];
|
||||
[key: string]: any;
|
||||
}
|
||||
};
|
||||
|
||||
interface ComicDetailProps {
|
||||
type ComicDetailProps = {
|
||||
data: {
|
||||
_id: string;
|
||||
rawFileDetails?: RawFileDetails;
|
||||
rawFileDetails?: RawFileDetailsType;
|
||||
inferredMetadata: {
|
||||
issue?: InferredIssue;
|
||||
};
|
||||
@@ -76,7 +64,7 @@ interface ComicDetailProps {
|
||||
userSettings?: any;
|
||||
queryClient?: any;
|
||||
comicObjectId?: string;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Component for displaying the metadata for a comic in greater detail.
|
||||
@@ -117,7 +105,7 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
||||
}, []);
|
||||
|
||||
const afterOpenModal = useCallback((things: any) => {
|
||||
console.log("kolaveri", things);
|
||||
// Modal opened callback
|
||||
}, []);
|
||||
|
||||
const closeModal = useCallback(() => {
|
||||
@@ -154,7 +142,6 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
||||
openEditMetadataPanel();
|
||||
break;
|
||||
default:
|
||||
console.log("No valid action selected.");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { ReactElement } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ComicDetail } from "../ComicDetail/ComicDetail";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||
import axios from "axios";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useGetComicByIdQuery } from "../../graphql/generated";
|
||||
import { adaptGraphQLComicToLegacy } from "../../graphql/adapters/comicAdapter";
|
||||
|
||||
export const ComicDetailContainer = (): ReactElement | null => {
|
||||
const { comicObjectId } = useParams<{ comicObjectId: string }>();
|
||||
@@ -13,31 +13,28 @@ export const ComicDetailContainer = (): ReactElement | null => {
|
||||
data: comicBookDetailData,
|
||||
isLoading,
|
||||
isError,
|
||||
} = useQuery({
|
||||
queryKey: ["comicBookMetadata", comicObjectId],
|
||||
queryFn: async () =>
|
||||
await axios({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`,
|
||||
method: "POST",
|
||||
data: {
|
||||
id: comicObjectId,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
{
|
||||
isError && <>Error</>;
|
||||
}
|
||||
{
|
||||
isLoading && <>Loading...</>;
|
||||
}
|
||||
return (
|
||||
comicBookDetailData?.data && (
|
||||
<ComicDetail
|
||||
data={comicBookDetailData.data}
|
||||
queryClient={queryClient}
|
||||
comicObjectId={comicObjectId}
|
||||
/>
|
||||
)
|
||||
} = useGetComicByIdQuery(
|
||||
{ id: comicObjectId! },
|
||||
{ enabled: !!comicObjectId }
|
||||
);
|
||||
|
||||
if (isError) {
|
||||
return <div>Error loading comic details</div>;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
const adaptedData = comicBookDetailData?.comic
|
||||
? adaptGraphQLComicToLegacy(comicBookDetailData.comic)
|
||||
: null;
|
||||
|
||||
return adaptedData ? (
|
||||
<ComicDetail
|
||||
data={adaptedData}
|
||||
queryClient={queryClient}
|
||||
comicObjectId={comicObjectId}
|
||||
/>
|
||||
) : null;
|
||||
};
|
||||
|
||||
@@ -77,8 +77,6 @@ export const DownloadProgressTick: React.FC<DownloadProgressTickProps> = ({
|
||||
*/
|
||||
const onDownloadTick = (data: DownloadTickData) => {
|
||||
// Compare numeric data.id to string bundleId
|
||||
console.log(data.id);
|
||||
console.log(`bundleId is ${bundleId}`)
|
||||
if (data.id === parseInt(bundleId, 10)) {
|
||||
setTick(data);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
import React, { ReactElement } from "react";
|
||||
import React, { ReactElement, ReactNode } from "react";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import { isEmpty } from "lodash";
|
||||
import { format, parseISO } from "date-fns";
|
||||
import { RawFileDetails as RawFileDetailsType } from "../../graphql/generated";
|
||||
|
||||
interface RawFileDetailsProps {
|
||||
type RawFileDetailsProps = {
|
||||
data?: {
|
||||
rawFileDetails?: {
|
||||
containedIn?: string;
|
||||
name?: string;
|
||||
fileSize?: number;
|
||||
path?: string;
|
||||
extension?: string;
|
||||
mimeType?: string;
|
||||
cover?: {
|
||||
filePath?: string;
|
||||
};
|
||||
};
|
||||
rawFileDetails?: RawFileDetailsType;
|
||||
inferredMetadata?: {
|
||||
issue?: {
|
||||
year?: string;
|
||||
@@ -27,18 +18,18 @@ interface RawFileDetailsProps {
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
children?: any;
|
||||
}
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
||||
const { rawFileDetails, inferredMetadata, created_at, updated_at } =
|
||||
props.data;
|
||||
props.data || {};
|
||||
return (
|
||||
<>
|
||||
<div className="max-w-2xl ml-5">
|
||||
<div className="px-4 sm:px-6">
|
||||
<p className="text-gray-500 dark:text-gray-400">
|
||||
<span className="text-xl">{rawFileDetails.name}</span>
|
||||
<span className="text-xl">{rawFileDetails?.name}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="px-4 py-5 sm:px-6">
|
||||
@@ -48,10 +39,10 @@ export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
||||
Raw File Details
|
||||
</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
||||
{rawFileDetails.containedIn +
|
||||
"/" +
|
||||
rawFileDetails.name +
|
||||
rawFileDetails.extension}
|
||||
{rawFileDetails?.containedIn}
|
||||
{"/"}
|
||||
{rawFileDetails?.name}
|
||||
{rawFileDetails?.extension}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="sm:col-span-1">
|
||||
@@ -59,10 +50,10 @@ export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
||||
Inferred Issue Metadata
|
||||
</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
||||
Series Name: {inferredMetadata.issue.name}
|
||||
{!isEmpty(inferredMetadata.issue.number) ? (
|
||||
Series Name: {inferredMetadata?.issue?.name}
|
||||
{!isEmpty(inferredMetadata?.issue?.number) ? (
|
||||
<span className="tag is-primary is-light">
|
||||
{inferredMetadata.issue.number}
|
||||
{inferredMetadata?.issue?.number}
|
||||
</span>
|
||||
) : null}
|
||||
</dd>
|
||||
@@ -79,7 +70,7 @@ export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
||||
</span>
|
||||
|
||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||
{rawFileDetails.mimeType}
|
||||
{rawFileDetails?.mimeType}
|
||||
</span>
|
||||
</span>
|
||||
</dd>
|
||||
@@ -96,7 +87,7 @@ export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
||||
</span>
|
||||
|
||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||
{prettyBytes(rawFileDetails.fileSize)}
|
||||
{rawFileDetails?.fileSize ? prettyBytes(rawFileDetails.fileSize) : "N/A"}
|
||||
</span>
|
||||
</span>
|
||||
</dd>
|
||||
@@ -106,8 +97,12 @@ export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
||||
Import Details
|
||||
</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
||||
{format(parseISO(created_at), "dd MMMM, yyyy")},{" "}
|
||||
{format(parseISO(created_at), "h aaaa")}
|
||||
{created_at ? (
|
||||
<>
|
||||
{format(parseISO(created_at), "dd MMMM, yyyy")},{" "}
|
||||
{format(parseISO(created_at), "h aaaa")}
|
||||
</>
|
||||
) : "N/A"}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
|
||||
@@ -2,17 +2,18 @@ import React from "react";
|
||||
import { ComicVineSearchForm } from "./ComicVineSearchForm";
|
||||
import { ComicVineMatchPanel } from "./ComicVineMatchPanel";
|
||||
import { EditMetadataPanel } from "./EditMetadataPanel";
|
||||
import { RawFileDetails } from "../../graphql/generated";
|
||||
|
||||
interface InferredIssue {
|
||||
type InferredIssue = {
|
||||
name?: string;
|
||||
number?: number;
|
||||
year?: string;
|
||||
subtitle?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
};
|
||||
|
||||
interface CVMatchesPanelProps {
|
||||
rawFileDetails: any;
|
||||
type CVMatchesPanelProps = {
|
||||
rawFileDetails?: RawFileDetails;
|
||||
inferredMetadata: {
|
||||
issue?: InferredIssue;
|
||||
};
|
||||
@@ -20,7 +21,7 @@ interface CVMatchesPanelProps {
|
||||
comicObjectId: string;
|
||||
queryClient: any;
|
||||
onMatchApplied: () => void;
|
||||
}
|
||||
};
|
||||
|
||||
export const CVMatchesPanel: React.FC<CVMatchesPanelProps> = ({
|
||||
rawFileDetails,
|
||||
@@ -55,9 +56,9 @@ export const CVMatchesPanel: React.FC<CVMatchesPanelProps> = ({
|
||||
</>
|
||||
);
|
||||
|
||||
interface EditMetadataPanelWrapperProps {
|
||||
rawFileDetails: any;
|
||||
}
|
||||
type EditMetadataPanelWrapperProps = {
|
||||
rawFileDetails?: RawFileDetails;
|
||||
};
|
||||
|
||||
export const EditMetadataPanelWrapper: React.FC<EditMetadataPanelWrapperProps> = ({
|
||||
rawFileDetails,
|
||||
|
||||
@@ -77,8 +77,7 @@ export const ArchiveOperations = (props: { data: any }): ReactElement => {
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error fetching uncompressed archive:", error);
|
||||
// Handle error if necessary
|
||||
// Error handling could be added here if needed
|
||||
}
|
||||
};
|
||||
fetchUncompressedArchive();
|
||||
|
||||
@@ -4,7 +4,6 @@ import prettyBytes from "pretty-bytes";
|
||||
|
||||
export const TorrentDownloads = (props) => {
|
||||
const { data } = props;
|
||||
console.log(Object.values(data));
|
||||
return (
|
||||
<>
|
||||
{data.map(({ torrent }) => {
|
||||
|
||||
@@ -43,7 +43,7 @@ export const TorrentSearchPanel = (props) => {
|
||||
mutationFn: async (newTorrent) =>
|
||||
axios.post(`${QBITTORRENT_SERVICE_BASE_URI}/addTorrent`, newTorrent),
|
||||
onSuccess: async (data) => {
|
||||
console.log(data);
|
||||
// Torrent added successfully
|
||||
},
|
||||
});
|
||||
const searchIndexer = (values) => {
|
||||
|
||||
@@ -3,29 +3,25 @@ import axios from "axios";
|
||||
import { isNil, isUndefined, isEmpty } from "lodash";
|
||||
import { refineQuery } from "filename-parser";
|
||||
import { COMICVINE_SERVICE_URI } from "../../constants/endpoints";
|
||||
import { RawFileDetails as RawFileDetailsType } from "../../graphql/generated";
|
||||
|
||||
interface ComicVineMatch {
|
||||
type ComicVineMatch = {
|
||||
score: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
};
|
||||
|
||||
interface ComicVineSearchQuery {
|
||||
type ComicVineSearchQuery = {
|
||||
inferredIssueDetails: {
|
||||
name: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
[key: string]: any;
|
||||
}
|
||||
};
|
||||
|
||||
interface RawFileDetails {
|
||||
name: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface ComicVineMetadata {
|
||||
type ComicVineMetadata = {
|
||||
name?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
};
|
||||
|
||||
export const useComicVineMatching = () => {
|
||||
const [comicVineMatches, setComicVineMatches] = useState<ComicVineMatch[]>([]);
|
||||
@@ -67,18 +63,18 @@ export const useComicVineMatching = () => {
|
||||
const scoredMatches = matches.sort((a: ComicVineMatch, b: ComicVineMatch) => b.score - a.score);
|
||||
setComicVineMatches(scoredMatches);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
// Error handling could be added here if needed
|
||||
}
|
||||
};
|
||||
|
||||
const prepareAndFetchMatches = (
|
||||
rawFileDetails: RawFileDetails | undefined,
|
||||
rawFileDetails: RawFileDetailsType | undefined,
|
||||
comicvine: ComicVineMetadata | undefined,
|
||||
) => {
|
||||
let seriesSearchQuery: ComicVineSearchQuery = {} as ComicVineSearchQuery;
|
||||
let issueSearchQuery: ComicVineSearchQuery = {} as ComicVineSearchQuery;
|
||||
|
||||
if (!isUndefined(rawFileDetails)) {
|
||||
if (!isUndefined(rawFileDetails) && rawFileDetails.name) {
|
||||
issueSearchQuery = refineQuery(rawFileDetails.name) as ComicVineSearchQuery;
|
||||
} else if (!isEmpty(comicvine) && comicvine?.name) {
|
||||
issueSearchQuery = refineQuery(comicvine.name) as ComicVineSearchQuery;
|
||||
|
||||
@@ -1,83 +1,70 @@
|
||||
import React, { ReactElement } from "react";
|
||||
import ZeroState from "./ZeroState";
|
||||
import { RecentlyImported } from "./RecentlyImported";
|
||||
import { WantedComicsList } from "./WantedComicsList";
|
||||
import { VolumeGroups } from "./VolumeGroups";
|
||||
import { LibraryStatistics } from "./LibraryStatistics";
|
||||
import { PullList } from "./PullList";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||
import {
|
||||
useGetRecentComicsQuery,
|
||||
useGetWantedComicsQuery,
|
||||
useGetVolumeGroupsQuery,
|
||||
useGetLibraryStatisticsQuery
|
||||
} from "../../graphql/generated";
|
||||
|
||||
export const Dashboard = (): ReactElement => {
|
||||
const { data: recentComics } = useQuery({
|
||||
queryFn: async () =>
|
||||
await axios({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBooks`,
|
||||
method: "POST",
|
||||
data: {
|
||||
paginationOptions: {
|
||||
page: 0,
|
||||
limit: 5,
|
||||
sort: { updatedAt: "-1" },
|
||||
},
|
||||
predicate: {
|
||||
wanted: { $exists: false },
|
||||
},
|
||||
comicStatus: "recent",
|
||||
},
|
||||
}),
|
||||
queryKey: ["recentComics"],
|
||||
});
|
||||
// Wanted Comics
|
||||
const { data: wantedComics } = useQuery({
|
||||
queryFn: async () =>
|
||||
await axios({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBooks`,
|
||||
method: "POST",
|
||||
data: {
|
||||
paginationOptions: {
|
||||
page: 0,
|
||||
limit: 5,
|
||||
sort: { updatedAt: "-1" },
|
||||
},
|
||||
predicate: {
|
||||
wanted: { $exists: true, $ne: null },
|
||||
},
|
||||
},
|
||||
}),
|
||||
queryKey: ["wantedComics"],
|
||||
});
|
||||
const { data: volumeGroups } = useQuery({
|
||||
queryFn: async () =>
|
||||
await axios({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookGroups`,
|
||||
method: "GET",
|
||||
}),
|
||||
queryKey: ["volumeGroups"],
|
||||
});
|
||||
// Use GraphQL for recent comics
|
||||
const { data: recentComicsData, error: recentComicsError } = useGetRecentComicsQuery(
|
||||
{ limit: 5 },
|
||||
{ refetchOnWindowFocus: false }
|
||||
);
|
||||
|
||||
const { data: statistics } = useQuery({
|
||||
queryFn: async () =>
|
||||
await axios({
|
||||
url: `${LIBRARY_SERVICE_BASE_URI}/libraryStatistics`,
|
||||
method: "GET",
|
||||
}),
|
||||
queryKey: ["libraryStatistics"],
|
||||
});
|
||||
// Wanted Comics - using GraphQL
|
||||
const { data: wantedComicsData, error: wantedComicsError } = useGetWantedComicsQuery(
|
||||
{
|
||||
paginationOptions: {
|
||||
page: 1,
|
||||
limit: 5,
|
||||
sort: '{"updatedAt": -1}'
|
||||
},
|
||||
predicate: '{"acquisition.source.wanted": true}'
|
||||
},
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
retry: false
|
||||
}
|
||||
);
|
||||
|
||||
// Volume Groups - using GraphQL
|
||||
const { data: volumeGroupsData, error: volumeGroupsError } = useGetVolumeGroupsQuery(
|
||||
undefined,
|
||||
{ refetchOnWindowFocus: false }
|
||||
);
|
||||
|
||||
// Library Statistics - using GraphQL
|
||||
const { data: statisticsData, error: statisticsError } = useGetLibraryStatisticsQuery(
|
||||
undefined,
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
retry: false
|
||||
}
|
||||
);
|
||||
|
||||
const recentComics = recentComicsData?.comics?.comics || [];
|
||||
const wantedComics = !wantedComicsError ? (wantedComicsData?.getComicBooks?.docs || []) : [];
|
||||
const volumeGroups = volumeGroupsData?.getComicBookGroups || [];
|
||||
const statistics = !statisticsError ? statisticsData?.getLibraryStatistics : undefined;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<div className="mx-auto max-w-screen-xl px-4 py-4 sm:px-6 sm:py-8 lg:px-8">
|
||||
<PullList />
|
||||
{recentComics && <RecentlyImported comics={recentComics?.data.docs} />}
|
||||
{recentComics.length > 0 && <RecentlyImported comics={recentComics} />}
|
||||
{/* Wanted comics */}
|
||||
<WantedComicsList comics={wantedComics?.data?.docs} />
|
||||
<WantedComicsList comics={wantedComics} />
|
||||
{/* Library Statistics */}
|
||||
{statistics && <LibraryStatistics stats={statistics?.data} />}
|
||||
{statistics && <LibraryStatistics stats={statistics} />}
|
||||
{/* Volume groups */}
|
||||
<VolumeGroups volumeGroups={volumeGroups?.data} />
|
||||
<VolumeGroups volumeGroups={volumeGroups} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import React, { ReactElement, useEffect } from "react";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import React, { ReactElement } from "react";
|
||||
import { isEmpty, isUndefined, map } from "lodash";
|
||||
import Header from "../shared/Header";
|
||||
import { GetLibraryStatisticsQuery } from "../../graphql/generated";
|
||||
|
||||
type LibraryStatisticsProps = {
|
||||
stats: GetLibraryStatisticsQuery['getLibraryStatistics'];
|
||||
};
|
||||
|
||||
export const LibraryStatistics = (
|
||||
props: ILibraryStatisticsProps,
|
||||
props: LibraryStatisticsProps,
|
||||
): ReactElement => {
|
||||
const { stats } = props;
|
||||
return (
|
||||
@@ -24,29 +28,30 @@ export const LibraryStatistics = (
|
||||
<dd className="text-3xl text-green-600 md:text-5xl">
|
||||
{props.stats.totalDocuments} files
|
||||
</dd>
|
||||
<dd>
|
||||
<span className="text-2xl text-green-600">
|
||||
{props.stats.comicDirectorySize &&
|
||||
prettyBytes(props.stats.comicDirectorySize)}
|
||||
</span>
|
||||
</dd>
|
||||
{props.stats.comicDirectorySize?.fileCount && (
|
||||
<dd>
|
||||
<span className="text-2xl text-green-600">
|
||||
{props.stats.comicDirectorySize.fileCount} comic files
|
||||
</span>
|
||||
</dd>
|
||||
)}
|
||||
</div>
|
||||
{/* comicinfo and comicvine tagged issues */}
|
||||
<div className="flex flex-col gap-4">
|
||||
{!isUndefined(props.stats.statistics) &&
|
||||
!isEmpty(props.stats.statistics[0].issues) && (
|
||||
!isEmpty(props.stats.statistics?.[0]?.issues) && (
|
||||
<div className="flex flex-col h-fit rounded-lg bg-green-100 dark:bg-green-200 px-4 py-3 text-center">
|
||||
<span className="text-xl">
|
||||
{props.stats.statistics[0].issues.length}
|
||||
{props.stats.statistics?.[0]?.issues?.length || 0}
|
||||
</span>{" "}
|
||||
tagged with ComicVine
|
||||
</div>
|
||||
)}
|
||||
{!isUndefined(props.stats.statistics) &&
|
||||
!isEmpty(props.stats.statistics[0].issuesWithComicInfoXML) && (
|
||||
!isEmpty(props.stats.statistics?.[0]?.issuesWithComicInfoXML) && (
|
||||
<div className="flex flex-col h-fit rounded-lg bg-green-100 dark:bg-green-200 px-4 py-3 text-center">
|
||||
<span className="text-xl">
|
||||
{props.stats.statistics[0].issuesWithComicInfoXML.length}
|
||||
{props.stats.statistics?.[0]?.issuesWithComicInfoXML?.length || 0}
|
||||
</span>{" "}
|
||||
<span className="tag is-warning has-text-weight-bold mr-2 ml-1">
|
||||
with ComicInfo.xml
|
||||
@@ -57,14 +62,14 @@ export const LibraryStatistics = (
|
||||
|
||||
<div className="">
|
||||
{!isUndefined(props.stats.statistics) &&
|
||||
!isEmpty(props.stats.statistics[0].fileTypes) &&
|
||||
map(props.stats.statistics[0].fileTypes, (fileType, idx) => {
|
||||
!isEmpty(props.stats.statistics?.[0]?.fileTypes) &&
|
||||
map(props.stats.statistics?.[0]?.fileTypes, (fileType, idx) => {
|
||||
return (
|
||||
<span
|
||||
key={idx}
|
||||
className="flex flex-col mb-4 h-fit text-xl rounded-lg bg-green-100 dark:bg-green-200 px-4 py-3 text-center"
|
||||
>
|
||||
{fileType.data.length} {fileType._id}
|
||||
{fileType.data.length} {fileType.id}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
@@ -75,20 +80,20 @@ export const LibraryStatistics = (
|
||||
{/* publisher with most issues */}
|
||||
{!isUndefined(props.stats.statistics) &&
|
||||
!isEmpty(
|
||||
props.stats.statistics[0].publisherWithMostComicsInLibrary[0],
|
||||
props.stats.statistics?.[0]?.publisherWithMostComicsInLibrary?.[0],
|
||||
) && (
|
||||
<>
|
||||
<span className="">
|
||||
{
|
||||
props.stats.statistics[0]
|
||||
.publisherWithMostComicsInLibrary[0]._id
|
||||
props.stats.statistics?.[0]
|
||||
?.publisherWithMostComicsInLibrary?.[0]?.id
|
||||
}
|
||||
</span>
|
||||
{" has the most issues "}
|
||||
<span className="">
|
||||
{
|
||||
props.stats.statistics[0]
|
||||
.publisherWithMostComicsInLibrary[0].count
|
||||
props.stats.statistics?.[0]
|
||||
?.publisherWithMostComicsInLibrary?.[0]?.count
|
||||
}
|
||||
</span>
|
||||
</>
|
||||
|
||||
@@ -1,31 +1,22 @@
|
||||
import React, { ReactElement, useState, useCallback } from "react";
|
||||
import React, { ReactElement, useState } from "react";
|
||||
import { map } from "lodash";
|
||||
import Card from "../shared/Carda";
|
||||
import Header from "../shared/Header";
|
||||
import { importToDB } from "../../actions/fileops.actions";
|
||||
import ellipsize from "ellipsize";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import axios from "axios";
|
||||
import rateLimiter from "axios-rate-limit";
|
||||
import { setupCache } from "axios-cache-interceptor";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import useEmblaCarousel from "embla-carousel-react";
|
||||
import { COMICVINE_SERVICE_URI, LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||
import { Field, Form } from "react-final-form";
|
||||
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||
import { Form } from "react-final-form";
|
||||
import DatePickerDialog from "../shared/DatePicker";
|
||||
import { format } from "date-fns";
|
||||
import { LocgMetadata, useGetWeeklyPullListQuery } from "../../graphql/generated";
|
||||
|
||||
type PullListProps = {
|
||||
issues: any;
|
||||
};
|
||||
interface PullListProps {
|
||||
issues?: LocgMetadata[];
|
||||
}
|
||||
|
||||
const http = rateLimiter(axios.create(), {
|
||||
maxRequests: 1,
|
||||
perMilliseconds: 1000,
|
||||
maxRPS: 1,
|
||||
});
|
||||
const cachedAxios = setupCache(axios);
|
||||
export const PullList = (): ReactElement => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
@@ -44,20 +35,22 @@ export const PullList = (): ReactElement => {
|
||||
});
|
||||
|
||||
const {
|
||||
data: pullList,
|
||||
data: pullListData,
|
||||
refetch,
|
||||
isSuccess,
|
||||
isLoading,
|
||||
isError,
|
||||
} = useQuery({
|
||||
queryFn: async (): any =>
|
||||
await cachedAxios(`${COMICVINE_SERVICE_URI}/getWeeklyPullList`, {
|
||||
method: "get",
|
||||
params: { startDate: inputValue, pageSize: "15", currentPage: "1" },
|
||||
}),
|
||||
queryKey: ["pullList", inputValue],
|
||||
} = useGetWeeklyPullListQuery({
|
||||
input: {
|
||||
startDate: inputValue,
|
||||
pageSize: 15,
|
||||
currentPage: 1,
|
||||
},
|
||||
});
|
||||
|
||||
// Transform the data to match the old structure
|
||||
const pullList = pullListData ? { data: pullListData.getWeeklyPullList } : undefined;
|
||||
|
||||
const { mutate: addToLibrary } = useMutation({
|
||||
mutationFn: async ({ sourceName, metadata }: { sourceName: string; metadata: any }) => {
|
||||
const comicBookMetadata = {
|
||||
@@ -146,7 +139,7 @@ export const PullList = (): ReactElement => {
|
||||
{isSuccess && !isLoading && (
|
||||
<div className="overflow-hidden" ref={emblaRef}>
|
||||
<div className="flex">
|
||||
{map(pullList?.data.result, (issue, idx) => {
|
||||
{map(pullList?.data.result, (issue: LocgMetadata, idx: number) => {
|
||||
return (
|
||||
<div
|
||||
key={idx}
|
||||
@@ -154,13 +147,13 @@ export const PullList = (): ReactElement => {
|
||||
>
|
||||
<Card
|
||||
orientation={"vertical-2"}
|
||||
imageUrl={issue.coverImageUrl}
|
||||
imageUrl={issue.cover || undefined}
|
||||
hasDetails
|
||||
title={ellipsize(issue.issueName, 25)}
|
||||
title={ellipsize(issue.name || 'Unknown', 25)}
|
||||
>
|
||||
<div className="px-1">
|
||||
<span className="inline-flex mb-2 items-center bg-slate-50 text-slate-800 text-xs font-medium px-2.5 py-1 rounded-md dark:text-slate-900 dark:bg-slate-400">
|
||||
{issue.publicationDate}
|
||||
{issue.publisher || 'Unknown Publisher'}
|
||||
</span>
|
||||
<div className="flex flex-row justify-end">
|
||||
<button
|
||||
|
||||
@@ -4,23 +4,19 @@ import { Link } from "react-router-dom";
|
||||
import ellipsize from "ellipsize";
|
||||
import { isEmpty, isNil, isUndefined, map } from "lodash";
|
||||
import { detectIssueTypes } from "../../shared/utils/tradepaperback.utils";
|
||||
import {
|
||||
determineCoverFile,
|
||||
determineExternalMetadata,
|
||||
} from "../../shared/utils/metadata.utils";
|
||||
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
||||
import { LIBRARY_SERVICE_HOST } from "../../constants/endpoints";
|
||||
import Header from "../shared/Header";
|
||||
import useEmblaCarousel from "embla-carousel-react";
|
||||
import { GetRecentComicsQuery } from "../../graphql/generated";
|
||||
|
||||
type RecentlyImportedProps = {
|
||||
comics: any;
|
||||
comics: GetRecentComicsQuery['comics']['comics'];
|
||||
};
|
||||
|
||||
export const RecentlyImported = (
|
||||
comics: RecentlyImportedProps,
|
||||
{ comics }: RecentlyImportedProps,
|
||||
): ReactElement => {
|
||||
console.log(comics);
|
||||
|
||||
// embla carousel
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel({
|
||||
loop: false,
|
||||
@@ -39,31 +35,30 @@ export const RecentlyImported = (
|
||||
<div className="-mr-10 sm:-mr-17 lg:-mr-29 xl:-mr-36 2xl:-mr-42 mt-3">
|
||||
<div className="overflow-hidden" ref={emblaRef}>
|
||||
<div className="flex">
|
||||
{comics?.comics.map(
|
||||
(
|
||||
{
|
||||
_id,
|
||||
{comics?.map((comic, idx) => {
|
||||
const {
|
||||
id,
|
||||
rawFileDetails,
|
||||
sourcedMetadata: { comicvine, comicInfo, locg },
|
||||
sourcedMetadata,
|
||||
canonicalMetadata,
|
||||
inferredMetadata,
|
||||
wanted: { source } = {},
|
||||
},
|
||||
idx,
|
||||
) => {
|
||||
} = comic;
|
||||
|
||||
// Parse sourced metadata (GraphQL returns as strings)
|
||||
const comicvine = typeof sourcedMetadata?.comicvine === 'string'
|
||||
? JSON.parse(sourcedMetadata.comicvine)
|
||||
: sourcedMetadata?.comicvine;
|
||||
const comicInfo = typeof sourcedMetadata?.comicInfo === 'string'
|
||||
? JSON.parse(sourcedMetadata.comicInfo)
|
||||
: sourcedMetadata?.comicInfo;
|
||||
const locg = sourcedMetadata?.locg;
|
||||
|
||||
const { issueName, url } = determineCoverFile({
|
||||
rawFileDetails,
|
||||
comicvine,
|
||||
comicInfo,
|
||||
locg,
|
||||
});
|
||||
const { issue, coverURL, icon } = determineExternalMetadata(
|
||||
source,
|
||||
{
|
||||
comicvine,
|
||||
comicInfo,
|
||||
locg,
|
||||
},
|
||||
);
|
||||
const isComicVineMetadataAvailable =
|
||||
!isUndefined(comicvine) &&
|
||||
!isUndefined(comicvine.volumeInformation);
|
||||
@@ -77,7 +72,7 @@ export const RecentlyImported = (
|
||||
<Card
|
||||
orientation="vertical-2"
|
||||
imageUrl={url}
|
||||
title={inferredMetadata.issue.name}
|
||||
title={inferredMetadata?.issue?.name}
|
||||
hasDetails
|
||||
cardState={cardState}
|
||||
>
|
||||
@@ -89,7 +84,7 @@ export const RecentlyImported = (
|
||||
<i className="icon-[solar--hashtag-outline]"></i>
|
||||
</span>
|
||||
<span className="text-md text-slate-900">
|
||||
{inferredMetadata.issue.number}
|
||||
{inferredMetadata?.issue?.number}
|
||||
</span>
|
||||
</span>
|
||||
{/* File extension */}
|
||||
@@ -99,7 +94,7 @@ export const RecentlyImported = (
|
||||
</span>
|
||||
|
||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||
{rawFileDetails.extension}
|
||||
{rawFileDetails?.extension}
|
||||
</span>
|
||||
</span>
|
||||
{/* Uncompressed status */}
|
||||
@@ -142,8 +137,7 @@ export const RecentlyImported = (
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
)}
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,12 +5,17 @@ import { Link, useNavigate } from "react-router-dom";
|
||||
import Card from "../shared/Carda";
|
||||
import Header from "../shared/Header";
|
||||
import useEmblaCarousel from "embla-carousel-react";
|
||||
import { GetVolumeGroupsQuery } from "../../graphql/generated";
|
||||
|
||||
export const VolumeGroups = (props): ReactElement => {
|
||||
type VolumeGroupsProps = {
|
||||
volumeGroups?: GetVolumeGroupsQuery['getComicBookGroups'];
|
||||
};
|
||||
|
||||
export const VolumeGroups = (props: VolumeGroupsProps): ReactElement => {
|
||||
// Till mongo gives us back the deduplicated results with the ObjectId
|
||||
const deduplicatedGroups = unionBy(props.volumeGroups, "volumes.id");
|
||||
const navigate = useNavigate();
|
||||
const navigateToVolumes = (row) => {
|
||||
const navigateToVolumes = (row: any) => {
|
||||
navigate(`/volumes/all`);
|
||||
};
|
||||
|
||||
@@ -36,18 +41,18 @@ export const VolumeGroups = (props): ReactElement => {
|
||||
{map(deduplicatedGroups, (data) => {
|
||||
return (
|
||||
<div
|
||||
key={data._id}
|
||||
key={data.id}
|
||||
className="flex-[0_0_200px] min-w-0 sm:flex-[0_0_220px] md:flex-[0_0_240px] lg:flex-[0_0_260px] xl:flex-[0_0_280px] pr-[15px]"
|
||||
>
|
||||
<Card
|
||||
orientation="vertical-2"
|
||||
imageUrl={data.volumes.image.small_url}
|
||||
imageUrl={data.volumes?.image?.small_url || undefined}
|
||||
hasDetails
|
||||
>
|
||||
<div className="py-3">
|
||||
<div className="text-sm">
|
||||
<Link to={`/volume/details/${data._id}`}>
|
||||
{ellipsize(data.volumes.name, 48)}
|
||||
<Link to={`/volume/details/${data.id}`}>
|
||||
{ellipsize(data.volumes?.name || 'Unknown', 48)}
|
||||
</Link>
|
||||
</div>
|
||||
{/* issue count */}
|
||||
@@ -57,7 +62,7 @@ export const VolumeGroups = (props): ReactElement => {
|
||||
</span>
|
||||
|
||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||
{data.volumes.count_of_issues} issues
|
||||
{data.volumes?.count_of_issues || 0} issues
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -7,9 +7,10 @@ import { detectIssueTypes } from "../../shared/utils/tradepaperback.utils";
|
||||
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
||||
import Header from "../shared/Header";
|
||||
import useEmblaCarousel from "embla-carousel-react";
|
||||
import { GetWantedComicsQuery } from "../../graphql/generated";
|
||||
|
||||
type WantedComicsListProps = {
|
||||
comics: any;
|
||||
comics?: GetWantedComicsQuery['getComicBooks']['docs'];
|
||||
};
|
||||
|
||||
export const WantedComicsList = ({
|
||||
@@ -38,12 +39,22 @@ export const WantedComicsList = ({
|
||||
<div className="flex">
|
||||
{map(
|
||||
comics,
|
||||
({
|
||||
_id,
|
||||
rawFileDetails,
|
||||
sourcedMetadata: { comicvine, comicInfo, locg },
|
||||
wanted,
|
||||
}) => {
|
||||
(comic) => {
|
||||
const {
|
||||
id,
|
||||
rawFileDetails,
|
||||
sourcedMetadata,
|
||||
} = comic;
|
||||
|
||||
// Parse sourced metadata (GraphQL returns as strings)
|
||||
const comicvine = typeof sourcedMetadata?.comicvine === 'string'
|
||||
? JSON.parse(sourcedMetadata.comicvine)
|
||||
: sourcedMetadata?.comicvine;
|
||||
const comicInfo = typeof sourcedMetadata?.comicInfo === 'string'
|
||||
? JSON.parse(sourcedMetadata.comicInfo)
|
||||
: sourcedMetadata?.comicInfo;
|
||||
const locg = sourcedMetadata?.locg;
|
||||
|
||||
const isComicBookMetadataAvailable = !isUndefined(comicvine);
|
||||
const consolidatedComicMetadata = {
|
||||
rawFileDetails,
|
||||
@@ -58,14 +69,14 @@ export const WantedComicsList = ({
|
||||
publisher = null,
|
||||
} = determineCoverFile(consolidatedComicMetadata);
|
||||
const titleElement = (
|
||||
<Link to={"/comic/details/" + _id}>
|
||||
<Link to={"/comic/details/" + id}>
|
||||
{ellipsize(issueName, 20)}
|
||||
<p>{publisher}</p>
|
||||
</Link>
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={_id}
|
||||
key={id}
|
||||
className="flex-[0_0_200px] min-w-0 sm:flex-[0_0_220px] md:flex-[0_0_240px] lg:flex-[0_0_260px] xl:flex-[0_0_280px] pr-[15px]"
|
||||
>
|
||||
<Card
|
||||
@@ -79,7 +90,7 @@ export const WantedComicsList = ({
|
||||
<div className="flex flex-row gap-2">
|
||||
{/* Issue type */}
|
||||
{isComicBookMetadataAvailable &&
|
||||
!isNil(detectIssueTypes(comicvine.description)) ? (
|
||||
!isNil(detectIssueTypes(comicvine?.description)) ? (
|
||||
<div className="my-2">
|
||||
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2.5 py-0.5 rounded-md dark:text-slate-900 dark:bg-slate-400">
|
||||
<span className="pr-1 pt-1">
|
||||
@@ -88,29 +99,14 @@ export const WantedComicsList = ({
|
||||
|
||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||
{
|
||||
detectIssueTypes(comicvine.description)
|
||||
.displayName
|
||||
detectIssueTypes(comicvine?.description)
|
||||
?.displayName
|
||||
}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
{/* issues marked as wanted, part of this volume */}
|
||||
{wanted?.markEntireVolumeWanted ? (
|
||||
<div className="text-sm">sagla volume pahije</div>
|
||||
) : (
|
||||
<div className="my-2">
|
||||
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2.5 py-0.5 rounded-md dark:text-slate-900 dark:bg-slate-400">
|
||||
<span className="pr-1 pt-1">
|
||||
<i className="icon-[solar--documents-bold-duotone] w-5 h-5"></i>
|
||||
</span>
|
||||
|
||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||
{wanted.issues.length}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{/* Wanted comics - info not available in current GraphQL query */}
|
||||
</div>
|
||||
{/* comicVine metadata presence */}
|
||||
{isComicBookMetadataAvailable && (
|
||||
|
||||
@@ -63,7 +63,6 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
|
||||
<div className="columns">
|
||||
<div className="column is-half">
|
||||
{bundles.map((bundle, idx) => {
|
||||
console.log(bundle);
|
||||
return (
|
||||
<div key={idx}>
|
||||
<MetadataPanel
|
||||
|
||||
@@ -78,7 +78,6 @@ export const Search = ({}: ISearchProps): ReactElement => {
|
||||
coverDate: cover_date,
|
||||
issueNumber: issue_number,
|
||||
});
|
||||
console.log(issues);
|
||||
// Get volume metadata from CV
|
||||
const response = await axios({
|
||||
url: `${COMICVINE_SERVICE_URI}/getVolumes`,
|
||||
@@ -111,7 +110,6 @@ export const Search = ({}: ISearchProps): ReactElement => {
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log("Invalid resource type.");
|
||||
break;
|
||||
}
|
||||
// Add to wanted list
|
||||
|
||||
@@ -35,7 +35,6 @@ export const AirDCPPSettingsForm = () => {
|
||||
// Handle setting update and subsequent AirDC++ initialization
|
||||
const { mutate } = useMutation({
|
||||
mutationFn: (values) => {
|
||||
console.log(values);
|
||||
return axios.post("http://localhost:3000/api/settings/saveSettings", {
|
||||
settingsPayload: values,
|
||||
settingsKey: "directConnect",
|
||||
|
||||
@@ -19,7 +19,6 @@ export const DockerVars = (): React.ReactElement => {
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
});
|
||||
console.log("Docker Vars: ", environmentVariables);
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<h2 className="text-xl font-semibold">Docker Environment Variables</h2>
|
||||
|
||||
@@ -19,7 +19,6 @@ export const ProwlarrSettingsForm = (props) => {
|
||||
},
|
||||
queryKey: ["prowlarrConnectionResult"],
|
||||
});
|
||||
console.log(data);
|
||||
const submitHandler = () => {};
|
||||
const initialData = {};
|
||||
return (
|
||||
|
||||
@@ -33,7 +33,6 @@ export const Volumes = (props): ReactElement => {
|
||||
}),
|
||||
queryKey: ["volumes"],
|
||||
});
|
||||
console.log(volumes);
|
||||
const columnData = useMemo(
|
||||
(): any => [
|
||||
{
|
||||
@@ -46,7 +45,6 @@ export const Volumes = (props): ReactElement => {
|
||||
const {
|
||||
_source: { sourcedMetadata },
|
||||
} = comicObject;
|
||||
console.log("jaggu", row.getValue());
|
||||
return (
|
||||
<div className="flex flex-row gap-3 mt-5">
|
||||
<Link to={`/volume/details/${comicObject._id}`}>
|
||||
|
||||
@@ -42,7 +42,6 @@ export const WantedComics = (props): ReactElement => {
|
||||
minWidth: 350,
|
||||
accessorFn: (data) => data,
|
||||
cell: (value) => {
|
||||
console.log("ASDASd", value);
|
||||
const row = value.getValue()._source;
|
||||
return row && <MetadataPanel data={row} />;
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Link } from "react-router-dom";
|
||||
|
||||
type IHeaderProps = {
|
||||
headerContent: string;
|
||||
subHeaderContent: ReactElement;
|
||||
subHeaderContent: ReactElement | string;
|
||||
iconClassNames: string;
|
||||
link?: string;
|
||||
};
|
||||
|
||||
76
src/client/graphql/adapters/comicAdapter.ts
Normal file
76
src/client/graphql/adapters/comicAdapter.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { GetComicByIdQuery } from '../generated';
|
||||
|
||||
/**
|
||||
* Adapter to transform GraphQL Comic response to legacy REST API format
|
||||
* This allows gradual migration while maintaining compatibility with existing components
|
||||
*/
|
||||
export function adaptGraphQLComicToLegacy(graphqlComic: GetComicByIdQuery['comic']) {
|
||||
if (!graphqlComic) return null;
|
||||
|
||||
// Parse sourced metadata (GraphQL returns as strings)
|
||||
const comicvine = graphqlComic.sourcedMetadata?.comicvine
|
||||
? (typeof graphqlComic.sourcedMetadata.comicvine === 'string'
|
||||
? JSON.parse(graphqlComic.sourcedMetadata.comicvine)
|
||||
: graphqlComic.sourcedMetadata.comicvine)
|
||||
: undefined;
|
||||
|
||||
const comicInfo = graphqlComic.sourcedMetadata?.comicInfo
|
||||
? (typeof graphqlComic.sourcedMetadata.comicInfo === 'string'
|
||||
? JSON.parse(graphqlComic.sourcedMetadata.comicInfo)
|
||||
: graphqlComic.sourcedMetadata.comicInfo)
|
||||
: undefined;
|
||||
|
||||
const locg = graphqlComic.sourcedMetadata?.locg || undefined;
|
||||
|
||||
// Use inferredMetadata from GraphQL response, or build from canonical metadata as fallback
|
||||
const inferredMetadata = graphqlComic.inferredMetadata || {
|
||||
issue: {
|
||||
name: graphqlComic.canonicalMetadata?.title?.value ||
|
||||
graphqlComic.canonicalMetadata?.series?.value ||
|
||||
graphqlComic.rawFileDetails?.name || '',
|
||||
number: graphqlComic.canonicalMetadata?.issueNumber?.value
|
||||
? parseInt(graphqlComic.canonicalMetadata.issueNumber.value, 10)
|
||||
: undefined,
|
||||
year: graphqlComic.canonicalMetadata?.publicationDate?.value?.substring(0, 4) ||
|
||||
graphqlComic.canonicalMetadata?.coverDate?.value?.substring(0, 4),
|
||||
subtitle: graphqlComic.canonicalMetadata?.series?.value,
|
||||
},
|
||||
};
|
||||
|
||||
// Build acquisition data (if available from importStatus or other fields)
|
||||
const acquisition = {
|
||||
directconnect: {
|
||||
downloads: [],
|
||||
},
|
||||
torrent: [],
|
||||
};
|
||||
|
||||
// Transform rawFileDetails to match expected format
|
||||
const rawFileDetails = graphqlComic.rawFileDetails ? {
|
||||
name: graphqlComic.rawFileDetails.name || '',
|
||||
filePath: graphqlComic.rawFileDetails.filePath,
|
||||
fileSize: graphqlComic.rawFileDetails.fileSize,
|
||||
extension: graphqlComic.rawFileDetails.extension,
|
||||
mimeType: graphqlComic.rawFileDetails.mimeType,
|
||||
containedIn: graphqlComic.rawFileDetails.containedIn,
|
||||
pageCount: graphqlComic.rawFileDetails.pageCount,
|
||||
archive: graphqlComic.rawFileDetails.archive,
|
||||
cover: graphqlComic.rawFileDetails.cover,
|
||||
} : undefined;
|
||||
|
||||
return {
|
||||
_id: graphqlComic.id,
|
||||
rawFileDetails,
|
||||
inferredMetadata,
|
||||
sourcedMetadata: {
|
||||
comicvine,
|
||||
locg,
|
||||
comicInfo,
|
||||
},
|
||||
acquisition,
|
||||
createdAt: graphqlComic.createdAt || new Date().toISOString(),
|
||||
updatedAt: graphqlComic.updatedAt || new Date().toISOString(),
|
||||
// Include the full GraphQL data for components that can use it
|
||||
__graphql: graphqlComic,
|
||||
} as any; // Use 'as any' to bypass strict type checking during migration
|
||||
}
|
||||
19
src/client/graphql/client.ts
Normal file
19
src/client/graphql/client.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
|
||||
import { LIBRARY_SERVICE_HOST } from '../constants/endpoints';
|
||||
|
||||
const httpLink = new HttpLink({
|
||||
uri: `${LIBRARY_SERVICE_HOST}/graphql`,
|
||||
});
|
||||
|
||||
export const apolloClient = new ApolloClient({
|
||||
link: httpLink,
|
||||
cache: new InMemoryCache(),
|
||||
defaultOptions: {
|
||||
watchQuery: {
|
||||
fetchPolicy: 'cache-and-network',
|
||||
},
|
||||
query: {
|
||||
fetchPolicy: 'network-only',
|
||||
},
|
||||
},
|
||||
});
|
||||
43
src/client/graphql/fetcher.ts
Normal file
43
src/client/graphql/fetcher.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { LIBRARY_SERVICE_HOST } from '../constants/endpoints';
|
||||
|
||||
export function fetcher<TData, TVariables>(
|
||||
query: string,
|
||||
variables?: TVariables,
|
||||
options?: RequestInit['headers']
|
||||
) {
|
||||
return async (): Promise<TData> => {
|
||||
try {
|
||||
const res = await fetch(`${LIBRARY_SERVICE_HOST}/graphql`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
variables,
|
||||
}),
|
||||
});
|
||||
|
||||
// Check if the response is OK (status 200-299)
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP error! status: ${res.status}`);
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
if (json.errors) {
|
||||
const { message } = json.errors[0] || {};
|
||||
throw new Error(message || 'Error fetching data');
|
||||
}
|
||||
|
||||
return json.data;
|
||||
} catch (error) {
|
||||
// Handle network errors or other fetch failures
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to fetch: ${error.message}`);
|
||||
}
|
||||
throw new Error('Failed to fetch data from server');
|
||||
}
|
||||
};
|
||||
}
|
||||
1848
src/client/graphql/generated.ts
Normal file
1848
src/client/graphql/generated.ts
Normal file
File diff suppressed because it is too large
Load Diff
191
src/client/graphql/queries/comicDetail.graphql
Normal file
191
src/client/graphql/queries/comicDetail.graphql
Normal file
@@ -0,0 +1,191 @@
|
||||
query GetComicById($id: ID!) {
|
||||
comic(id: $id) {
|
||||
id
|
||||
|
||||
# Inferred metadata
|
||||
inferredMetadata {
|
||||
issue {
|
||||
name
|
||||
number
|
||||
year
|
||||
subtitle
|
||||
}
|
||||
}
|
||||
|
||||
# Canonical metadata
|
||||
canonicalMetadata {
|
||||
title {
|
||||
value
|
||||
provenance {
|
||||
source
|
||||
sourceId
|
||||
confidence
|
||||
fetchedAt
|
||||
url
|
||||
}
|
||||
userOverride
|
||||
}
|
||||
series {
|
||||
value
|
||||
provenance {
|
||||
source
|
||||
sourceId
|
||||
confidence
|
||||
fetchedAt
|
||||
url
|
||||
}
|
||||
userOverride
|
||||
}
|
||||
volume {
|
||||
value
|
||||
provenance {
|
||||
source
|
||||
sourceId
|
||||
confidence
|
||||
fetchedAt
|
||||
url
|
||||
}
|
||||
userOverride
|
||||
}
|
||||
issueNumber {
|
||||
value
|
||||
provenance {
|
||||
source
|
||||
sourceId
|
||||
confidence
|
||||
fetchedAt
|
||||
url
|
||||
}
|
||||
userOverride
|
||||
}
|
||||
publisher {
|
||||
value
|
||||
provenance {
|
||||
source
|
||||
sourceId
|
||||
confidence
|
||||
fetchedAt
|
||||
url
|
||||
}
|
||||
userOverride
|
||||
}
|
||||
publicationDate {
|
||||
value
|
||||
provenance {
|
||||
source
|
||||
sourceId
|
||||
confidence
|
||||
fetchedAt
|
||||
url
|
||||
}
|
||||
userOverride
|
||||
}
|
||||
coverDate {
|
||||
value
|
||||
provenance {
|
||||
source
|
||||
sourceId
|
||||
confidence
|
||||
fetchedAt
|
||||
url
|
||||
}
|
||||
userOverride
|
||||
}
|
||||
description {
|
||||
value
|
||||
provenance {
|
||||
source
|
||||
sourceId
|
||||
confidence
|
||||
fetchedAt
|
||||
url
|
||||
}
|
||||
userOverride
|
||||
}
|
||||
creators {
|
||||
name
|
||||
role
|
||||
provenance {
|
||||
source
|
||||
sourceId
|
||||
confidence
|
||||
fetchedAt
|
||||
url
|
||||
}
|
||||
}
|
||||
pageCount {
|
||||
value
|
||||
provenance {
|
||||
source
|
||||
sourceId
|
||||
confidence
|
||||
fetchedAt
|
||||
url
|
||||
}
|
||||
userOverride
|
||||
}
|
||||
coverImage {
|
||||
value
|
||||
provenance {
|
||||
source
|
||||
sourceId
|
||||
confidence
|
||||
fetchedAt
|
||||
url
|
||||
}
|
||||
userOverride
|
||||
}
|
||||
}
|
||||
|
||||
# Sourced metadata
|
||||
sourcedMetadata {
|
||||
comicInfo
|
||||
comicvine
|
||||
metron
|
||||
gcd
|
||||
locg {
|
||||
name
|
||||
publisher
|
||||
url
|
||||
cover
|
||||
description
|
||||
price
|
||||
rating
|
||||
pulls
|
||||
potw
|
||||
}
|
||||
}
|
||||
|
||||
# Raw file details
|
||||
rawFileDetails {
|
||||
name
|
||||
filePath
|
||||
fileSize
|
||||
extension
|
||||
mimeType
|
||||
containedIn
|
||||
pageCount
|
||||
archive {
|
||||
uncompressed
|
||||
expandedPath
|
||||
}
|
||||
cover {
|
||||
filePath
|
||||
stats
|
||||
}
|
||||
}
|
||||
|
||||
# Import status
|
||||
importStatus {
|
||||
isImported
|
||||
tagged
|
||||
matchedResult {
|
||||
score
|
||||
}
|
||||
}
|
||||
|
||||
# Timestamps
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
216
src/client/graphql/queries/dashboard.graphql
Normal file
216
src/client/graphql/queries/dashboard.graphql
Normal file
@@ -0,0 +1,216 @@
|
||||
query GetComics($page: Int, $limit: Int, $search: String, $publisher: String, $series: String) {
|
||||
comics(page: $page, limit: $limit, search: $search, publisher: $publisher, series: $series) {
|
||||
comics {
|
||||
id
|
||||
inferredMetadata {
|
||||
issue {
|
||||
name
|
||||
number
|
||||
year
|
||||
subtitle
|
||||
}
|
||||
}
|
||||
rawFileDetails {
|
||||
name
|
||||
extension
|
||||
archive {
|
||||
uncompressed
|
||||
}
|
||||
}
|
||||
sourcedMetadata {
|
||||
comicvine
|
||||
comicInfo
|
||||
locg {
|
||||
name
|
||||
publisher
|
||||
cover
|
||||
}
|
||||
}
|
||||
canonicalMetadata {
|
||||
title {
|
||||
value
|
||||
}
|
||||
series {
|
||||
value
|
||||
}
|
||||
issueNumber {
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
totalCount
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
currentPage
|
||||
totalPages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query GetRecentComics($limit: Int) {
|
||||
comics(page: 1, limit: $limit) {
|
||||
comics {
|
||||
id
|
||||
inferredMetadata {
|
||||
issue {
|
||||
name
|
||||
number
|
||||
year
|
||||
subtitle
|
||||
}
|
||||
}
|
||||
rawFileDetails {
|
||||
name
|
||||
extension
|
||||
cover {
|
||||
filePath
|
||||
}
|
||||
archive {
|
||||
uncompressed
|
||||
}
|
||||
}
|
||||
sourcedMetadata {
|
||||
comicvine
|
||||
comicInfo
|
||||
locg {
|
||||
name
|
||||
publisher
|
||||
cover
|
||||
}
|
||||
}
|
||||
canonicalMetadata {
|
||||
title {
|
||||
value
|
||||
}
|
||||
series {
|
||||
value
|
||||
}
|
||||
issueNumber {
|
||||
value
|
||||
}
|
||||
publisher {
|
||||
value
|
||||
}
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
|
||||
query GetWantedComics($paginationOptions: PaginationOptionsInput!, $predicate: PredicateInput) {
|
||||
getComicBooks(paginationOptions: $paginationOptions, predicate: $predicate) {
|
||||
docs {
|
||||
id
|
||||
inferredMetadata {
|
||||
issue {
|
||||
name
|
||||
number
|
||||
year
|
||||
subtitle
|
||||
}
|
||||
}
|
||||
rawFileDetails {
|
||||
name
|
||||
extension
|
||||
cover {
|
||||
filePath
|
||||
}
|
||||
archive {
|
||||
uncompressed
|
||||
}
|
||||
}
|
||||
sourcedMetadata {
|
||||
comicvine
|
||||
comicInfo
|
||||
locg {
|
||||
name
|
||||
publisher
|
||||
cover
|
||||
}
|
||||
}
|
||||
canonicalMetadata {
|
||||
title {
|
||||
value
|
||||
}
|
||||
series {
|
||||
value
|
||||
}
|
||||
issueNumber {
|
||||
value
|
||||
}
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
totalDocs
|
||||
limit
|
||||
page
|
||||
totalPages
|
||||
hasNextPage
|
||||
hasPrevPage
|
||||
}
|
||||
}
|
||||
|
||||
query GetVolumeGroups {
|
||||
getComicBookGroups {
|
||||
id
|
||||
volumes {
|
||||
id
|
||||
name
|
||||
count_of_issues
|
||||
publisher {
|
||||
id
|
||||
name
|
||||
}
|
||||
start_year
|
||||
image {
|
||||
small_url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query GetLibraryStatistics {
|
||||
getLibraryStatistics {
|
||||
totalDocuments
|
||||
comicDirectorySize {
|
||||
fileCount
|
||||
}
|
||||
statistics {
|
||||
fileTypes {
|
||||
id
|
||||
data
|
||||
}
|
||||
issues {
|
||||
id {
|
||||
id
|
||||
name
|
||||
}
|
||||
data
|
||||
}
|
||||
fileLessComics {
|
||||
id
|
||||
}
|
||||
issuesWithComicInfoXML {
|
||||
id
|
||||
}
|
||||
publisherWithMostComicsInLibrary {
|
||||
id
|
||||
count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query GetWeeklyPullList($input: WeeklyPullListInput!) {
|
||||
getWeeklyPullList(input: $input) {
|
||||
result {
|
||||
name
|
||||
publisher
|
||||
cover
|
||||
}
|
||||
}
|
||||
}
|
||||
72
src/client/graphql/queries/library.graphql
Normal file
72
src/client/graphql/queries/library.graphql
Normal file
@@ -0,0 +1,72 @@
|
||||
# Library queries
|
||||
# Note: The Library component currently uses Elasticsearch for search functionality
|
||||
# These queries are prepared for when the backend supports GraphQL-based library queries
|
||||
|
||||
query GetLibraryComics($page: Int, $limit: Int, $search: String, $series: String) {
|
||||
comics(page: $page, limit: $limit, search: $search, series: $series) {
|
||||
comics {
|
||||
id
|
||||
inferredMetadata {
|
||||
issue {
|
||||
name
|
||||
number
|
||||
year
|
||||
subtitle
|
||||
}
|
||||
}
|
||||
rawFileDetails {
|
||||
name
|
||||
filePath
|
||||
fileSize
|
||||
extension
|
||||
mimeType
|
||||
pageCount
|
||||
archive {
|
||||
uncompressed
|
||||
}
|
||||
cover {
|
||||
filePath
|
||||
}
|
||||
}
|
||||
sourcedMetadata {
|
||||
comicvine
|
||||
comicInfo
|
||||
locg {
|
||||
name
|
||||
publisher
|
||||
cover
|
||||
}
|
||||
}
|
||||
canonicalMetadata {
|
||||
title {
|
||||
value
|
||||
}
|
||||
series {
|
||||
value
|
||||
}
|
||||
issueNumber {
|
||||
value
|
||||
}
|
||||
publisher {
|
||||
value
|
||||
}
|
||||
pageCount {
|
||||
value
|
||||
}
|
||||
}
|
||||
importStatus {
|
||||
isImported
|
||||
tagged
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
totalCount
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
currentPage
|
||||
totalPages
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,14 @@ import Volumes from "./components/Volumes/Volumes";
|
||||
import VolumeDetails from "./components/VolumeDetail/VolumeDetail";
|
||||
import WantedComics from "./components/WantedComics/WantedComics";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
|
||||
@@ -47,7 +47,6 @@ const onSearchSent = async (item, instance, unsubscribe, searchInfo) => {
|
||||
|
||||
if (results.length > 0) {
|
||||
// We have results, download the best one
|
||||
console.log("SASAAAA", results);
|
||||
// const result = results[0];
|
||||
// SocketService.post(`search/${instance.id}/results/${result.id}/download`, {
|
||||
// priority: Utils.toApiPriority(item.priority),
|
||||
|
||||
@@ -42,6 +42,6 @@ const getIssueTypeDisplayName = (
|
||||
return { displayName, results };
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
// Error handling could be added here if needed
|
||||
}
|
||||
};
|
||||
|
||||
@@ -47,12 +47,11 @@ export const useStore = create<StoreState>((set, get) => ({
|
||||
});
|
||||
|
||||
socket.on("connect", () => {
|
||||
console.log(`✅ Connected to ${namespace}:`, socket.id);
|
||||
// Socket connected successfully
|
||||
});
|
||||
|
||||
// Always listen for sessionInitialized in case backend creates a new session
|
||||
socket.on("sessionInitialized", (id) => {
|
||||
console.log("Session initialized with ID:", id);
|
||||
localStorage.setItem("sessionId", id);
|
||||
});
|
||||
|
||||
|
||||
3
src/client/types/index.d.ts
vendored
3
src/client/types/index.d.ts
vendored
@@ -6,3 +6,6 @@ declare module "*.png" {
|
||||
declare module "*.jpg";
|
||||
declare module "*.gif";
|
||||
declare module "*.less";
|
||||
|
||||
// Comic types are now generated from GraphQL schema
|
||||
// Import from '../../graphql/generated' instead
|
||||
|
||||
@@ -21,21 +21,7 @@ app.use(function (req, res, next) {
|
||||
// Getting data from body of requests
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
//
|
||||
// app.get("/", (req: Request, res: Response) => {
|
||||
// console.log("sending index.html");
|
||||
// res.sendFile(path.resolve(__dirname, "../dist/index.html"));
|
||||
// });
|
||||
|
||||
// REGISTER ROUTES
|
||||
// all of the routes will be prefixed with /api
|
||||
const routes: Router[] = Object.values(router);
|
||||
// app.use("/api", routes);
|
||||
|
||||
// Send index.html on root request
|
||||
// app.use(express.static("dist"));
|
||||
// app.use(express.static("public"));
|
||||
|
||||
// app.listen(port);
|
||||
|
||||
// console.log(`Server is listening on ${port}`);
|
||||
|
||||
Reference in New Issue
Block a user