diff --git a/screenshots/CVMatching.png b/screenshots/CVMatching.png new file mode 100644 index 0000000..3b25cb4 Binary files /dev/null and b/screenshots/CVMatching.png differ diff --git a/screenshots/ComicDetail.png b/screenshots/ComicDetail.png new file mode 100644 index 0000000..87731ee Binary files /dev/null and b/screenshots/ComicDetail.png differ diff --git a/screenshots/DC++Searching.png b/screenshots/DC++Searching.png new file mode 100644 index 0000000..1dba61f Binary files /dev/null and b/screenshots/DC++Searching.png differ diff --git a/screenshots/Dashboard.png b/screenshots/Dashboard.png new file mode 100644 index 0000000..23bbbc9 Binary files /dev/null and b/screenshots/Dashboard.png differ diff --git a/screenshots/Import.png b/screenshots/Import.png new file mode 100644 index 0000000..a9c90a5 Binary files /dev/null and b/screenshots/Import.png differ diff --git a/screenshots/Library.png b/screenshots/Library.png new file mode 100644 index 0000000..76902e0 Binary files /dev/null and b/screenshots/Library.png differ diff --git a/src/client/components/ComicDetail/AcquisitionPanel.tsx b/src/client/components/ComicDetail/AcquisitionPanel.tsx index 86baf2b..d8fd82c 100644 --- a/src/client/components/ComicDetail/AcquisitionPanel.tsx +++ b/src/client/components/ComicDetail/AcquisitionPanel.tsx @@ -278,9 +278,9 @@ export const AcquisitionPanel = ( )} /> ) : ( -
-
-
+
+
+
AirDC++ is not configured. Please configure it in{" "} Settings > AirDC++ > Connection.
diff --git a/src/client/components/ComicDetail/ComicDetail.tsx b/src/client/components/ComicDetail/ComicDetail.tsx index afa107b..4c52279 100644 --- a/src/client/components/ComicDetail/ComicDetail.tsx +++ b/src/client/components/ComicDetail/ComicDetail.tsx @@ -12,6 +12,7 @@ import { Menu } from "./ActionMenu/Menu"; import { ArchiveOperations } from "./Tabs/ArchiveOperations"; import { ComicInfoXML } from "./Tabs/ComicInfoXML"; import AcquisitionPanel from "./AcquisitionPanel"; +import TorrentSearchPanel from "./TorrentSearchPanel"; import DownloadsPanel from "./DownloadsPanel"; import { VolumeInformation } from "./Tabs/VolumeInformation"; @@ -350,7 +351,7 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => { ), name: "Torrent Search", - content: <>Torrents, + content: , shouldShow: true, }, { diff --git a/src/client/components/ComicDetail/ComicVineDetails.tsx b/src/client/components/ComicDetail/ComicVineDetails.tsx index f7fbf56..8ef6cd1 100644 --- a/src/client/components/ComicDetail/ComicVineDetails.tsx +++ b/src/client/components/ComicDetail/ComicVineDetails.tsx @@ -16,7 +16,7 @@ export const ComicVineDetails = (props): ReactElement => {
diff --git a/src/client/components/ComicDetail/TorrentSearchPanel.tsx b/src/client/components/ComicDetail/TorrentSearchPanel.tsx new file mode 100644 index 0000000..983b561 --- /dev/null +++ b/src/client/components/ComicDetail/TorrentSearchPanel.tsx @@ -0,0 +1,77 @@ +import React, { useCallback, ReactElement, useEffect, useState } from "react"; + +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import axios from "axios"; +import { Form, Field } from "react-final-form"; +import { PROWLARR_SERVICE_BASE_URI } from "../../constants/endpoints"; + +export const TorrentSearchPanel = (props): ReactElement => { + const [prowlarrSettingsData, setProwlarrSettingsData] = useState({}); + + const { data } = useQuery({ + queryFn: async () => + axios({ + url: `${PROWLARR_SERVICE_BASE_URI}/search`, + method: "POST", + data: { + port: "9696", + apiKey: "c4f42e265fb044dc81f7e88bd41c3367", + offset: 0, + categories: [7030], + query: "the darkness", + host: "localhost", + limit: 100, + type: "search", + indexerIds: [2], + }, + }), + queryKey: ["prowlarrSettingsData"], + }); + console.log(data?.data); + return ( + <> +
+
{}} + initialValues={{}} + render={({ handleSubmit, form, submitting, pristine, values }) => ( + + + {({ input, meta }) => { + return ( +
+
+
+ +
+ + + +
+
+ ); + }} +
+ + )} + /> +
+ + ); +}; + +export default TorrentSearchPanel; diff --git a/src/client/components/Dashboard/Dashboard.tsx b/src/client/components/Dashboard/Dashboard.tsx index e088307..04098a8 100644 --- a/src/client/components/Dashboard/Dashboard.tsx +++ b/src/client/components/Dashboard/Dashboard.tsx @@ -5,7 +5,6 @@ import { WantedComicsList } from "./WantedComicsList"; import { VolumeGroups } from "./VolumeGroups"; import { LibraryStatistics } from "./LibraryStatistics"; import { PullList } from "./PullList"; -import { getLibraryStatistics } from "../../actions/comicinfo.actions"; import { useQuery } from "@tanstack/react-query"; import axios from "axios"; import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints"; @@ -54,16 +53,23 @@ export const Dashboard = (): ReactElement => { queryKey: ["volumeGroups"], }); - // - // const libraryStatistics = useSelector( - // (state: RootState) => state.comicInfo.libraryStatistics, - // ); + const { data: statistics } = useQuery({ + queryFn: async () => + await axios({ + url: `${LIBRARY_SERVICE_BASE_URI}/libraryStatistics`, + method: "GET", + }), + queryKey: ["libraryStatistics"], + }); + return (
{recentComics && } {/* Wanted comics */} + {/* Library Statistics */} + {statistics && } {/* Volume groups */}
diff --git a/src/client/components/Dashboard/LibraryStatistics.tsx b/src/client/components/Dashboard/LibraryStatistics.tsx index 5d5eca8..76fe2d6 100644 --- a/src/client/components/Dashboard/LibraryStatistics.tsx +++ b/src/client/components/Dashboard/LibraryStatistics.tsx @@ -1,113 +1,99 @@ import React, { ReactElement, useEffect } from "react"; import prettyBytes from "pretty-bytes"; import { isEmpty, isUndefined, map } from "lodash"; +import Header from "../shared/Header"; export const LibraryStatistics = ( props: ILibraryStatisticsProps, ): ReactElement => { - // const { stats } = props; + const { stats } = props; return (
-

- Your Library In Numbers -

-

A brief snapshot of your library.

-
-
-
-
- - {props.stats.totalDocuments} - {" "} - files +
A brief snapshot of your library. + } + iconClassNames="fa-solid fa-binoculars mr-2" + /> + +
+
+
+
Library size
+
+ {props.stats.totalDocuments} files
-
- Library size - - {" "} +
+ {props.stats.comicDirectorySize && prettyBytes(props.stats.comicDirectorySize)}
+
+ {/* comicinfo and comicvine tagged issues */} +
{!isUndefined(props.stats.statistics) && !isEmpty(props.stats.statistics[0].issues) && ( -
- +
+ {props.stats.statistics[0].issues.length} {" "} tagged with ComicVine -
+
)} {!isUndefined(props.stats.statistics) && !isEmpty(props.stats.statistics[0].issuesWithComicInfoXML) && ( -
- +
+ {props.stats.statistics[0].issuesWithComicInfoXML.length} {" "} - with - ComicInfo.xml + with ComicInfo.xml -
+
)} -
-
+
-
-
-
- Issues -
-
- 304 Volumes -
-
- {!isUndefined(props.stats.statistics) && - !isEmpty(props.stats.statistics[0].fileTypes) && - map(props.stats.statistics[0].fileTypes, (fileType, idx) => { - return ( - - - {fileType.data.length} - - - {fileType._id} - - - ); - })} -
-
-
+
+ {!isUndefined(props.stats.statistics) && + !isEmpty(props.stats.statistics[0].fileTypes) && + map(props.stats.statistics[0].fileTypes, (fileType, idx) => { + return ( + + {fileType.data.length} {fileType._id} + + ); + })} +
- {/* file types */} -
- {/* publisher with most issues */} -
+ {/* file types */} +
+ {/* publisher with most issues */} {!isUndefined(props.stats.statistics) && !isEmpty( props.stats.statistics[0].publisherWithMostComicsInLibrary[0], ) && ( -
- + <> + { props.stats.statistics[0] .publisherWithMostComicsInLibrary[0]._id } {" has the most issues "} - + { props.stats.statistics[0] .publisherWithMostComicsInLibrary[0].count } -
+ )} -
- 304 Volumes -
-
+
diff --git a/src/client/components/Dashboard/PullList.tsx b/src/client/components/Dashboard/PullList.tsx index 346bac2..8ea16f7 100644 --- a/src/client/components/Dashboard/PullList.tsx +++ b/src/client/components/Dashboard/PullList.tsx @@ -82,7 +82,17 @@ export const PullList = (): ReactElement => {
+ Pull List aggregated for the week from{" "} + + + League Of Comic Geeks + + + + + } iconClassNames="fa-solid fa-binoculars mr-2" link="/pull-list/all/" /> @@ -101,7 +111,10 @@ export const PullList = (): ReactElement => { /> {inputValue && (
- Showing pull list for {inputValue} + Showing pull list for{" "} + + {inputValue} +
)}
diff --git a/src/client/components/Settings/ProwlarrSettings/ProwlarrSettingsForm.tsx b/src/client/components/Settings/ProwlarrSettings/ProwlarrSettingsForm.tsx new file mode 100644 index 0000000..d9f7340 --- /dev/null +++ b/src/client/components/Settings/ProwlarrSettings/ProwlarrSettingsForm.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import { useQuery } from "@tanstack/react-query"; +import { Form, Field } from "react-final-form"; +import { PROWLARR_SERVICE_BASE_URI } from "../../../constants/endpoints"; +import axios from "axios"; + +export const ProwlarrSettingsForm = (props) => { + const { data } = useQuery({ + queryFn: async (): any => { + return await axios({ + url: `${PROWLARR_SERVICE_BASE_URI}/getIndexers`, + method: "POST", + data: { + host: "localhost", + port: "9696", + apiKey: "c4f42e265fb044dc81f7e88bd41c3367", + }, + }); + }, + queryKey: ["prowlarrConnectionResult"], + }); + console.log(data); + const submitHandler = () => {}; + const initialData = {}; + return ( + <> + Prowlarr Settings. +
( + +
+
+

Configure Prowlarr integration here.

+

+ Note that you need a Prowlarr instance hosted and running to + configure the integration. +

+

+ See{" "} + + here + {" "} + for Prowlarr installation instructions for various platforms. +

+
+
+
+ )} + /> + + ); +}; + +export default ProwlarrSettingsForm; diff --git a/src/client/components/Settings/Settings.tsx b/src/client/components/Settings/Settings.tsx index eeb590b..d47966f 100644 --- a/src/client/components/Settings/Settings.tsx +++ b/src/client/components/Settings/Settings.tsx @@ -3,6 +3,7 @@ import { AirDCPPSettingsForm } from "./AirDCPPSettings/AirDCPPSettingsForm"; import { AirDCPPHubsForm } from "./AirDCPPSettings/AirDCPPHubsForm"; import { QbittorrentConnectionForm } from "./QbittorrentSettings/QbittorrentConnectionForm"; import { SystemSettingsForm } from "./SystemSettings/SystemSettingsForm"; +import ProwlarrSettingsForm from "./ProwlarrSettings/ProwlarrSettingsForm"; import { ServiceStatuses } from "../ServiceStatuses/ServiceStatuses"; import settingsObject from "../../constants/settings/settingsMenu.json"; import { isUndefined, map } from "lodash"; @@ -37,6 +38,14 @@ export const Settings = (props: ISettingsProps): ReactElement => {
), }, + { + id: "prwlr-connection", + content: ( + <> + + + ), + }, { id: "core-service", content: <>a, diff --git a/src/client/components/shared/Header.tsx b/src/client/components/shared/Header.tsx index 95bf3c6..4b00744 100644 --- a/src/client/components/shared/Header.tsx +++ b/src/client/components/shared/Header.tsx @@ -3,7 +3,7 @@ import { Link } from "react-router-dom"; type IHeaderProps = { headerContent: string; - subHeaderContent: string; + subHeaderContent: ReactElement; iconClassNames: string; link?: string; }; diff --git a/src/client/constants/endpoints.ts b/src/client/constants/endpoints.ts index 65ad90d..30600fa 100644 --- a/src/client/constants/endpoints.ts +++ b/src/client/constants/endpoints.ts @@ -90,3 +90,10 @@ export const QBITTORRENT_SERVICE_BASE_URI = hostURIBuilder({ port: "3060", apiPath: `/api/qbittorrent`, }); + +export const PROWLARR_SERVICE_BASE_URI = hostURIBuilder({ + protocol: "http", + host: import.meta.env.UNDERLYING_HOSTNAME || "localhost", + port: "3060", + apiPath: `/api/prowlarr`, +}); diff --git a/src/client/constants/settings/settingsMenu.json b/src/client/constants/settings/settingsMenu.json index 0233226..5b21641 100644 --- a/src/client/constants/settings/settingsMenu.json +++ b/src/client/constants/settings/settingsMenu.json @@ -57,7 +57,7 @@ "displayName": "Prowlarr", "children": [ { - "id": "prowlarr-connection", + "id": "prwlr-connection", "displayName": "Connection" }, { diff --git a/src/client/index.tsx b/src/client/index.tsx index 688374e..7c4c686 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -8,7 +8,6 @@ import { ErrorPage } from "./components/shared/ErrorPage"; const rootEl = document.getElementById("root"); const root = createRoot(rootEl); import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import Import from "./components/Import/Import"; import Dashboard from "./components/Dashboard/Dashboard"; import Search from "./components/Search/Search"; @@ -47,6 +46,5 @@ const router = createBrowserRouter([ root.render( - , );