Consolidating types across the project
This commit is contained in:
@@ -34,19 +34,7 @@ import {
|
|||||||
import { isNil } from "lodash";
|
import { isNil } from "lodash";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
/**
|
import type { AirDCPPSearchData } from "../types";
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a promise that resolves after a specified delay.
|
* 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 axios from "axios";
|
||||||
import { AIRDCPP_SERVICE_BASE_URI } from "../../constants/endpoints";
|
import { AIRDCPP_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||||
import type { Socket } from "socket.io-client";
|
import type { Socket } from "socket.io-client";
|
||||||
|
import type { AcquisitionPanelProps } from "../../types";
|
||||||
interface IAcquisitionPanelProps {
|
|
||||||
query: any;
|
|
||||||
comicObjectId: any;
|
|
||||||
comicObject: any;
|
|
||||||
settings: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AcquisitionPanel = (
|
export const AcquisitionPanel = (
|
||||||
props: IAcquisitionPanelProps,
|
props: AcquisitionPanelProps,
|
||||||
): ReactElement => {
|
): ReactElement => {
|
||||||
const socketRef = useRef<Socket>();
|
const socketRef = useRef<Socket>();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import "react-sliding-pane/dist/react-sliding-pane.css";
|
|||||||
import SlidingPane from "react-sliding-pane";
|
import SlidingPane from "react-sliding-pane";
|
||||||
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
||||||
import { styled } from "styled-components";
|
import { styled } from "styled-components";
|
||||||
import type { RawFileDetails as RawFileDetailsType, InferredMetadata } from "../../graphql/generated";
|
import type { ComicDetailProps } from "../../types";
|
||||||
|
|
||||||
// Extracted modules
|
// Extracted modules
|
||||||
import { useComicVineMatching } from "./useComicVineMatching";
|
import { useComicVineMatching } from "./useComicVineMatching";
|
||||||
@@ -23,39 +23,6 @@ const StyledSlidingPanel = styled(SlidingPane)`
|
|||||||
background: #ccc;
|
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
|
* Displays full comic detail: cover, file info, action menu, and tabbed panels
|
||||||
* for metadata, archive operations, and acquisition.
|
* for metadata, archive operations, and acquisition.
|
||||||
|
|||||||
@@ -4,16 +4,7 @@ import dayjs from "dayjs";
|
|||||||
import { isEmpty, isUndefined } from "lodash";
|
import { isEmpty, isUndefined } from "lodash";
|
||||||
import Card from "../shared/Carda";
|
import Card from "../shared/Carda";
|
||||||
import { convert } from "html-to-text";
|
import { convert } from "html-to-text";
|
||||||
|
import type { ComicVineDetailsProps } from "../../types";
|
||||||
interface ComicVineDetailsProps {
|
|
||||||
updatedAt?: string;
|
|
||||||
data?: {
|
|
||||||
name?: string;
|
|
||||||
number?: string;
|
|
||||||
resource_type?: string;
|
|
||||||
id?: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ComicVineDetails = (props: ComicVineDetailsProps): ReactElement => {
|
export const ComicVineDetails = (props: ComicVineDetailsProps): ReactElement => {
|
||||||
const { data, updatedAt } = props;
|
const { data, updatedAt } = props;
|
||||||
|
|||||||
@@ -3,15 +3,7 @@ import MatchResult from "./MatchResult";
|
|||||||
import { isEmpty } from "lodash";
|
import { isEmpty } from "lodash";
|
||||||
import { useStore } from "../../store";
|
import { useStore } from "../../store";
|
||||||
import { useShallow } from "zustand/react/shallow";
|
import { useShallow } from "zustand/react/shallow";
|
||||||
|
import type { ComicVineMatchPanelProps } from "../../types";
|
||||||
interface ComicVineMatchPanelProps {
|
|
||||||
props: {
|
|
||||||
comicObjectId: string;
|
|
||||||
comicVineMatches: any[];
|
|
||||||
queryClient?: any;
|
|
||||||
onMatchApplied?: () => void;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Displays ComicVine search results or a status message while searching. */
|
/** Displays ComicVine search results or a status message while searching. */
|
||||||
export const ComicVineMatchPanel = ({ props: comicVineData }: ComicVineMatchPanelProps): ReactElement => {
|
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 React, { ReactElement, useEffect, useRef, useState } from "react";
|
||||||
import { useStore } from "../../store";
|
import { useStore } from "../../store";
|
||||||
import type { Socket } from "socket.io-client";
|
import type { Socket } from "socket.io-client";
|
||||||
|
import type { DownloadProgressTickProps } from "../../types";
|
||||||
/**
|
|
||||||
* @typedef {Object} DownloadProgressTickProps
|
|
||||||
* @property {string} bundleId - The bundle ID to filter ticks by (as string)
|
|
||||||
*/
|
|
||||||
interface DownloadProgressTickProps {
|
|
||||||
bundleId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shape of the download tick data received over the socket.
|
* 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;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
downloaded_bytes: number;
|
downloaded_bytes: number;
|
||||||
@@ -48,7 +28,7 @@ interface DownloadTickData {
|
|||||||
str: string;
|
str: string;
|
||||||
};
|
};
|
||||||
target: string;
|
target: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const DownloadProgressTick: React.FC<DownloadProgressTickProps> = ({
|
export const DownloadProgressTick: React.FC<DownloadProgressTickProps> = ({
|
||||||
bundleId,
|
bundleId,
|
||||||
|
|||||||
@@ -5,13 +5,7 @@ import ellipsize from "ellipsize";
|
|||||||
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useGetComicByIdQuery } from "../../graphql/generated";
|
import { useGetComicByIdQuery } from "../../graphql/generated";
|
||||||
|
import type { MatchResultProps } from "../../types";
|
||||||
interface MatchResultProps {
|
|
||||||
matchData: any;
|
|
||||||
comicObjectId: string;
|
|
||||||
queryClient?: any;
|
|
||||||
onMatchApplied?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleBrokenImage = (e) => {
|
const handleBrokenImage = (e) => {
|
||||||
e.target.src = "http://localhost:3050/dist/img/noimage.svg";
|
e.target.src = "http://localhost:3050/dist/img/noimage.svg";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { lazy } from "react";
|
import React, { lazy } from "react";
|
||||||
import { isNil, isEmpty } from "lodash";
|
import { isNil, isEmpty } from "lodash";
|
||||||
|
import type { TabConfig, TabConfigParams } from "../../types";
|
||||||
|
|
||||||
const VolumeInformation = lazy(() => import("./Tabs/VolumeInformation").then(m => ({ default: m.VolumeInformation })));
|
const VolumeInformation = lazy(() => import("./Tabs/VolumeInformation").then(m => ({ default: m.VolumeInformation })));
|
||||||
const ArchiveOperations = lazy(() => import("./Tabs/ArchiveOperations").then(m => ({ default: m.ArchiveOperations })));
|
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 TorrentSearchPanel = lazy(() => import("./TorrentSearchPanel"));
|
||||||
const DownloadsPanel = lazy(() => import("./DownloadsPanel"));
|
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 = ({
|
export const createTabConfig = ({
|
||||||
data,
|
data,
|
||||||
hasAnyMetadata,
|
hasAnyMetadata,
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
import React, { ReactElement } from "react";
|
import React, { ReactElement } from "react";
|
||||||
import Header from "../shared/Header";
|
import Header from "../shared/Header";
|
||||||
import { GetLibraryStatisticsQuery, DirectorySize } from "../../graphql/generated";
|
import { GetLibraryStatisticsQuery, DirectorySize } from "../../graphql/generated";
|
||||||
|
import type { LibraryStatisticsProps } from "../../types";
|
||||||
type Stats = Omit<GetLibraryStatisticsQuery["getLibraryStatistics"], "comicDirectorySize"> & {
|
|
||||||
comicDirectorySize: DirectorySize;
|
|
||||||
comicsMissingFiles: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Props for {@link LibraryStatistics}. */
|
|
||||||
interface LibraryStatisticsProps {
|
|
||||||
stats: Stats | null | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a snapshot of library metrics: total comic files, tagging coverage,
|
* 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 DatePickerDialog from "../shared/DatePicker";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { LocgMetadata, useGetWeeklyPullListQuery } from "../../graphql/generated";
|
import { LocgMetadata, useGetWeeklyPullListQuery } from "../../graphql/generated";
|
||||||
|
import type { PullListProps } from "../../types";
|
||||||
interface PullListProps {
|
|
||||||
issues?: LocgMetadata[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PullList = (): ReactElement => {
|
export const PullList = (): ReactElement => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import type { ZeroStateProps } from "../../types";
|
||||||
|
|
||||||
interface ZeroStateProps {
|
|
||||||
header: string;
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
const ZeroState: React.FunctionComponent<ZeroStateProps> = (props) => {
|
const ZeroState: React.FunctionComponent<ZeroStateProps> = (props) => {
|
||||||
return (
|
return (
|
||||||
<article className="">
|
<article className="">
|
||||||
|
|||||||
@@ -3,12 +3,9 @@ import { getTransfers } from "../../actions/airdcpp.actions";
|
|||||||
import { isEmpty, isNil, isUndefined } from "lodash";
|
import { isEmpty, isNil, isUndefined } from "lodash";
|
||||||
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
import { determineCoverFile } from "../../shared/utils/metadata.utils";
|
||||||
import MetadataPanel from "../shared/MetadataPanel";
|
import MetadataPanel from "../shared/MetadataPanel";
|
||||||
|
import type { DownloadsProps } from "../../types";
|
||||||
|
|
||||||
interface IDownloadsProps {
|
export const Downloads = (props: DownloadsProps): ReactElement => {
|
||||||
data: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Downloads = (props: IDownloadsProps): ReactElement => {
|
|
||||||
// const airDCPPConfiguration = useContext(AirDCPPSocketContext);
|
// const airDCPPConfiguration = useContext(AirDCPPSocketContext);
|
||||||
const {
|
const {
|
||||||
airDCPPState: { settings, socket },
|
airDCPPState: { settings, socket },
|
||||||
|
|||||||
@@ -5,12 +5,9 @@ import Card from "../shared/Carda";
|
|||||||
|
|
||||||
import { searchIssue } from "../../actions/fileops.actions";
|
import { searchIssue } from "../../actions/fileops.actions";
|
||||||
import MetadataPanel from "../shared/MetadataPanel";
|
import MetadataPanel from "../shared/MetadataPanel";
|
||||||
|
import type { GlobalSearchBarProps } from "../../types";
|
||||||
|
|
||||||
interface ISearchBarProps {
|
export const SearchBar = (data: GlobalSearchBarProps): ReactElement => {
|
||||||
data: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SearchBar = (data: ISearchBarProps): ReactElement => {
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const searchResults = useSelector(
|
const searchResults = useSelector(
|
||||||
(state: RootState) => state.fileOps.librarySearchResultsFormatted,
|
(state: RootState) => state.fileOps.librarySearchResultsFormatted,
|
||||||
|
|||||||
@@ -14,14 +14,7 @@ import axios from "axios";
|
|||||||
import { format, parseISO } from "date-fns";
|
import { format, parseISO } from "date-fns";
|
||||||
import { useGetWantedComicsQuery } from "../../graphql/generated";
|
import { useGetWantedComicsQuery } from "../../graphql/generated";
|
||||||
|
|
||||||
type FilterOption = "all" | "missingFiles";
|
import type { LibrarySearchQuery, FilterOption } from "../../types";
|
||||||
|
|
||||||
interface SearchQuery {
|
|
||||||
query: Record<string, any>;
|
|
||||||
pagination: { size: number; from: number };
|
|
||||||
type: string;
|
|
||||||
trigger: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FILTER_OPTIONS: { value: FilterOption; label: string }[] = [
|
const FILTER_OPTIONS: { value: FilterOption; label: string }[] = [
|
||||||
{ value: "all", label: "All Comics" },
|
{ value: "all", label: "All Comics" },
|
||||||
@@ -37,7 +30,7 @@ export const Library = (): ReactElement => {
|
|||||||
const initialFilter = (searchParams.get("filter") as FilterOption) ?? "all";
|
const initialFilter = (searchParams.get("filter") as FilterOption) ?? "all";
|
||||||
|
|
||||||
const [activeFilter, setActiveFilter] = useState<FilterOption>(initialFilter);
|
const [activeFilter, setActiveFilter] = useState<FilterOption>(initialFilter);
|
||||||
const [searchQuery, setSearchQuery] = useState<SearchQuery>({
|
const [searchQuery, setSearchQuery] = useState<LibrarySearchQuery>({
|
||||||
query: {},
|
query: {},
|
||||||
pagination: { size: 25, from: 0 },
|
pagination: { size: 25, from: 0 },
|
||||||
type: "all",
|
type: "all",
|
||||||
@@ -47,7 +40,7 @@ export const Library = (): ReactElement => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
/** Fetches a page of issues from the search API. */
|
/** Fetches a page of issues from the search API. */
|
||||||
const fetchIssues = async (q: SearchQuery) => {
|
const fetchIssues = async (q: LibrarySearchQuery) => {
|
||||||
const { pagination, query, type } = q;
|
const { pagination, query, type } = q;
|
||||||
return await axios({
|
return await axios({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ import Card from "../shared/Carda";
|
|||||||
import { detectIssueTypes } from "../../shared/utils/tradepaperback.utils";
|
import { detectIssueTypes } from "../../shared/utils/tradepaperback.utils";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { LIBRARY_SERVICE_HOST } from "../../constants/endpoints";
|
import { LIBRARY_SERVICE_HOST } from "../../constants/endpoints";
|
||||||
|
import type { LibraryGridProps } from "../../types";
|
||||||
|
|
||||||
interface ILibraryGridProps {}
|
export const LibraryGrid = (libraryGridProps: LibraryGridProps) => {
|
||||||
export const LibraryGrid = (libraryGridProps: ILibraryGridProps) => {
|
|
||||||
const data = useSelector(
|
const data = useSelector(
|
||||||
(state: RootState) => state.fileOps.recentComics.docs,
|
(state: RootState) => state.fileOps.recentComics.docs,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ import PullList from "../PullList/PullList";
|
|||||||
import { Volumes } from "../Volumes/Volumes";
|
import { Volumes } from "../Volumes/Volumes";
|
||||||
import WantedComics from "../WantedComics/WantedComics";
|
import WantedComics from "../WantedComics/WantedComics";
|
||||||
import { Library } from "./Library";
|
import { Library } from "./Library";
|
||||||
|
import type { TabulatedContentContainerProps } from "../../types";
|
||||||
interface ITabulatedContentContainerProps {
|
|
||||||
category: string;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Component to draw the contents of a category in a table.
|
* Component to draw the contents of a category in a table.
|
||||||
*
|
*
|
||||||
@@ -18,7 +15,7 @@ interface ITabulatedContentContainerProps {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const TabulatedContentContainer = (
|
const TabulatedContentContainer = (
|
||||||
props: ITabulatedContentContainerProps,
|
props: TabulatedContentContainerProps,
|
||||||
): ReactElement => {
|
): ReactElement => {
|
||||||
const { category } = props;
|
const { category } = props;
|
||||||
const renderTabulatedContent = () => {
|
const renderTabulatedContent = () => {
|
||||||
|
|||||||
@@ -16,10 +16,9 @@ import {
|
|||||||
LIBRARY_SERVICE_BASE_URI,
|
LIBRARY_SERVICE_BASE_URI,
|
||||||
} from "../../constants/endpoints";
|
} from "../../constants/endpoints";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import type { SearchPageProps } from "../../types";
|
||||||
|
|
||||||
interface ISearchProps {}
|
export const Search = ({}: SearchPageProps): ReactElement => {
|
||||||
|
|
||||||
export const Search = ({}: ISearchProps): ReactElement => {
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const formData = {
|
const formData = {
|
||||||
search: "",
|
search: "",
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ import DockerVars from "./DockerVars/DockerVars";
|
|||||||
import { ServiceStatuses } from "../ServiceStatuses/ServiceStatuses";
|
import { ServiceStatuses } from "../ServiceStatuses/ServiceStatuses";
|
||||||
import settingsObject from "../../constants/settings/settingsMenu.json";
|
import settingsObject from "../../constants/settings/settingsMenu.json";
|
||||||
import { isUndefined, map } from "lodash";
|
import { isUndefined, map } from "lodash";
|
||||||
|
import type { SettingsProps } from "../../types";
|
||||||
|
|
||||||
interface ISettingsProps {}
|
export const Settings = (props: SettingsProps): ReactElement => {
|
||||||
|
|
||||||
export const Settings = (props: ISettingsProps): ReactElement => {
|
|
||||||
const [active, setActive] = useState("gen-db");
|
const [active, setActive] = useState("gen-db");
|
||||||
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
|
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
|
|||||||
@@ -6,25 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ReactElement, ReactNode } from "react";
|
import { ReactElement, ReactNode } from "react";
|
||||||
|
import type { AlertVariant, AlertCardProps } from "../../types";
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const variantStyles: Record<AlertVariant, {
|
const variantStyles: Record<AlertVariant, {
|
||||||
container: string;
|
container: string;
|
||||||
|
|||||||
@@ -5,25 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
|
import type { ProgressBarProps } from "../../types";
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reusable progress bar component with percentage display.
|
* A reusable progress bar component with percentage display.
|
||||||
|
|||||||
@@ -5,25 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
|
import type { StatsCardProps } from "../../types";
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reusable stats card component for displaying numeric metrics.
|
* A reusable stats card component for displaying numeric metrics.
|
||||||
|
|||||||
@@ -7,27 +7,7 @@ import {
|
|||||||
useReactTable,
|
useReactTable,
|
||||||
PaginationState,
|
PaginationState,
|
||||||
} from "@tanstack/react-table";
|
} from "@tanstack/react-table";
|
||||||
|
import type { T2TableProps } from "../../types";
|
||||||
/** 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A paginated data table with a two-row sticky header.
|
* 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