Consolidating types across the project

This commit is contained in:
Rishi Ghan
2026-04-15 12:22:20 -04:00
parent 4514f578ae
commit 0c363dd8ae
27 changed files with 659 additions and 253 deletions

View File

@@ -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<Socket>();
const queryClient = useQueryClient();

View File

@@ -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<string, unknown>;
[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<string, unknown>;
comicInfo?: Record<string, unknown>;
};
acquisition?: Acquisition;
createdAt: string;
updatedAt: string;
};
userSettings?: Record<string, unknown>;
queryClient?: unknown;
comicObjectId?: string;
}
/**
* Displays full comic detail: cover, file info, action menu, and tabbed panels
* for metadata, archive operations, and acquisition.

View File

@@ -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;

View File

@@ -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 => {

View File

@@ -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<DownloadProgressTickProps> = ({
bundleId,

View File

@@ -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";

View File

@@ -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,

View File

@@ -1,16 +1,7 @@
import React, { ReactElement } from "react";
import Header from "../shared/Header";
import { GetLibraryStatisticsQuery, DirectorySize } from "../../graphql/generated";
type Stats = Omit<GetLibraryStatisticsQuery["getLibraryStatistics"], "comicDirectorySize"> & {
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,

View File

@@ -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();

View File

@@ -1,9 +1,6 @@
import * as React from "react";
import type { ZeroStateProps } from "../../types";
interface ZeroStateProps {
header: string;
message: string;
}
const ZeroState: React.FunctionComponent<ZeroStateProps> = (props) => {
return (
<article className="">

View File

@@ -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 },

View File

@@ -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,

View File

@@ -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<string, any>;
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<FilterOption>(initialFilter);
const [searchQuery, setSearchQuery] = useState<SearchQuery>({
const [searchQuery, setSearchQuery] = useState<LibrarySearchQuery>({
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",

View File

@@ -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,
);

View File

@@ -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 = () => {

View File

@@ -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: "",

View File

@@ -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<Record<string, boolean>>({});

View File

@@ -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<AlertVariant, {
container: string;

View File

@@ -5,25 +5,7 @@
*/
import { ReactElement } from "react";
/**
* Props for the ProgressBar component.
* @interface ProgressBarProps
*/
interface 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;
}
import type { ProgressBarProps } from "../../types";
/**
* A reusable progress bar component with percentage display.

View File

@@ -5,25 +5,7 @@
*/
import { ReactElement } from "react";
/**
* Props for the StatsCard component.
* @interface StatsCardProps
*/
interface 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;
}
import type { StatsCardProps } from "../../types";
/**
* A reusable stats card component for displaying numeric metrics.

View File

@@ -7,27 +7,7 @@ import {
useReactTable,
PaginationState,
} from "@tanstack/react-table";
/** Props for {@link T2Table}. */
interface T2TableProps<TData> {
/** 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<TData>[];
/** 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<TData>): void;
/** Returns additional CSS classes for a given row (e.g. for highlight states). */
getRowClassName?(row: Row<TData>): 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.