🐘 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:watch": "jest --watch",
|
||||||
"test:coverage": "jest --coverage",
|
"test:coverage": "jest --coverage",
|
||||||
"storybook": "storybook dev -p 6006",
|
"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",
|
"author": "Rishi Ghan",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@apollo/client": "^4.1.6",
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
@@ -45,6 +48,7 @@
|
|||||||
"final-form": "^5.0.0",
|
"final-form": "^5.0.0",
|
||||||
"final-form-arrays": "^4.0.0",
|
"final-form-arrays": "^4.0.0",
|
||||||
"focus-trap-react": "^12.0.0",
|
"focus-trap-react": "^12.0.0",
|
||||||
|
"graphql": "^16.13.1",
|
||||||
"history": "^5.3.0",
|
"history": "^5.3.0",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
"i18next": "^25.8.13",
|
"i18next": "^25.8.13",
|
||||||
@@ -74,6 +78,7 @@
|
|||||||
"react-sliding-pane": "^7.3.0",
|
"react-sliding-pane": "^7.3.0",
|
||||||
"react-textarea-autosize": "^8.5.9",
|
"react-textarea-autosize": "^8.5.9",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
|
"rxjs": "^7.8.2",
|
||||||
"socket.io-client": "^4.8.3",
|
"socket.io-client": "^4.8.3",
|
||||||
"styled-components": "^6.3.11",
|
"styled-components": "^6.3.11",
|
||||||
"threetwo-ui-typings": "^1.0.14",
|
"threetwo-ui-typings": "^1.0.14",
|
||||||
@@ -83,6 +88,10 @@
|
|||||||
"zustand": "^5.0.11"
|
"zustand": "^5.0.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"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/solar": "^1.2.5",
|
||||||
"@iconify/json": "^2.2.443",
|
"@iconify/json": "^2.2.443",
|
||||||
"@iconify/tailwind": "^1.2.0",
|
"@iconify/tailwind": "^1.2.0",
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ export const toggleAirDCPPSocketConnectionStatus =
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.log("Can't set AirDC++ socket status.");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const getWeeklyPullList = (options) => async (dispatch) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
// Error handling could be added here if needed
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -73,10 +73,9 @@ export const comicinfoAPICall = (options) => async (dispatch) => {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.log("Could not complete request.");
|
break;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CV_API_GENERIC_FAILURE,
|
type: CV_API_GENERIC_FAILURE,
|
||||||
error,
|
error,
|
||||||
@@ -99,7 +98,6 @@ export const getIssuesForSeries =
|
|||||||
comicObjectID,
|
comicObjectID,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log(issues);
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CV_ISSUES_FOR_VOLUME_IN_LIBRARY_SUCCESS,
|
type: CV_ISSUES_FOR_VOLUME_IN_LIBRARY_SUCCESS,
|
||||||
issues: issues.data.results,
|
issues: issues.data.results,
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import {
|
|||||||
LS_SET_QUEUE_STATUS,
|
LS_SET_QUEUE_STATUS,
|
||||||
LS_IMPORT_JOB_STATISTICS_FETCHED,
|
LS_IMPORT_JOB_STATISTICS_FETCHED,
|
||||||
} from "../constants/action-types";
|
} from "../constants/action-types";
|
||||||
import { success } from "react-notification-system-redux";
|
|
||||||
|
|
||||||
import { isNil } from "lodash";
|
import { isNil } from "lodash";
|
||||||
|
|
||||||
@@ -151,7 +150,7 @@ export const getComicBooks = (options) => async (dispatch) => {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log("Unrecognized comic status.");
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -219,12 +218,11 @@ export const fetchVolumeGroups = () => async (dispatch) => {
|
|||||||
data: response.data,
|
data: response.data,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
// Error handling could be added here if needed
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const fetchComicVineMatches =
|
export const fetchComicVineMatches =
|
||||||
(searchPayload, issueSearchQuery, seriesSearchQuery?) => async (dispatch) => {
|
(searchPayload, issueSearchQuery, seriesSearchQuery?) => async (dispatch) => {
|
||||||
console.log(issueSearchQuery);
|
|
||||||
try {
|
try {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CV_API_CALL_IN_PROGRESS,
|
type: CV_API_CALL_IN_PROGRESS,
|
||||||
@@ -273,7 +271,7 @@ export const fetchComicVineMatches =
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
// Error handling could be added here if needed
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ export const fetchMetronResource = async (options) => {
|
|||||||
`${METRON_SERVICE_URI}/fetchResource`,
|
`${METRON_SERVICE_URI}/fetchResource`,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
console.log(metronResourceResults);
|
|
||||||
console.log("has more? ", !isNil(metronResourceResults.data.next));
|
|
||||||
const results = metronResourceResults.data.results.map((result) => {
|
const results = metronResourceResults.data.results.map((result) => {
|
||||||
return {
|
return {
|
||||||
label: result.name || result.__str__,
|
label: result.name || result.__str__,
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import React, { ReactElement, useEffect } from "react";
|
import React, { ReactElement, useEffect } from "react";
|
||||||
import { Outlet } from "react-router-dom";
|
import { Outlet } from "react-router-dom";
|
||||||
|
import { ApolloProvider } from "@apollo/client/react";
|
||||||
import { Navbar2 } from "./shared/Navbar2";
|
import { Navbar2 } from "./shared/Navbar2";
|
||||||
import { ToastContainer } from "react-toastify";
|
import { ToastContainer } from "react-toastify";
|
||||||
import "../assets/scss/App.css";
|
import "../assets/scss/App.css";
|
||||||
import { useStore } from "../store";
|
import { useStore } from "../store";
|
||||||
|
import { apolloClient } from "../graphql/client";
|
||||||
|
|
||||||
export const App = (): ReactElement => {
|
export const App = (): ReactElement => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -11,11 +13,11 @@ export const App = (): ReactElement => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<ApolloProvider client={apolloClient}>
|
||||||
<Navbar2 />
|
<Navbar2 />
|
||||||
<Outlet />
|
<Outlet />
|
||||||
<ToastContainer stacked hideProgressBar />
|
<ToastContainer stacked hideProgressBar />
|
||||||
</>
|
</ApolloProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,9 @@ export const AcquisitionPanel = (
|
|||||||
type,
|
type,
|
||||||
config,
|
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 SlidingPane from "react-sliding-pane";
|
||||||
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
||||||
import { styled } from "styled-components";
|
import { styled } from "styled-components";
|
||||||
|
import { RawFileDetails as RawFileDetailsType } from "../../graphql/generated";
|
||||||
|
|
||||||
// Extracted modules
|
// Extracted modules
|
||||||
import { useComicVineMatching } from "./useComicVineMatching";
|
import { useComicVineMatching } from "./useComicVineMatching";
|
||||||
@@ -22,45 +23,32 @@ const StyledSlidingPanel = styled(SlidingPane)`
|
|||||||
background: #ccc;
|
background: #ccc;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface RawFileDetails {
|
type InferredIssue = {
|
||||||
name: string;
|
|
||||||
cover?: {
|
|
||||||
filePath?: string;
|
|
||||||
};
|
|
||||||
containedIn?: string;
|
|
||||||
fileSize?: number;
|
|
||||||
path?: string;
|
|
||||||
extension?: string;
|
|
||||||
mimeType?: string;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InferredIssue {
|
|
||||||
name?: string;
|
name?: string;
|
||||||
number?: number;
|
number?: number;
|
||||||
year?: string;
|
year?: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ComicVineMetadata {
|
type ComicVineMetadata = {
|
||||||
name?: string;
|
name?: string;
|
||||||
volumeInformation?: any;
|
volumeInformation?: any;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Acquisition {
|
type Acquisition = {
|
||||||
directconnect?: {
|
directconnect?: {
|
||||||
downloads?: any[];
|
downloads?: any[];
|
||||||
};
|
};
|
||||||
torrent?: any[];
|
torrent?: any[];
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ComicDetailProps {
|
type ComicDetailProps = {
|
||||||
data: {
|
data: {
|
||||||
_id: string;
|
_id: string;
|
||||||
rawFileDetails?: RawFileDetails;
|
rawFileDetails?: RawFileDetailsType;
|
||||||
inferredMetadata: {
|
inferredMetadata: {
|
||||||
issue?: InferredIssue;
|
issue?: InferredIssue;
|
||||||
};
|
};
|
||||||
@@ -76,7 +64,7 @@ interface ComicDetailProps {
|
|||||||
userSettings?: any;
|
userSettings?: any;
|
||||||
queryClient?: any;
|
queryClient?: any;
|
||||||
comicObjectId?: string;
|
comicObjectId?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for displaying the metadata for a comic in greater detail.
|
* 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) => {
|
const afterOpenModal = useCallback((things: any) => {
|
||||||
console.log("kolaveri", things);
|
// Modal opened callback
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const closeModal = useCallback(() => {
|
const closeModal = useCallback(() => {
|
||||||
@@ -154,7 +142,6 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
|||||||
openEditMetadataPanel();
|
openEditMetadataPanel();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log("No valid action selected.");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
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, useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
import { useGetComicByIdQuery } from "../../graphql/generated";
|
||||||
import axios from "axios";
|
import { adaptGraphQLComicToLegacy } from "../../graphql/adapters/comicAdapter";
|
||||||
|
|
||||||
export const ComicDetailContainer = (): ReactElement | null => {
|
export const ComicDetailContainer = (): ReactElement | null => {
|
||||||
const { comicObjectId } = useParams<{ comicObjectId: string }>();
|
const { comicObjectId } = useParams<{ comicObjectId: string }>();
|
||||||
@@ -13,31 +13,28 @@ export const ComicDetailContainer = (): ReactElement | null => {
|
|||||||
data: comicBookDetailData,
|
data: comicBookDetailData,
|
||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
} = useQuery({
|
} = useGetComicByIdQuery(
|
||||||
queryKey: ["comicBookMetadata", comicObjectId],
|
{ id: comicObjectId! },
|
||||||
queryFn: async () =>
|
{ enabled: !!comicObjectId }
|
||||||
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}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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) => {
|
const onDownloadTick = (data: DownloadTickData) => {
|
||||||
// Compare numeric data.id to string bundleId
|
// Compare numeric data.id to string bundleId
|
||||||
console.log(data.id);
|
|
||||||
console.log(`bundleId is ${bundleId}`)
|
|
||||||
if (data.id === parseInt(bundleId, 10)) {
|
if (data.id === parseInt(bundleId, 10)) {
|
||||||
setTick(data);
|
setTick(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,12 @@
|
|||||||
import React, { ReactElement } from "react";
|
import React, { ReactElement, ReactNode } from "react";
|
||||||
import prettyBytes from "pretty-bytes";
|
import prettyBytes from "pretty-bytes";
|
||||||
import { isEmpty } from "lodash";
|
import { isEmpty } from "lodash";
|
||||||
import { format, parseISO } from "date-fns";
|
import { format, parseISO } from "date-fns";
|
||||||
|
import { RawFileDetails as RawFileDetailsType } from "../../graphql/generated";
|
||||||
|
|
||||||
interface RawFileDetailsProps {
|
type RawFileDetailsProps = {
|
||||||
data?: {
|
data?: {
|
||||||
rawFileDetails?: {
|
rawFileDetails?: RawFileDetailsType;
|
||||||
containedIn?: string;
|
|
||||||
name?: string;
|
|
||||||
fileSize?: number;
|
|
||||||
path?: string;
|
|
||||||
extension?: string;
|
|
||||||
mimeType?: string;
|
|
||||||
cover?: {
|
|
||||||
filePath?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
inferredMetadata?: {
|
inferredMetadata?: {
|
||||||
issue?: {
|
issue?: {
|
||||||
year?: string;
|
year?: string;
|
||||||
@@ -27,18 +18,18 @@ interface RawFileDetailsProps {
|
|||||||
created_at?: string;
|
created_at?: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
};
|
};
|
||||||
children?: any;
|
children?: ReactNode;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
||||||
const { rawFileDetails, inferredMetadata, created_at, updated_at } =
|
const { rawFileDetails, inferredMetadata, created_at, updated_at } =
|
||||||
props.data;
|
props.data || {};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="max-w-2xl ml-5">
|
<div className="max-w-2xl ml-5">
|
||||||
<div className="px-4 sm:px-6">
|
<div className="px-4 sm:px-6">
|
||||||
<p className="text-gray-500 dark:text-gray-400">
|
<p className="text-gray-500 dark:text-gray-400">
|
||||||
<span className="text-xl">{rawFileDetails.name}</span>
|
<span className="text-xl">{rawFileDetails?.name}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-4 py-5 sm:px-6">
|
<div className="px-4 py-5 sm:px-6">
|
||||||
@@ -48,10 +39,10 @@ export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
|||||||
Raw File Details
|
Raw File Details
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
||||||
{rawFileDetails.containedIn +
|
{rawFileDetails?.containedIn}
|
||||||
"/" +
|
{"/"}
|
||||||
rawFileDetails.name +
|
{rawFileDetails?.name}
|
||||||
rawFileDetails.extension}
|
{rawFileDetails?.extension}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:col-span-1">
|
<div className="sm:col-span-1">
|
||||||
@@ -59,10 +50,10 @@ export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
|||||||
Inferred Issue Metadata
|
Inferred Issue Metadata
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
||||||
Series Name: {inferredMetadata.issue.name}
|
Series Name: {inferredMetadata?.issue?.name}
|
||||||
{!isEmpty(inferredMetadata.issue.number) ? (
|
{!isEmpty(inferredMetadata?.issue?.number) ? (
|
||||||
<span className="tag is-primary is-light">
|
<span className="tag is-primary is-light">
|
||||||
{inferredMetadata.issue.number}
|
{inferredMetadata?.issue?.number}
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
</dd>
|
</dd>
|
||||||
@@ -79,7 +70,7 @@ export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||||
{rawFileDetails.mimeType}
|
{rawFileDetails?.mimeType}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</dd>
|
</dd>
|
||||||
@@ -96,7 +87,7 @@ export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||||
{prettyBytes(rawFileDetails.fileSize)}
|
{rawFileDetails?.fileSize ? prettyBytes(rawFileDetails.fileSize) : "N/A"}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</dd>
|
</dd>
|
||||||
@@ -106,8 +97,12 @@ export const RawFileDetails = (props: RawFileDetailsProps): ReactElement => {
|
|||||||
Import Details
|
Import Details
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
||||||
{format(parseISO(created_at), "dd MMMM, yyyy")},{" "}
|
{created_at ? (
|
||||||
{format(parseISO(created_at), "h aaaa")}
|
<>
|
||||||
|
{format(parseISO(created_at), "dd MMMM, yyyy")},{" "}
|
||||||
|
{format(parseISO(created_at), "h aaaa")}
|
||||||
|
</>
|
||||||
|
) : "N/A"}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:col-span-2">
|
<div className="sm:col-span-2">
|
||||||
|
|||||||
@@ -2,17 +2,18 @@ import React from "react";
|
|||||||
import { ComicVineSearchForm } from "./ComicVineSearchForm";
|
import { ComicVineSearchForm } from "./ComicVineSearchForm";
|
||||||
import { ComicVineMatchPanel } from "./ComicVineMatchPanel";
|
import { ComicVineMatchPanel } from "./ComicVineMatchPanel";
|
||||||
import { EditMetadataPanel } from "./EditMetadataPanel";
|
import { EditMetadataPanel } from "./EditMetadataPanel";
|
||||||
|
import { RawFileDetails } from "../../graphql/generated";
|
||||||
|
|
||||||
interface InferredIssue {
|
type InferredIssue = {
|
||||||
name?: string;
|
name?: string;
|
||||||
number?: number;
|
number?: number;
|
||||||
year?: string;
|
year?: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface CVMatchesPanelProps {
|
type CVMatchesPanelProps = {
|
||||||
rawFileDetails: any;
|
rawFileDetails?: RawFileDetails;
|
||||||
inferredMetadata: {
|
inferredMetadata: {
|
||||||
issue?: InferredIssue;
|
issue?: InferredIssue;
|
||||||
};
|
};
|
||||||
@@ -20,7 +21,7 @@ interface CVMatchesPanelProps {
|
|||||||
comicObjectId: string;
|
comicObjectId: string;
|
||||||
queryClient: any;
|
queryClient: any;
|
||||||
onMatchApplied: () => void;
|
onMatchApplied: () => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const CVMatchesPanel: React.FC<CVMatchesPanelProps> = ({
|
export const CVMatchesPanel: React.FC<CVMatchesPanelProps> = ({
|
||||||
rawFileDetails,
|
rawFileDetails,
|
||||||
@@ -55,9 +56,9 @@ export const CVMatchesPanel: React.FC<CVMatchesPanelProps> = ({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
interface EditMetadataPanelWrapperProps {
|
type EditMetadataPanelWrapperProps = {
|
||||||
rawFileDetails: any;
|
rawFileDetails?: RawFileDetails;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const EditMetadataPanelWrapper: React.FC<EditMetadataPanelWrapperProps> = ({
|
export const EditMetadataPanelWrapper: React.FC<EditMetadataPanelWrapperProps> = ({
|
||||||
rawFileDetails,
|
rawFileDetails,
|
||||||
|
|||||||
@@ -77,8 +77,7 @@ export const ArchiveOperations = (props: { data: any }): ReactElement => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching uncompressed archive:", error);
|
// Error handling could be added here if needed
|
||||||
// Handle error if necessary
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchUncompressedArchive();
|
fetchUncompressedArchive();
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import prettyBytes from "pretty-bytes";
|
|||||||
|
|
||||||
export const TorrentDownloads = (props) => {
|
export const TorrentDownloads = (props) => {
|
||||||
const { data } = props;
|
const { data } = props;
|
||||||
console.log(Object.values(data));
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{data.map(({ torrent }) => {
|
{data.map(({ torrent }) => {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const TorrentSearchPanel = (props) => {
|
|||||||
mutationFn: async (newTorrent) =>
|
mutationFn: async (newTorrent) =>
|
||||||
axios.post(`${QBITTORRENT_SERVICE_BASE_URI}/addTorrent`, newTorrent),
|
axios.post(`${QBITTORRENT_SERVICE_BASE_URI}/addTorrent`, newTorrent),
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
console.log(data);
|
// Torrent added successfully
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const searchIndexer = (values) => {
|
const searchIndexer = (values) => {
|
||||||
|
|||||||
@@ -3,29 +3,25 @@ import axios from "axios";
|
|||||||
import { isNil, isUndefined, isEmpty } from "lodash";
|
import { isNil, isUndefined, isEmpty } from "lodash";
|
||||||
import { refineQuery } from "filename-parser";
|
import { refineQuery } from "filename-parser";
|
||||||
import { COMICVINE_SERVICE_URI } from "../../constants/endpoints";
|
import { COMICVINE_SERVICE_URI } from "../../constants/endpoints";
|
||||||
|
import { RawFileDetails as RawFileDetailsType } from "../../graphql/generated";
|
||||||
|
|
||||||
interface ComicVineMatch {
|
type ComicVineMatch = {
|
||||||
score: number;
|
score: number;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ComicVineSearchQuery {
|
type ComicVineSearchQuery = {
|
||||||
inferredIssueDetails: {
|
inferredIssueDetails: {
|
||||||
name: string;
|
name: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface RawFileDetails {
|
type ComicVineMetadata = {
|
||||||
name: string;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ComicVineMetadata {
|
|
||||||
name?: string;
|
name?: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const useComicVineMatching = () => {
|
export const useComicVineMatching = () => {
|
||||||
const [comicVineMatches, setComicVineMatches] = useState<ComicVineMatch[]>([]);
|
const [comicVineMatches, setComicVineMatches] = useState<ComicVineMatch[]>([]);
|
||||||
@@ -67,18 +63,18 @@ export const useComicVineMatching = () => {
|
|||||||
const scoredMatches = matches.sort((a: ComicVineMatch, b: ComicVineMatch) => b.score - a.score);
|
const scoredMatches = matches.sort((a: ComicVineMatch, b: ComicVineMatch) => b.score - a.score);
|
||||||
setComicVineMatches(scoredMatches);
|
setComicVineMatches(scoredMatches);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
// Error handling could be added here if needed
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const prepareAndFetchMatches = (
|
const prepareAndFetchMatches = (
|
||||||
rawFileDetails: RawFileDetails | undefined,
|
rawFileDetails: RawFileDetailsType | undefined,
|
||||||
comicvine: ComicVineMetadata | undefined,
|
comicvine: ComicVineMetadata | undefined,
|
||||||
) => {
|
) => {
|
||||||
let seriesSearchQuery: ComicVineSearchQuery = {} as ComicVineSearchQuery;
|
let seriesSearchQuery: ComicVineSearchQuery = {} as ComicVineSearchQuery;
|
||||||
let issueSearchQuery: ComicVineSearchQuery = {} as ComicVineSearchQuery;
|
let issueSearchQuery: ComicVineSearchQuery = {} as ComicVineSearchQuery;
|
||||||
|
|
||||||
if (!isUndefined(rawFileDetails)) {
|
if (!isUndefined(rawFileDetails) && rawFileDetails.name) {
|
||||||
issueSearchQuery = refineQuery(rawFileDetails.name) as ComicVineSearchQuery;
|
issueSearchQuery = refineQuery(rawFileDetails.name) as ComicVineSearchQuery;
|
||||||
} else if (!isEmpty(comicvine) && comicvine?.name) {
|
} else if (!isEmpty(comicvine) && comicvine?.name) {
|
||||||
issueSearchQuery = refineQuery(comicvine.name) as ComicVineSearchQuery;
|
issueSearchQuery = refineQuery(comicvine.name) as ComicVineSearchQuery;
|
||||||
|
|||||||
@@ -1,83 +1,70 @@
|
|||||||
import React, { ReactElement } from "react";
|
import React, { ReactElement } from "react";
|
||||||
import ZeroState from "./ZeroState";
|
|
||||||
import { RecentlyImported } from "./RecentlyImported";
|
import { RecentlyImported } from "./RecentlyImported";
|
||||||
import { WantedComicsList } from "./WantedComicsList";
|
import { WantedComicsList } from "./WantedComicsList";
|
||||||
import { VolumeGroups } from "./VolumeGroups";
|
import { VolumeGroups } from "./VolumeGroups";
|
||||||
import { LibraryStatistics } from "./LibraryStatistics";
|
import { LibraryStatistics } from "./LibraryStatistics";
|
||||||
import { PullList } from "./PullList";
|
import { PullList } from "./PullList";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import {
|
||||||
import axios from "axios";
|
useGetRecentComicsQuery,
|
||||||
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
useGetWantedComicsQuery,
|
||||||
|
useGetVolumeGroupsQuery,
|
||||||
|
useGetLibraryStatisticsQuery
|
||||||
|
} from "../../graphql/generated";
|
||||||
|
|
||||||
export const Dashboard = (): ReactElement => {
|
export const Dashboard = (): ReactElement => {
|
||||||
const { data: recentComics } = useQuery({
|
// Use GraphQL for recent comics
|
||||||
queryFn: async () =>
|
const { data: recentComicsData, error: recentComicsError } = useGetRecentComicsQuery(
|
||||||
await axios({
|
{ limit: 5 },
|
||||||
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBooks`,
|
{ refetchOnWindowFocus: false }
|
||||||
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"],
|
|
||||||
});
|
|
||||||
|
|
||||||
const { data: statistics } = useQuery({
|
// Wanted Comics - using GraphQL
|
||||||
queryFn: async () =>
|
const { data: wantedComicsData, error: wantedComicsError } = useGetWantedComicsQuery(
|
||||||
await axios({
|
{
|
||||||
url: `${LIBRARY_SERVICE_BASE_URI}/libraryStatistics`,
|
paginationOptions: {
|
||||||
method: "GET",
|
page: 1,
|
||||||
}),
|
limit: 5,
|
||||||
queryKey: ["libraryStatistics"],
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
<div className="mx-auto max-w-screen-xl px-4 py-4 sm:px-6 sm:py-8 lg:px-8">
|
<div className="mx-auto max-w-screen-xl px-4 py-4 sm:px-6 sm:py-8 lg:px-8">
|
||||||
<PullList />
|
<PullList />
|
||||||
{recentComics && <RecentlyImported comics={recentComics?.data.docs} />}
|
{recentComics.length > 0 && <RecentlyImported comics={recentComics} />}
|
||||||
{/* Wanted comics */}
|
{/* Wanted comics */}
|
||||||
<WantedComicsList comics={wantedComics?.data?.docs} />
|
<WantedComicsList comics={wantedComics} />
|
||||||
{/* Library Statistics */}
|
{/* Library Statistics */}
|
||||||
{statistics && <LibraryStatistics stats={statistics?.data} />}
|
{statistics && <LibraryStatistics stats={statistics} />}
|
||||||
{/* Volume groups */}
|
{/* Volume groups */}
|
||||||
<VolumeGroups volumeGroups={volumeGroups?.data} />
|
<VolumeGroups volumeGroups={volumeGroups} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import React, { ReactElement, useEffect } from "react";
|
import React, { ReactElement } from "react";
|
||||||
import prettyBytes from "pretty-bytes";
|
|
||||||
import { isEmpty, isUndefined, map } from "lodash";
|
import { isEmpty, isUndefined, map } from "lodash";
|
||||||
import Header from "../shared/Header";
|
import Header from "../shared/Header";
|
||||||
|
import { GetLibraryStatisticsQuery } from "../../graphql/generated";
|
||||||
|
|
||||||
|
type LibraryStatisticsProps = {
|
||||||
|
stats: GetLibraryStatisticsQuery['getLibraryStatistics'];
|
||||||
|
};
|
||||||
|
|
||||||
export const LibraryStatistics = (
|
export const LibraryStatistics = (
|
||||||
props: ILibraryStatisticsProps,
|
props: LibraryStatisticsProps,
|
||||||
): ReactElement => {
|
): ReactElement => {
|
||||||
const { stats } = props;
|
const { stats } = props;
|
||||||
return (
|
return (
|
||||||
@@ -24,29 +28,30 @@ export const LibraryStatistics = (
|
|||||||
<dd className="text-3xl text-green-600 md:text-5xl">
|
<dd className="text-3xl text-green-600 md:text-5xl">
|
||||||
{props.stats.totalDocuments} files
|
{props.stats.totalDocuments} files
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
{props.stats.comicDirectorySize?.fileCount && (
|
||||||
<span className="text-2xl text-green-600">
|
<dd>
|
||||||
{props.stats.comicDirectorySize &&
|
<span className="text-2xl text-green-600">
|
||||||
prettyBytes(props.stats.comicDirectorySize)}
|
{props.stats.comicDirectorySize.fileCount} comic files
|
||||||
</span>
|
</span>
|
||||||
</dd>
|
</dd>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* comicinfo and comicvine tagged issues */}
|
{/* comicinfo and comicvine tagged issues */}
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{!isUndefined(props.stats.statistics) &&
|
{!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">
|
<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">
|
<span className="text-xl">
|
||||||
{props.stats.statistics[0].issues.length}
|
{props.stats.statistics?.[0]?.issues?.length || 0}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
tagged with ComicVine
|
tagged with ComicVine
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isUndefined(props.stats.statistics) &&
|
{!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">
|
<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">
|
<span className="text-xl">
|
||||||
{props.stats.statistics[0].issuesWithComicInfoXML.length}
|
{props.stats.statistics?.[0]?.issuesWithComicInfoXML?.length || 0}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
<span className="tag is-warning has-text-weight-bold mr-2 ml-1">
|
<span className="tag is-warning has-text-weight-bold mr-2 ml-1">
|
||||||
with ComicInfo.xml
|
with ComicInfo.xml
|
||||||
@@ -57,14 +62,14 @@ export const LibraryStatistics = (
|
|||||||
|
|
||||||
<div className="">
|
<div className="">
|
||||||
{!isUndefined(props.stats.statistics) &&
|
{!isUndefined(props.stats.statistics) &&
|
||||||
!isEmpty(props.stats.statistics[0].fileTypes) &&
|
!isEmpty(props.stats.statistics?.[0]?.fileTypes) &&
|
||||||
map(props.stats.statistics[0].fileTypes, (fileType, idx) => {
|
map(props.stats.statistics?.[0]?.fileTypes, (fileType, idx) => {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
key={idx}
|
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"
|
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>
|
</span>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -75,20 +80,20 @@ export const LibraryStatistics = (
|
|||||||
{/* publisher with most issues */}
|
{/* publisher with most issues */}
|
||||||
{!isUndefined(props.stats.statistics) &&
|
{!isUndefined(props.stats.statistics) &&
|
||||||
!isEmpty(
|
!isEmpty(
|
||||||
props.stats.statistics[0].publisherWithMostComicsInLibrary[0],
|
props.stats.statistics?.[0]?.publisherWithMostComicsInLibrary?.[0],
|
||||||
) && (
|
) && (
|
||||||
<>
|
<>
|
||||||
<span className="">
|
<span className="">
|
||||||
{
|
{
|
||||||
props.stats.statistics[0]
|
props.stats.statistics?.[0]
|
||||||
.publisherWithMostComicsInLibrary[0]._id
|
?.publisherWithMostComicsInLibrary?.[0]?.id
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
{" has the most issues "}
|
{" has the most issues "}
|
||||||
<span className="">
|
<span className="">
|
||||||
{
|
{
|
||||||
props.stats.statistics[0]
|
props.stats.statistics?.[0]
|
||||||
.publisherWithMostComicsInLibrary[0].count
|
?.publisherWithMostComicsInLibrary?.[0]?.count
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,31 +1,22 @@
|
|||||||
import React, { ReactElement, useState, useCallback } from "react";
|
import React, { ReactElement, useState } from "react";
|
||||||
import { map } from "lodash";
|
import { map } from "lodash";
|
||||||
import Card from "../shared/Carda";
|
import Card from "../shared/Carda";
|
||||||
import Header from "../shared/Header";
|
import Header from "../shared/Header";
|
||||||
import { importToDB } from "../../actions/fileops.actions";
|
|
||||||
import ellipsize from "ellipsize";
|
import ellipsize from "ellipsize";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import rateLimiter from "axios-rate-limit";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { setupCache } from "axios-cache-interceptor";
|
|
||||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
||||||
import useEmblaCarousel from "embla-carousel-react";
|
import useEmblaCarousel from "embla-carousel-react";
|
||||||
import { COMICVINE_SERVICE_URI, LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||||
import { Field, Form } from "react-final-form";
|
import { Form } from "react-final-form";
|
||||||
import DatePickerDialog from "../shared/DatePicker";
|
import DatePickerDialog from "../shared/DatePicker";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
|
import { LocgMetadata, useGetWeeklyPullListQuery } from "../../graphql/generated";
|
||||||
|
|
||||||
type PullListProps = {
|
interface PullListProps {
|
||||||
issues: any;
|
issues?: LocgMetadata[];
|
||||||
};
|
}
|
||||||
|
|
||||||
const http = rateLimiter(axios.create(), {
|
|
||||||
maxRequests: 1,
|
|
||||||
perMilliseconds: 1000,
|
|
||||||
maxRPS: 1,
|
|
||||||
});
|
|
||||||
const cachedAxios = setupCache(axios);
|
|
||||||
export const PullList = (): ReactElement => {
|
export const PullList = (): ReactElement => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
@@ -44,20 +35,22 @@ export const PullList = (): ReactElement => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: pullList,
|
data: pullListData,
|
||||||
refetch,
|
refetch,
|
||||||
isSuccess,
|
isSuccess,
|
||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
} = useQuery({
|
} = useGetWeeklyPullListQuery({
|
||||||
queryFn: async (): any =>
|
input: {
|
||||||
await cachedAxios(`${COMICVINE_SERVICE_URI}/getWeeklyPullList`, {
|
startDate: inputValue,
|
||||||
method: "get",
|
pageSize: 15,
|
||||||
params: { startDate: inputValue, pageSize: "15", currentPage: "1" },
|
currentPage: 1,
|
||||||
}),
|
},
|
||||||
queryKey: ["pullList", inputValue],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Transform the data to match the old structure
|
||||||
|
const pullList = pullListData ? { data: pullListData.getWeeklyPullList } : undefined;
|
||||||
|
|
||||||
const { mutate: addToLibrary } = useMutation({
|
const { mutate: addToLibrary } = useMutation({
|
||||||
mutationFn: async ({ sourceName, metadata }: { sourceName: string; metadata: any }) => {
|
mutationFn: async ({ sourceName, metadata }: { sourceName: string; metadata: any }) => {
|
||||||
const comicBookMetadata = {
|
const comicBookMetadata = {
|
||||||
@@ -146,7 +139,7 @@ export const PullList = (): ReactElement => {
|
|||||||
{isSuccess && !isLoading && (
|
{isSuccess && !isLoading && (
|
||||||
<div className="overflow-hidden" ref={emblaRef}>
|
<div className="overflow-hidden" ref={emblaRef}>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
{map(pullList?.data.result, (issue, idx) => {
|
{map(pullList?.data.result, (issue: LocgMetadata, idx: number) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={idx}
|
key={idx}
|
||||||
@@ -154,13 +147,13 @@ export const PullList = (): ReactElement => {
|
|||||||
>
|
>
|
||||||
<Card
|
<Card
|
||||||
orientation={"vertical-2"}
|
orientation={"vertical-2"}
|
||||||
imageUrl={issue.coverImageUrl}
|
imageUrl={issue.cover || undefined}
|
||||||
hasDetails
|
hasDetails
|
||||||
title={ellipsize(issue.issueName, 25)}
|
title={ellipsize(issue.name || 'Unknown', 25)}
|
||||||
>
|
>
|
||||||
<div className="px-1">
|
<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">
|
<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>
|
</span>
|
||||||
<div className="flex flex-row justify-end">
|
<div className="flex flex-row justify-end">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -4,23 +4,19 @@ import { Link } from "react-router-dom";
|
|||||||
import ellipsize from "ellipsize";
|
import ellipsize from "ellipsize";
|
||||||
import { isEmpty, isNil, isUndefined, map } from "lodash";
|
import { isEmpty, isNil, isUndefined, map } from "lodash";
|
||||||
import { detectIssueTypes } from "../../shared/utils/tradepaperback.utils";
|
import { detectIssueTypes } from "../../shared/utils/tradepaperback.utils";
|
||||||
import {
|
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
||||||
determineCoverFile,
|
|
||||||
determineExternalMetadata,
|
|
||||||
} from "../../shared/utils/metadata.utils";
|
|
||||||
import { LIBRARY_SERVICE_HOST } from "../../constants/endpoints";
|
import { LIBRARY_SERVICE_HOST } from "../../constants/endpoints";
|
||||||
import Header from "../shared/Header";
|
import Header from "../shared/Header";
|
||||||
import useEmblaCarousel from "embla-carousel-react";
|
import useEmblaCarousel from "embla-carousel-react";
|
||||||
|
import { GetRecentComicsQuery } from "../../graphql/generated";
|
||||||
|
|
||||||
type RecentlyImportedProps = {
|
type RecentlyImportedProps = {
|
||||||
comics: any;
|
comics: GetRecentComicsQuery['comics']['comics'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RecentlyImported = (
|
export const RecentlyImported = (
|
||||||
comics: RecentlyImportedProps,
|
{ comics }: RecentlyImportedProps,
|
||||||
): ReactElement => {
|
): ReactElement => {
|
||||||
console.log(comics);
|
|
||||||
|
|
||||||
// embla carousel
|
// embla carousel
|
||||||
const [emblaRef, emblaApi] = useEmblaCarousel({
|
const [emblaRef, emblaApi] = useEmblaCarousel({
|
||||||
loop: false,
|
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="-mr-10 sm:-mr-17 lg:-mr-29 xl:-mr-36 2xl:-mr-42 mt-3">
|
||||||
<div className="overflow-hidden" ref={emblaRef}>
|
<div className="overflow-hidden" ref={emblaRef}>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
{comics?.comics.map(
|
{comics?.map((comic, idx) => {
|
||||||
(
|
const {
|
||||||
{
|
id,
|
||||||
_id,
|
|
||||||
rawFileDetails,
|
rawFileDetails,
|
||||||
sourcedMetadata: { comicvine, comicInfo, locg },
|
sourcedMetadata,
|
||||||
|
canonicalMetadata,
|
||||||
inferredMetadata,
|
inferredMetadata,
|
||||||
wanted: { source } = {},
|
} = comic;
|
||||||
},
|
|
||||||
idx,
|
// 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({
|
const { issueName, url } = determineCoverFile({
|
||||||
rawFileDetails,
|
rawFileDetails,
|
||||||
comicvine,
|
comicvine,
|
||||||
comicInfo,
|
comicInfo,
|
||||||
locg,
|
locg,
|
||||||
});
|
});
|
||||||
const { issue, coverURL, icon } = determineExternalMetadata(
|
|
||||||
source,
|
|
||||||
{
|
|
||||||
comicvine,
|
|
||||||
comicInfo,
|
|
||||||
locg,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const isComicVineMetadataAvailable =
|
const isComicVineMetadataAvailable =
|
||||||
!isUndefined(comicvine) &&
|
!isUndefined(comicvine) &&
|
||||||
!isUndefined(comicvine.volumeInformation);
|
!isUndefined(comicvine.volumeInformation);
|
||||||
@@ -77,7 +72,7 @@ export const RecentlyImported = (
|
|||||||
<Card
|
<Card
|
||||||
orientation="vertical-2"
|
orientation="vertical-2"
|
||||||
imageUrl={url}
|
imageUrl={url}
|
||||||
title={inferredMetadata.issue.name}
|
title={inferredMetadata?.issue?.name}
|
||||||
hasDetails
|
hasDetails
|
||||||
cardState={cardState}
|
cardState={cardState}
|
||||||
>
|
>
|
||||||
@@ -89,7 +84,7 @@ export const RecentlyImported = (
|
|||||||
<i className="icon-[solar--hashtag-outline]"></i>
|
<i className="icon-[solar--hashtag-outline]"></i>
|
||||||
</span>
|
</span>
|
||||||
<span className="text-md text-slate-900">
|
<span className="text-md text-slate-900">
|
||||||
{inferredMetadata.issue.number}
|
{inferredMetadata?.issue?.number}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
{/* File extension */}
|
{/* File extension */}
|
||||||
@@ -99,7 +94,7 @@ export const RecentlyImported = (
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||||
{rawFileDetails.extension}
|
{rawFileDetails?.extension}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
{/* Uncompressed status */}
|
{/* Uncompressed status */}
|
||||||
@@ -142,8 +137,7 @@ export const RecentlyImported = (
|
|||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
})}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,12 +5,17 @@ import { Link, useNavigate } from "react-router-dom";
|
|||||||
import Card from "../shared/Carda";
|
import Card from "../shared/Carda";
|
||||||
import Header from "../shared/Header";
|
import Header from "../shared/Header";
|
||||||
import useEmblaCarousel from "embla-carousel-react";
|
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
|
// Till mongo gives us back the deduplicated results with the ObjectId
|
||||||
const deduplicatedGroups = unionBy(props.volumeGroups, "volumes.id");
|
const deduplicatedGroups = unionBy(props.volumeGroups, "volumes.id");
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const navigateToVolumes = (row) => {
|
const navigateToVolumes = (row: any) => {
|
||||||
navigate(`/volumes/all`);
|
navigate(`/volumes/all`);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,18 +41,18 @@ export const VolumeGroups = (props): ReactElement => {
|
|||||||
{map(deduplicatedGroups, (data) => {
|
{map(deduplicatedGroups, (data) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<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]"
|
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
|
<Card
|
||||||
orientation="vertical-2"
|
orientation="vertical-2"
|
||||||
imageUrl={data.volumes.image.small_url}
|
imageUrl={data.volumes?.image?.small_url || undefined}
|
||||||
hasDetails
|
hasDetails
|
||||||
>
|
>
|
||||||
<div className="py-3">
|
<div className="py-3">
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
<Link to={`/volume/details/${data._id}`}>
|
<Link to={`/volume/details/${data.id}`}>
|
||||||
{ellipsize(data.volumes.name, 48)}
|
{ellipsize(data.volumes?.name || 'Unknown', 48)}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{/* issue count */}
|
{/* issue count */}
|
||||||
@@ -57,7 +62,7 @@ export const VolumeGroups = (props): ReactElement => {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import { detectIssueTypes } from "../../shared/utils/tradepaperback.utils";
|
|||||||
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
||||||
import Header from "../shared/Header";
|
import Header from "../shared/Header";
|
||||||
import useEmblaCarousel from "embla-carousel-react";
|
import useEmblaCarousel from "embla-carousel-react";
|
||||||
|
import { GetWantedComicsQuery } from "../../graphql/generated";
|
||||||
|
|
||||||
type WantedComicsListProps = {
|
type WantedComicsListProps = {
|
||||||
comics: any;
|
comics?: GetWantedComicsQuery['getComicBooks']['docs'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WantedComicsList = ({
|
export const WantedComicsList = ({
|
||||||
@@ -38,12 +39,22 @@ export const WantedComicsList = ({
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
{map(
|
{map(
|
||||||
comics,
|
comics,
|
||||||
({
|
(comic) => {
|
||||||
_id,
|
const {
|
||||||
rawFileDetails,
|
id,
|
||||||
sourcedMetadata: { comicvine, comicInfo, locg },
|
rawFileDetails,
|
||||||
wanted,
|
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 isComicBookMetadataAvailable = !isUndefined(comicvine);
|
||||||
const consolidatedComicMetadata = {
|
const consolidatedComicMetadata = {
|
||||||
rawFileDetails,
|
rawFileDetails,
|
||||||
@@ -58,14 +69,14 @@ export const WantedComicsList = ({
|
|||||||
publisher = null,
|
publisher = null,
|
||||||
} = determineCoverFile(consolidatedComicMetadata);
|
} = determineCoverFile(consolidatedComicMetadata);
|
||||||
const titleElement = (
|
const titleElement = (
|
||||||
<Link to={"/comic/details/" + _id}>
|
<Link to={"/comic/details/" + id}>
|
||||||
{ellipsize(issueName, 20)}
|
{ellipsize(issueName, 20)}
|
||||||
<p>{publisher}</p>
|
<p>{publisher}</p>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div
|
<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]"
|
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
|
<Card
|
||||||
@@ -79,7 +90,7 @@ export const WantedComicsList = ({
|
|||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
{/* Issue type */}
|
{/* Issue type */}
|
||||||
{isComicBookMetadataAvailable &&
|
{isComicBookMetadataAvailable &&
|
||||||
!isNil(detectIssueTypes(comicvine.description)) ? (
|
!isNil(detectIssueTypes(comicvine?.description)) ? (
|
||||||
<div className="my-2">
|
<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="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">
|
<span className="pr-1 pt-1">
|
||||||
@@ -88,29 +99,14 @@ export const WantedComicsList = ({
|
|||||||
|
|
||||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||||
{
|
{
|
||||||
detectIssueTypes(comicvine.description)
|
detectIssueTypes(comicvine?.description)
|
||||||
.displayName
|
?.displayName
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{/* issues marked as wanted, part of this volume */}
|
{/* Wanted comics - info not available in current GraphQL query */}
|
||||||
{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>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{/* comicVine metadata presence */}
|
{/* comicVine metadata presence */}
|
||||||
{isComicBookMetadataAvailable && (
|
{isComicBookMetadataAvailable && (
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ export const Downloads = (props: IDownloadsProps): ReactElement => {
|
|||||||
<div className="columns">
|
<div className="columns">
|
||||||
<div className="column is-half">
|
<div className="column is-half">
|
||||||
{bundles.map((bundle, idx) => {
|
{bundles.map((bundle, idx) => {
|
||||||
console.log(bundle);
|
|
||||||
return (
|
return (
|
||||||
<div key={idx}>
|
<div key={idx}>
|
||||||
<MetadataPanel
|
<MetadataPanel
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ export const Search = ({}: ISearchProps): ReactElement => {
|
|||||||
coverDate: cover_date,
|
coverDate: cover_date,
|
||||||
issueNumber: issue_number,
|
issueNumber: issue_number,
|
||||||
});
|
});
|
||||||
console.log(issues);
|
|
||||||
// Get volume metadata from CV
|
// Get volume metadata from CV
|
||||||
const response = await axios({
|
const response = await axios({
|
||||||
url: `${COMICVINE_SERVICE_URI}/getVolumes`,
|
url: `${COMICVINE_SERVICE_URI}/getVolumes`,
|
||||||
@@ -111,7 +110,6 @@ export const Search = ({}: ISearchProps): ReactElement => {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.log("Invalid resource type.");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Add to wanted list
|
// Add to wanted list
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ export const AirDCPPSettingsForm = () => {
|
|||||||
// Handle setting update and subsequent AirDC++ initialization
|
// Handle setting update and subsequent AirDC++ initialization
|
||||||
const { mutate } = useMutation({
|
const { mutate } = useMutation({
|
||||||
mutationFn: (values) => {
|
mutationFn: (values) => {
|
||||||
console.log(values);
|
|
||||||
return axios.post("http://localhost:3000/api/settings/saveSettings", {
|
return axios.post("http://localhost:3000/api/settings/saveSettings", {
|
||||||
settingsPayload: values,
|
settingsPayload: values,
|
||||||
settingsKey: "directConnect",
|
settingsKey: "directConnect",
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export const DockerVars = (): React.ReactElement => {
|
|||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
refetchOnReconnect: false,
|
refetchOnReconnect: false,
|
||||||
});
|
});
|
||||||
console.log("Docker Vars: ", environmentVariables);
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<h2 className="text-xl font-semibold">Docker Environment Variables</h2>
|
<h2 className="text-xl font-semibold">Docker Environment Variables</h2>
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export const ProwlarrSettingsForm = (props) => {
|
|||||||
},
|
},
|
||||||
queryKey: ["prowlarrConnectionResult"],
|
queryKey: ["prowlarrConnectionResult"],
|
||||||
});
|
});
|
||||||
console.log(data);
|
|
||||||
const submitHandler = () => {};
|
const submitHandler = () => {};
|
||||||
const initialData = {};
|
const initialData = {};
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ export const Volumes = (props): ReactElement => {
|
|||||||
}),
|
}),
|
||||||
queryKey: ["volumes"],
|
queryKey: ["volumes"],
|
||||||
});
|
});
|
||||||
console.log(volumes);
|
|
||||||
const columnData = useMemo(
|
const columnData = useMemo(
|
||||||
(): any => [
|
(): any => [
|
||||||
{
|
{
|
||||||
@@ -46,7 +45,6 @@ export const Volumes = (props): ReactElement => {
|
|||||||
const {
|
const {
|
||||||
_source: { sourcedMetadata },
|
_source: { sourcedMetadata },
|
||||||
} = comicObject;
|
} = comicObject;
|
||||||
console.log("jaggu", row.getValue());
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-row gap-3 mt-5">
|
<div className="flex flex-row gap-3 mt-5">
|
||||||
<Link to={`/volume/details/${comicObject._id}`}>
|
<Link to={`/volume/details/${comicObject._id}`}>
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ export const WantedComics = (props): ReactElement => {
|
|||||||
minWidth: 350,
|
minWidth: 350,
|
||||||
accessorFn: (data) => data,
|
accessorFn: (data) => data,
|
||||||
cell: (value) => {
|
cell: (value) => {
|
||||||
console.log("ASDASd", value);
|
|
||||||
const row = value.getValue()._source;
|
const row = value.getValue()._source;
|
||||||
return row && <MetadataPanel data={row} />;
|
return row && <MetadataPanel data={row} />;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Link } from "react-router-dom";
|
|||||||
|
|
||||||
type IHeaderProps = {
|
type IHeaderProps = {
|
||||||
headerContent: string;
|
headerContent: string;
|
||||||
subHeaderContent: ReactElement;
|
subHeaderContent: ReactElement | string;
|
||||||
iconClassNames: string;
|
iconClassNames: string;
|
||||||
link?: 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 VolumeDetails from "./components/VolumeDetail/VolumeDetail";
|
||||||
import WantedComics from "./components/WantedComics/WantedComics";
|
import WantedComics from "./components/WantedComics/WantedComics";
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
retry: false,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ const onSearchSent = async (item, instance, unsubscribe, searchInfo) => {
|
|||||||
|
|
||||||
if (results.length > 0) {
|
if (results.length > 0) {
|
||||||
// We have results, download the best one
|
// We have results, download the best one
|
||||||
console.log("SASAAAA", results);
|
|
||||||
// const result = results[0];
|
// const result = results[0];
|
||||||
// SocketService.post(`search/${instance.id}/results/${result.id}/download`, {
|
// SocketService.post(`search/${instance.id}/results/${result.id}/download`, {
|
||||||
// priority: Utils.toApiPriority(item.priority),
|
// priority: Utils.toApiPriority(item.priority),
|
||||||
|
|||||||
@@ -42,6 +42,6 @@ const getIssueTypeDisplayName = (
|
|||||||
return { displayName, results };
|
return { displayName, results };
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} 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", () => {
|
socket.on("connect", () => {
|
||||||
console.log(`✅ Connected to ${namespace}:`, socket.id);
|
// Socket connected successfully
|
||||||
});
|
});
|
||||||
|
|
||||||
// Always listen for sessionInitialized in case backend creates a new session
|
// Always listen for sessionInitialized in case backend creates a new session
|
||||||
socket.on("sessionInitialized", (id) => {
|
socket.on("sessionInitialized", (id) => {
|
||||||
console.log("Session initialized with ID:", id);
|
|
||||||
localStorage.setItem("sessionId", 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 "*.jpg";
|
||||||
declare module "*.gif";
|
declare module "*.gif";
|
||||||
declare module "*.less";
|
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
|
// Getting data from body of requests
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
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
|
// REGISTER ROUTES
|
||||||
// all of the routes will be prefixed with /api
|
// all of the routes will be prefixed with /api
|
||||||
const routes: Router[] = Object.values(router);
|
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