From 0c363dd8aea0fb4f4e4f3a4205c9cf62b11bf603 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Wed, 15 Apr 2026 12:22:20 -0400 Subject: [PATCH] Consolidating types across the project --- src/client/actions/airdcpp.actions.tsx | 14 +- .../ComicDetail/AcquisitionPanel.tsx | 10 +- .../components/ComicDetail/ComicDetail.tsx | 35 +-- .../ComicDetail/ComicVineDetails.tsx | 11 +- .../ComicDetail/ComicVineMatchPanel.tsx | 10 +- .../ComicDetail/DownloadProgressTick.tsx | 26 +- .../components/ComicDetail/MatchResult.tsx | 8 +- .../components/ComicDetail/tabConfig.tsx | 21 +- .../Dashboard/LibraryStatistics.tsx | 11 +- src/client/components/Dashboard/PullList.tsx | 5 +- src/client/components/Dashboard/ZeroState.tsx | 5 +- src/client/components/Downloads/Downloads.tsx | 7 +- .../components/GlobalSearchBar/SearchBar.tsx | 7 +- src/client/components/Library/Library.tsx | 13 +- src/client/components/Library/LibraryGrid.tsx | 4 +- .../Library/TabulatedContentContainer.tsx | 7 +- src/client/components/Search/Search.tsx | 5 +- src/client/components/Settings/Settings.tsx | 5 +- src/client/components/shared/AlertCard.tsx | 20 +- src/client/components/shared/ProgressBar.tsx | 20 +- src/client/components/shared/StatsCard.tsx | 20 +- src/client/components/shared/T2Table.tsx | 22 +- src/client/types/comic.types.ts | 224 ++++++++++++++++++ src/client/types/dashboard.types.ts | 78 ++++++ src/client/types/index.ts | 74 ++++++ src/client/types/search.types.ts | 94 ++++++++ src/client/types/shared.types.ts | 156 ++++++++++++ 27 files changed, 659 insertions(+), 253 deletions(-) create mode 100644 src/client/types/comic.types.ts create mode 100644 src/client/types/dashboard.types.ts create mode 100644 src/client/types/index.ts create mode 100644 src/client/types/search.types.ts create mode 100644 src/client/types/shared.types.ts diff --git a/src/client/actions/airdcpp.actions.tsx b/src/client/actions/airdcpp.actions.tsx index ba50303..fe2df19 100644 --- a/src/client/actions/airdcpp.actions.tsx +++ b/src/client/actions/airdcpp.actions.tsx @@ -34,19 +34,7 @@ import { import { isNil } from "lodash"; import axios from "axios"; -/** - * Configuration data for an AirDC++ search operation. - * @interface SearchData - * @property {Object} query - Search query parameters - * @property {string} query.pattern - Search pattern/term (required) - * @property {string[]|undefined|null} hub_urls - List of hub URLs to search - * @property {PriorityEnum} priority - Download priority level - */ -interface SearchData { - query: Pick & Partial>; - hub_urls: string[] | undefined | null; - priority: PriorityEnum; -} +import type { AirDCPPSearchData } from "../types"; /** * Creates a promise that resolves after a specified delay. diff --git a/src/client/components/ComicDetail/AcquisitionPanel.tsx b/src/client/components/ComicDetail/AcquisitionPanel.tsx index 4f5de62..2bb1bb4 100644 --- a/src/client/components/ComicDetail/AcquisitionPanel.tsx +++ b/src/client/components/ComicDetail/AcquisitionPanel.tsx @@ -17,16 +17,10 @@ import { useQuery, useQueryClient } from "@tanstack/react-query"; import axios from "axios"; import { AIRDCPP_SERVICE_BASE_URI } from "../../constants/endpoints"; import type { Socket } from "socket.io-client"; - -interface IAcquisitionPanelProps { - query: any; - comicObjectId: any; - comicObject: any; - settings: any; -} +import type { AcquisitionPanelProps } from "../../types"; export const AcquisitionPanel = ( - props: IAcquisitionPanelProps, + props: AcquisitionPanelProps, ): ReactElement => { const socketRef = useRef(); const queryClient = useQueryClient(); diff --git a/src/client/components/ComicDetail/ComicDetail.tsx b/src/client/components/ComicDetail/ComicDetail.tsx index 940bddb..94a550b 100644 --- a/src/client/components/ComicDetail/ComicDetail.tsx +++ b/src/client/components/ComicDetail/ComicDetail.tsx @@ -10,7 +10,7 @@ import "react-sliding-pane/dist/react-sliding-pane.css"; import SlidingPane from "react-sliding-pane"; import { determineCoverFile } from "../../shared/utils/metadata.utils"; import { styled } from "styled-components"; -import type { RawFileDetails as RawFileDetailsType, InferredMetadata } from "../../graphql/generated"; +import type { ComicDetailProps } from "../../types"; // Extracted modules import { useComicVineMatching } from "./useComicVineMatching"; @@ -23,39 +23,6 @@ const StyledSlidingPanel = styled(SlidingPane)` background: #ccc; `; -interface ComicVineMetadata { - name?: string; - volumeInformation?: Record; - [key: string]: unknown; -} - -interface Acquisition { - directconnect?: { - downloads?: unknown[]; - }; - torrent?: unknown[]; - [key: string]: unknown; -} - -interface ComicDetailProps { - data: { - _id: string; - rawFileDetails?: RawFileDetailsType; - inferredMetadata: InferredMetadata; - sourcedMetadata: { - comicvine?: ComicVineMetadata; - locg?: Record; - comicInfo?: Record; - }; - acquisition?: Acquisition; - createdAt: string; - updatedAt: string; - }; - userSettings?: Record; - queryClient?: unknown; - comicObjectId?: string; -} - /** * Displays full comic detail: cover, file info, action menu, and tabbed panels * for metadata, archive operations, and acquisition. diff --git a/src/client/components/ComicDetail/ComicVineDetails.tsx b/src/client/components/ComicDetail/ComicVineDetails.tsx index f4a48fa..467ad8b 100644 --- a/src/client/components/ComicDetail/ComicVineDetails.tsx +++ b/src/client/components/ComicDetail/ComicVineDetails.tsx @@ -4,16 +4,7 @@ import dayjs from "dayjs"; import { isEmpty, isUndefined } from "lodash"; import Card from "../shared/Carda"; import { convert } from "html-to-text"; - -interface ComicVineDetailsProps { - updatedAt?: string; - data?: { - name?: string; - number?: string; - resource_type?: string; - id?: number; - }; -} +import type { ComicVineDetailsProps } from "../../types"; export const ComicVineDetails = (props: ComicVineDetailsProps): ReactElement => { const { data, updatedAt } = props; diff --git a/src/client/components/ComicDetail/ComicVineMatchPanel.tsx b/src/client/components/ComicDetail/ComicVineMatchPanel.tsx index 474555b..9a6af71 100644 --- a/src/client/components/ComicDetail/ComicVineMatchPanel.tsx +++ b/src/client/components/ComicDetail/ComicVineMatchPanel.tsx @@ -3,15 +3,7 @@ import MatchResult from "./MatchResult"; import { isEmpty } from "lodash"; import { useStore } from "../../store"; import { useShallow } from "zustand/react/shallow"; - -interface ComicVineMatchPanelProps { - props: { - comicObjectId: string; - comicVineMatches: any[]; - queryClient?: any; - onMatchApplied?: () => void; - }; -} +import type { ComicVineMatchPanelProps } from "../../types"; /** Displays ComicVine search results or a status message while searching. */ export const ComicVineMatchPanel = ({ props: comicVineData }: ComicVineMatchPanelProps): ReactElement => { diff --git a/src/client/components/ComicDetail/DownloadProgressTick.tsx b/src/client/components/ComicDetail/DownloadProgressTick.tsx index 731a771..fa8b9c9 100644 --- a/src/client/components/ComicDetail/DownloadProgressTick.tsx +++ b/src/client/components/ComicDetail/DownloadProgressTick.tsx @@ -2,32 +2,12 @@ import prettyBytes from "pretty-bytes"; import React, { ReactElement, useEffect, useRef, useState } from "react"; import { useStore } from "../../store"; import type { Socket } from "socket.io-client"; - -/** - * @typedef {Object} DownloadProgressTickProps - * @property {string} bundleId - The bundle ID to filter ticks by (as string) - */ -interface DownloadProgressTickProps { - bundleId: string; -} +import type { DownloadProgressTickProps } from "../../types"; /** * Shape of the download tick data received over the socket. - * - * @typedef DownloadTickData - * @property {number} id - Unique download ID - * @property {string} name - File name (e.g. "movie.mkv") - * @property {number} downloaded_bytes - Bytes downloaded so far - * @property {number} size - Total size in bytes - * @property {number} speed - Current download speed (bytes/sec) - * @property {number} seconds_left - Estimated seconds remaining - * @property {{ id: string; str: string; completed: boolean; downloaded: boolean; failed: boolean; hook_error: any }} status - * - Status object (e.g. `{ id: "queued", str: "Running (15.1%)", ... }`) - * @property {{ online: number; total: number; str: string }} sources - * - Peer count (e.g. `{ online: 1, total: 1, str: "1/1 online" }`) - * @property {string} target - Download destination (e.g. "/Downloads/movie.mkv") */ -interface DownloadTickData { +type DownloadTickData = { id: number; name: string; downloaded_bytes: number; @@ -48,7 +28,7 @@ interface DownloadTickData { str: string; }; target: string; -} +}; export const DownloadProgressTick: React.FC = ({ bundleId, diff --git a/src/client/components/ComicDetail/MatchResult.tsx b/src/client/components/ComicDetail/MatchResult.tsx index ee0f0ab..0cc6c9b 100644 --- a/src/client/components/ComicDetail/MatchResult.tsx +++ b/src/client/components/ComicDetail/MatchResult.tsx @@ -5,13 +5,7 @@ import ellipsize from "ellipsize"; import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints"; import axios from "axios"; import { useGetComicByIdQuery } from "../../graphql/generated"; - -interface MatchResultProps { - matchData: any; - comicObjectId: string; - queryClient?: any; - onMatchApplied?: () => void; -} +import type { MatchResultProps } from "../../types"; const handleBrokenImage = (e) => { e.target.src = "http://localhost:3050/dist/img/noimage.svg"; diff --git a/src/client/components/ComicDetail/tabConfig.tsx b/src/client/components/ComicDetail/tabConfig.tsx index 64d0302..efea5ab 100644 --- a/src/client/components/ComicDetail/tabConfig.tsx +++ b/src/client/components/ComicDetail/tabConfig.tsx @@ -1,5 +1,6 @@ import React, { lazy } from "react"; import { isNil, isEmpty } from "lodash"; +import type { TabConfig, TabConfigParams } from "../../types"; const VolumeInformation = lazy(() => import("./Tabs/VolumeInformation").then(m => ({ default: m.VolumeInformation }))); const ArchiveOperations = lazy(() => import("./Tabs/ArchiveOperations").then(m => ({ default: m.ArchiveOperations }))); @@ -7,26 +8,6 @@ const AcquisitionPanel = lazy(() => import("./AcquisitionPanel")); const TorrentSearchPanel = lazy(() => import("./TorrentSearchPanel")); const DownloadsPanel = lazy(() => import("./DownloadsPanel")); -interface TabConfig { - id: number; - name: string; - icon: React.ReactElement; - content: React.ReactElement | null; - shouldShow: boolean; -} - -interface TabConfigParams { - data: any; - hasAnyMetadata: boolean; - areRawFileDetailsAvailable: boolean; - airDCPPQuery: any; - comicObjectId: string; - userSettings: any; - issueName: string; - acquisition?: any; - onReconcileMetadata?: () => void; -} - export const createTabConfig = ({ data, hasAnyMetadata, diff --git a/src/client/components/Dashboard/LibraryStatistics.tsx b/src/client/components/Dashboard/LibraryStatistics.tsx index 740cfa3..a99955b 100644 --- a/src/client/components/Dashboard/LibraryStatistics.tsx +++ b/src/client/components/Dashboard/LibraryStatistics.tsx @@ -1,16 +1,7 @@ import React, { ReactElement } from "react"; import Header from "../shared/Header"; import { GetLibraryStatisticsQuery, DirectorySize } from "../../graphql/generated"; - -type Stats = Omit & { - comicDirectorySize: DirectorySize; - comicsMissingFiles: number; -}; - -/** Props for {@link LibraryStatistics}. */ -interface LibraryStatisticsProps { - stats: Stats | null | undefined; -} +import type { LibraryStatisticsProps } from "../../types"; /** * Displays a snapshot of library metrics: total comic files, tagging coverage, diff --git a/src/client/components/Dashboard/PullList.tsx b/src/client/components/Dashboard/PullList.tsx index cc2d3c2..051b2a6 100644 --- a/src/client/components/Dashboard/PullList.tsx +++ b/src/client/components/Dashboard/PullList.tsx @@ -12,10 +12,7 @@ import { Form } from "react-final-form"; import DatePickerDialog from "../shared/DatePicker"; import { format } from "date-fns"; import { LocgMetadata, useGetWeeklyPullListQuery } from "../../graphql/generated"; - -interface PullListProps { - issues?: LocgMetadata[]; -} +import type { PullListProps } from "../../types"; export const PullList = (): ReactElement => { const queryClient = useQueryClient(); diff --git a/src/client/components/Dashboard/ZeroState.tsx b/src/client/components/Dashboard/ZeroState.tsx index e061846..f3c76c8 100644 --- a/src/client/components/Dashboard/ZeroState.tsx +++ b/src/client/components/Dashboard/ZeroState.tsx @@ -1,9 +1,6 @@ import * as React from "react"; +import type { ZeroStateProps } from "../../types"; -interface ZeroStateProps { - header: string; - message: string; -} const ZeroState: React.FunctionComponent = (props) => { return (
diff --git a/src/client/components/Downloads/Downloads.tsx b/src/client/components/Downloads/Downloads.tsx index ae2b00a..0fec1ac 100644 --- a/src/client/components/Downloads/Downloads.tsx +++ b/src/client/components/Downloads/Downloads.tsx @@ -3,12 +3,9 @@ import { getTransfers } from "../../actions/airdcpp.actions"; import { isEmpty, isNil, isUndefined } from "lodash"; import { determineCoverFile } from "../../shared/utils/metadata.utils"; import MetadataPanel from "../shared/MetadataPanel"; +import type { DownloadsProps } from "../../types"; -interface IDownloadsProps { - data: any; -} - -export const Downloads = (props: IDownloadsProps): ReactElement => { +export const Downloads = (props: DownloadsProps): ReactElement => { // const airDCPPConfiguration = useContext(AirDCPPSocketContext); const { airDCPPState: { settings, socket }, diff --git a/src/client/components/GlobalSearchBar/SearchBar.tsx b/src/client/components/GlobalSearchBar/SearchBar.tsx index d2f74d6..04f324e 100644 --- a/src/client/components/GlobalSearchBar/SearchBar.tsx +++ b/src/client/components/GlobalSearchBar/SearchBar.tsx @@ -5,12 +5,9 @@ import Card from "../shared/Carda"; import { searchIssue } from "../../actions/fileops.actions"; import MetadataPanel from "../shared/MetadataPanel"; +import type { GlobalSearchBarProps } from "../../types"; -interface ISearchBarProps { - data: any; -} - -export const SearchBar = (data: ISearchBarProps): ReactElement => { +export const SearchBar = (data: GlobalSearchBarProps): ReactElement => { const dispatch = useDispatch(); const searchResults = useSelector( (state: RootState) => state.fileOps.librarySearchResultsFormatted, diff --git a/src/client/components/Library/Library.tsx b/src/client/components/Library/Library.tsx index 9296159..972d589 100644 --- a/src/client/components/Library/Library.tsx +++ b/src/client/components/Library/Library.tsx @@ -14,14 +14,7 @@ import axios from "axios"; import { format, parseISO } from "date-fns"; import { useGetWantedComicsQuery } from "../../graphql/generated"; -type FilterOption = "all" | "missingFiles"; - -interface SearchQuery { - query: Record; - pagination: { size: number; from: number }; - type: string; - trigger: string; -} +import type { LibrarySearchQuery, FilterOption } from "../../types"; const FILTER_OPTIONS: { value: FilterOption; label: string }[] = [ { value: "all", label: "All Comics" }, @@ -37,7 +30,7 @@ export const Library = (): ReactElement => { const initialFilter = (searchParams.get("filter") as FilterOption) ?? "all"; const [activeFilter, setActiveFilter] = useState(initialFilter); - const [searchQuery, setSearchQuery] = useState({ + const [searchQuery, setSearchQuery] = useState({ query: {}, pagination: { size: 25, from: 0 }, type: "all", @@ -47,7 +40,7 @@ export const Library = (): ReactElement => { const queryClient = useQueryClient(); /** Fetches a page of issues from the search API. */ - const fetchIssues = async (q: SearchQuery) => { + const fetchIssues = async (q: LibrarySearchQuery) => { const { pagination, query, type } = q; return await axios({ method: "POST", diff --git a/src/client/components/Library/LibraryGrid.tsx b/src/client/components/Library/LibraryGrid.tsx index 64727df..dadcd39 100644 --- a/src/client/components/Library/LibraryGrid.tsx +++ b/src/client/components/Library/LibraryGrid.tsx @@ -16,9 +16,9 @@ import Card from "../shared/Carda"; import { detectIssueTypes } from "../../shared/utils/tradepaperback.utils"; import { Link } from "react-router-dom"; import { LIBRARY_SERVICE_HOST } from "../../constants/endpoints"; +import type { LibraryGridProps } from "../../types"; -interface ILibraryGridProps {} -export const LibraryGrid = (libraryGridProps: ILibraryGridProps) => { +export const LibraryGrid = (libraryGridProps: LibraryGridProps) => { const data = useSelector( (state: RootState) => state.fileOps.recentComics.docs, ); diff --git a/src/client/components/Library/TabulatedContentContainer.tsx b/src/client/components/Library/TabulatedContentContainer.tsx index 9c6ddab..52d3167 100644 --- a/src/client/components/Library/TabulatedContentContainer.tsx +++ b/src/client/components/Library/TabulatedContentContainer.tsx @@ -3,10 +3,7 @@ import PullList from "../PullList/PullList"; import { Volumes } from "../Volumes/Volumes"; import WantedComics from "../WantedComics/WantedComics"; import { Library } from "./Library"; - -interface ITabulatedContentContainerProps { - category: string; -} +import type { TabulatedContentContainerProps } from "../../types"; /** * Component to draw the contents of a category in a table. * @@ -18,7 +15,7 @@ interface ITabulatedContentContainerProps { */ const TabulatedContentContainer = ( - props: ITabulatedContentContainerProps, + props: TabulatedContentContainerProps, ): ReactElement => { const { category } = props; const renderTabulatedContent = () => { diff --git a/src/client/components/Search/Search.tsx b/src/client/components/Search/Search.tsx index 1e89c2e..4ac0f43 100644 --- a/src/client/components/Search/Search.tsx +++ b/src/client/components/Search/Search.tsx @@ -16,10 +16,9 @@ import { LIBRARY_SERVICE_BASE_URI, } from "../../constants/endpoints"; import axios from "axios"; +import type { SearchPageProps } from "../../types"; -interface ISearchProps {} - -export const Search = ({}: ISearchProps): ReactElement => { +export const Search = ({}: SearchPageProps): ReactElement => { const queryClient = useQueryClient(); const formData = { search: "", diff --git a/src/client/components/Settings/Settings.tsx b/src/client/components/Settings/Settings.tsx index d1f8d0e..b9f54b6 100644 --- a/src/client/components/Settings/Settings.tsx +++ b/src/client/components/Settings/Settings.tsx @@ -8,10 +8,9 @@ import DockerVars from "./DockerVars/DockerVars"; import { ServiceStatuses } from "../ServiceStatuses/ServiceStatuses"; import settingsObject from "../../constants/settings/settingsMenu.json"; import { isUndefined, map } from "lodash"; +import type { SettingsProps } from "../../types"; -interface ISettingsProps {} - -export const Settings = (props: ISettingsProps): ReactElement => { +export const Settings = (props: SettingsProps): ReactElement => { const [active, setActive] = useState("gen-db"); const [expanded, setExpanded] = useState>({}); diff --git a/src/client/components/shared/AlertCard.tsx b/src/client/components/shared/AlertCard.tsx index 1df71d5..6a18e22 100644 --- a/src/client/components/shared/AlertCard.tsx +++ b/src/client/components/shared/AlertCard.tsx @@ -6,25 +6,7 @@ */ import { ReactElement, ReactNode } from "react"; - -/** - * Visual style variants for the alert card. - * @typedef {"error"|"warning"|"info"|"success"} AlertVariant - */ -export type AlertVariant = "error" | "warning" | "info" | "success"; - -interface AlertCardProps { - /** The visual style variant of the alert */ - variant: AlertVariant; - /** Optional title displayed prominently */ - title?: string; - /** Main message content */ - children: ReactNode; - /** Optional callback when dismiss button is clicked */ - onDismiss?: () => void; - /** Additional CSS classes */ - className?: string; -} +import type { AlertVariant, AlertCardProps } from "../../types"; const variantStyles: Record { - /** Row data to render. */ - sourceData?: TData[]; - /** Total number of records across all pages, used for pagination display. */ - totalPages?: number; - /** Column definitions (TanStack Table {@link ColumnDef} array). */ - columns?: ColumnDef[]; - /** Callbacks for navigating between pages. */ - paginationHandlers?: { - nextPage?(pageIndex: number, pageSize: number): void; - previousPage?(pageIndex: number, pageSize: number): void; - }; - /** Called with the TanStack row object when a row is clicked. */ - rowClickHandler?(row: Row): void; - /** Returns additional CSS classes for a given row (e.g. for highlight states). */ - getRowClassName?(row: Row): string; - /** Optional slot rendered in the toolbar area (e.g. a search input). */ - children?: ReactNode; -} +import type { T2TableProps } from "../../types"; /** * A paginated data table with a two-row sticky header. diff --git a/src/client/types/comic.types.ts b/src/client/types/comic.types.ts new file mode 100644 index 0000000..092cdec --- /dev/null +++ b/src/client/types/comic.types.ts @@ -0,0 +1,224 @@ +/** + * @fileoverview Centralized type definitions for ComicDetail components. + * @module types/comic.types + */ + +import type { ReactElement } from "react"; +import type { RawFileDetails as RawFileDetailsType, InferredMetadata } from "../graphql/generated"; + +/** + * ComicVine metadata structure from sourced metadata. + */ +export type ComicVineMetadata = { + name?: string; + volumeInformation?: Record; + [key: string]: unknown; +}; + +/** + * Acquisition data for a comic (downloads, torrents, etc.). + */ +export type Acquisition = { + directconnect?: { + downloads?: unknown[]; + }; + torrent?: unknown[]; + [key: string]: unknown; +}; + +/** + * Props for the ComicDetail component. + */ +export type ComicDetailProps = { + data: { + _id: string; + rawFileDetails?: RawFileDetailsType; + inferredMetadata: InferredMetadata; + sourcedMetadata: { + comicvine?: ComicVineMetadata; + locg?: Record; + comicInfo?: Record; + }; + acquisition?: Acquisition; + createdAt: string; + updatedAt: string; + }; + userSettings?: Record; + queryClient?: unknown; + comicObjectId?: string; +}; + +/** + * Tab configuration for ComicDetail tabs. + */ +export type TabConfig = { + id: number; + name: string; + icon: ReactElement; + content: ReactElement | null; + shouldShow: boolean; +}; + +/** + * Parameters for creating tab configuration. + */ +export type TabConfigParams = { + data: any; + hasAnyMetadata: boolean; + areRawFileDetailsAvailable: boolean; + airDCPPQuery: any; + comicObjectId: string; + userSettings: any; + issueName: string; + acquisition?: any; + onReconcileMetadata?: () => void; +}; + +/** + * Props for ComicVineMatchPanel component. + */ +export type ComicVineMatchPanelProps = { + props: { + rawFileDetails?: RawFileDetailsType; + comicVineMatches: any; + comicObjectId: string; + inferredMetadata?: InferredMetadata; + queryClient?: unknown; + onMatchApplied?: () => void; + }; +}; + +/** + * Props for MatchResult component. + */ +export type MatchResultProps = { + matchData: any; + comicObjectId: string; + queryClient?: any; + onMatchApplied?: () => void; +}; + +/** + * Props for AcquisitionPanel component. + */ +export type AcquisitionPanelProps = { + query: any; + comicObjectId: any; + comicObject: any; + settings: any; +}; + +/** + * Props for ComicVineDetails component. + */ +export type ComicVineDetailsProps = { + updatedAt?: string; + data?: { + name?: string; + number?: string; + resource_type?: string; + id?: number; + volumeInformation?: any; + issue_number?: string; + description?: string; + }; +}; + +/** + * Props for CVMatchesPanel in SlidingPanelContent. + */ +export type CVMatchesPanelProps = { + rawFileDetails?: RawFileDetailsType; + comicVineMatches: any; + comicObjectId: string; + inferredMetadata?: InferredMetadata; + queryClient?: unknown; + onMatchApplied?: () => void; +}; + +/** + * Props for EditMetadataPanel component. + */ +export type EditMetadataPanelProps = { + data: { + rawFileDetails: any; + comicObjectId: string; + }; +}; + +/** + * Props for DownloadProgressTick component. + */ +export type DownloadProgressTickProps = { + bundleId: string; +}; + +/** + * Data structure for download tick. + */ +export type DownloadTickData = { + id: number; + name: string; + type: { + id: string; + str: string; + content_type: string; + }; + target: string; + speed: number; + seconds_left: number; + bytes_downloaded: number; + size: number; + status: { + id: string; + str: string; + }; + time_finished: number; + time_added: number; +}; + +/** + * Props for AsyncSelectPaginate component. + */ +export type AsyncSelectPaginateProps = { + metronResource: string; + metronResourceId?: string; + value?: any; + onChange?: (value: any) => void; +}; + +/** + * Sourced metadata structure for VolumeInformation. + */ +export type SourcedMetadata = { + comicvine?: ComicVineMetadata; + locg?: Record; + comicInfo?: Record; +}; + +/** + * Data structure for VolumeInformation component. + */ +export type VolumeInformationData = { + id?: string; + _id?: string; + sourcedMetadata?: SourcedMetadata; + rawFileDetails?: RawFileDetailsType; +}; + +/** + * Props for VolumeInformation component. + */ +export type VolumeInformationProps = { + data: VolumeInformationData; + onReconcile?: () => void; +}; + +/** + * Props for ScalarCell in ReconcilerDrawer. + */ +export type ScalarCellProps = { + value: string | null; + isSelected?: boolean; + onClick?: () => void; +}; diff --git a/src/client/types/dashboard.types.ts b/src/client/types/dashboard.types.ts new file mode 100644 index 0000000..b9a16d5 --- /dev/null +++ b/src/client/types/dashboard.types.ts @@ -0,0 +1,78 @@ +/** + * @fileoverview Centralized type definitions for Dashboard components. + * @module types/dashboard.types + */ + +import type { LocgMetadata } from "../graphql/generated"; + +/** + * Props for ZeroState component. + */ +export type ZeroStateProps = { + header: string; + message: string; +}; + +/** + * Props for PullList component. + */ +export type PullListProps = { + issues?: LocgMetadata[]; +}; + +/** + * Library statistics structure for the dashboard. + */ +export type LibraryStats = { + totalDocuments: number; + comicDirectorySize: { + totalSizeInGB?: number | null; + }; + comicsMissingFiles: number; + statistics?: Array<{ + issues?: unknown[]; + issuesWithComicInfoXML?: unknown[]; + fileTypes?: Array<{ + id: string; + data: unknown[]; + }>; + publisherWithMostComicsInLibrary?: Array<{ + id: string; + count: number; + }>; + }>; +}; + +/** + * Props for LibraryStatistics component. + */ +export type LibraryStatisticsProps = { + stats: LibraryStats | null | undefined; +}; + +/** + * Props for RecentlyImported component. + */ +export type RecentlyImportedProps = { + comics?: unknown[]; + isLoading?: boolean; +}; + +/** + * Props for VolumeGroups component. + */ +export type VolumeGroupsProps = { + groups?: Array<{ + id: string; + name: string; + count: number; + }>; +}; + +/** + * Props for WantedComicsList component. + */ +export type WantedComicsListProps = { + comics?: unknown[]; + isLoading?: boolean; +}; diff --git a/src/client/types/index.ts b/src/client/types/index.ts new file mode 100644 index 0000000..9124fc6 --- /dev/null +++ b/src/client/types/index.ts @@ -0,0 +1,74 @@ +/** + * @fileoverview Barrel export for all centralized type definitions. + * Import types from here for cleaner imports throughout the application. + * @module types + * + * @example + * ```ts + * import type { ComicDetailProps, AlertVariant, LibrarySearchQuery } from '../types'; + * ``` + */ + +// Comic Detail types +export type { + ComicVineMetadata, + Acquisition, + ComicDetailProps, + TabConfig, + TabConfigParams, + ComicVineMatchPanelProps, + MatchResultProps, + AcquisitionPanelProps, + ComicVineDetailsProps, + CVMatchesPanelProps, + EditMetadataPanelProps, + DownloadProgressTickProps, + DownloadTickData, + AsyncSelectPaginateProps, + SourcedMetadata, + VolumeInformationData, + VolumeInformationProps, + ScalarCellProps, +} from "./comic.types"; + +// Dashboard types +export type { + ZeroStateProps, + PullListProps, + LibraryStats, + LibraryStatisticsProps, + RecentlyImportedProps, + VolumeGroupsProps, + WantedComicsListProps, +} from "./dashboard.types"; + +// Search types +export type { + LibrarySearchQuery, + AirDCPPSearchData, + SearchBarProps, + SearchPageProps, + FilterOption, + FilterOptionConfig, + ComicVineResourceType, + ComicVineSearchResult, + GlobalSearchBarProps, +} from "./search.types"; + +// Shared component types +export type { + AlertVariant, + AlertCardProps, + CardProps, + ProgressBarProps, + StatsCardProps, + T2TableProps, + MetadataPanelProps, + HeaderProps, + DatePickerProps, + PopoverButtonProps, + TabulatedContentContainerProps, + DownloadsProps, + LibraryGridProps, + SettingsProps, +} from "./shared.types"; diff --git a/src/client/types/search.types.ts b/src/client/types/search.types.ts new file mode 100644 index 0000000..e1aeea1 --- /dev/null +++ b/src/client/types/search.types.ts @@ -0,0 +1,94 @@ +/** + * @fileoverview Centralized type definitions for search functionality. + * @module types/search.types + */ + +import type { + SearchQuery as AirDCPPSearchQuery, + PriorityEnum, +} from "threetwo-ui-typings"; + +/** + * Library search query structure. + */ +export type LibrarySearchQuery = { + query: Record; + pagination: { size: number; from: number }; + type: string; + trigger: string; +}; + +/** + * AirDC++ search data configuration. + */ +export type AirDCPPSearchData = { + query: Pick & Partial>; + hub_urls: string[] | undefined | null; + priority: PriorityEnum; +}; + +/** + * Props for SearchBar component. + */ +export type SearchBarProps = { + data?: any; + searchHandler?: (e: any) => void; +}; + +/** + * Props for Search page component. + */ +export type SearchPageProps = Record; + +/** + * Filter options for library. + */ +export type FilterOption = "all" | "missingFiles"; + +/** + * Filter option configuration. + */ +export type FilterOptionConfig = { + value: FilterOption; + label: string; +}; + +/** + * ComicVine search result resource types. + */ +export type ComicVineResourceType = "volume" | "issue"; + +/** + * ComicVine search result structure. + */ +export type ComicVineSearchResult = { + id: number; + name?: string; + deck?: string; + api_detail_url?: string; + image?: { + small_url?: string; + medium_url?: string; + original_url?: string; + }; + description?: string; + volume?: { + api_detail_url?: string; + name?: string; + }; + cover_date?: string; + start_year?: string; + count_of_issues?: number; + publisher?: { + name?: string; + }; + issue_number?: string; + resource_type?: ComicVineResourceType; +}; + +/** + * Props for GlobalSearchBar component. + */ +export type GlobalSearchBarProps = { + data?: any; +}; diff --git a/src/client/types/shared.types.ts b/src/client/types/shared.types.ts new file mode 100644 index 0000000..4948e50 --- /dev/null +++ b/src/client/types/shared.types.ts @@ -0,0 +1,156 @@ +/** + * @fileoverview Centralized type definitions for shared UI components. + * @module types/shared.types + */ + +import type { ReactNode, ReactElement } from "react"; +import type { ColumnDef, Row } from "@tanstack/react-table"; + +/** + * Visual style variants for alert components. + */ +export type AlertVariant = "error" | "warning" | "info" | "success"; + +/** + * Props for AlertCard component. + */ +export type AlertCardProps = { + /** The visual style variant of the alert */ + variant: AlertVariant; + /** Optional title displayed prominently */ + title?: string; + /** Main message content */ + children: ReactNode; + /** Optional callback when dismiss button is clicked */ + onDismiss?: () => void; + /** Additional CSS classes */ + className?: string; +}; + +/** + * Props for Card component. + */ +export type CardProps = { + orientation: string; + imageUrl?: string; + hasDetails?: boolean; + title?: string; + children?: ReactNode; +}; + +/** + * Props for ProgressBar component. + */ +export type ProgressBarProps = { + /** Current progress value */ + current: number; + /** Total/maximum value */ + total: number; + /** Whether the progress is actively running (shows animation) */ + isActive?: boolean; + /** Label shown on the left side */ + activeLabel?: string; + /** Label shown when complete */ + completeLabel?: string; + /** Additional CSS classes */ + className?: string; +}; + +/** + * Props for StatsCard component. + */ +export type StatsCardProps = { + /** The main numeric value to display */ + value: number; + /** Label text below the value */ + label: string; + /** Background color (CSS color string or Tailwind class) */ + backgroundColor?: string; + /** Text color for the value (defaults to white) */ + valueColor?: string; + /** Text color for the label (defaults to slightly transparent) */ + labelColor?: string; + /** Additional CSS classes */ + className?: string; +}; + +/** + * Props for T2Table component. + */ +export type T2TableProps = { + /** Row data to render. */ + sourceData?: TData[]; + /** Total number of records across all pages, used for pagination display. */ + totalPages?: number; + /** Column definitions (TanStack Table ColumnDef array). */ + columns?: ColumnDef[]; + /** Callbacks for navigating between pages. */ + paginationHandlers?: { + nextPage?(pageIndex: number, pageSize: number): void; + previousPage?(pageIndex: number, pageSize: number): void; + }; + /** Called with the TanStack row object when a row is clicked. */ + rowClickHandler?(row: Row): void; + /** Returns additional CSS classes for a given row (e.g. for highlight states). */ + getRowClassName?(row: Row): string; + /** Optional slot rendered in the toolbar area (e.g. a search input). */ + children?: ReactNode; +}; + +/** + * Props for MetadataPanel component. + */ +export type MetadataPanelProps = { + data: any; + isMissing?: boolean; +}; + +/** + * Props for Header component. + */ +export type HeaderProps = { + headerContent: string; + subHeaderContent?: ReactNode; + iconClassNames?: string; + link?: string; +}; + +/** + * Props for DatePicker component. + */ +export type DatePickerProps = { + inputValue: string; + setter: (value: string) => void; +}; + +/** + * Props for PopoverButton component. + */ +export type PopoverButtonProps = { + content: string; + clickHandler: () => void; +}; + +/** + * Props for TabulatedContentContainer component. + */ +export type TabulatedContentContainerProps = { + category: string; +}; + +/** + * Props for Downloads component. + */ +export type DownloadsProps = { + data: any; +}; + +/** + * Props for LibraryGrid component. + */ +export type LibraryGridProps = Record; + +/** + * Props for Settings component. + */ +export type SettingsProps = Record;