Consolidating types across the project
This commit is contained in:
@@ -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<SearchQuery, "pattern"> & Partial<Omit<SearchQuery, "pattern">>;
|
||||
hub_urls: string[] | undefined | null;
|
||||
priority: PriorityEnum;
|
||||
}
|
||||
import type { AirDCPPSearchData } from "../types";
|
||||
|
||||
/**
|
||||
* Creates a promise that resolves after a specified delay.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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="">
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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: "",
|
||||
|
||||
@@ -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>>({});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
224
src/client/types/comic.types.ts
Normal file
224
src/client/types/comic.types.ts
Normal file
@@ -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<string, unknown>;
|
||||
[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<string, unknown>;
|
||||
comicInfo?: Record<string, unknown>;
|
||||
};
|
||||
acquisition?: Acquisition;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
userSettings?: Record<string, unknown>;
|
||||
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<string, unknown>;
|
||||
comicInfo?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
78
src/client/types/dashboard.types.ts
Normal file
78
src/client/types/dashboard.types.ts
Normal file
@@ -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;
|
||||
};
|
||||
74
src/client/types/index.ts
Normal file
74
src/client/types/index.ts
Normal file
@@ -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";
|
||||
94
src/client/types/search.types.ts
Normal file
94
src/client/types/search.types.ts
Normal file
@@ -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<string, any>;
|
||||
pagination: { size: number; from: number };
|
||||
type: string;
|
||||
trigger: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* AirDC++ search data configuration.
|
||||
*/
|
||||
export type AirDCPPSearchData = {
|
||||
query: Pick<AirDCPPSearchQuery, "pattern"> & Partial<Omit<AirDCPPSearchQuery, "pattern">>;
|
||||
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<string, never>;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
156
src/client/types/shared.types.ts
Normal file
156
src/client/types/shared.types.ts
Normal file
@@ -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<TData> = {
|
||||
/** 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<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;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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<string, never>;
|
||||
|
||||
/**
|
||||
* Props for Settings component.
|
||||
*/
|
||||
export type SettingsProps = Record<string, never>;
|
||||
Reference in New Issue
Block a user