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

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

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.

View 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;
};

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

View 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;
};

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