🪑 Styled download panel table
This commit is contained in:
@@ -236,14 +236,19 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
|||||||
<i className="h-5 w-5 icon-[solar--download-bold-duotone] text-slate-500 dark:text-slate-300" />
|
<i className="h-5 w-5 icon-[solar--download-bold-duotone] text-slate-500 dark:text-slate-300" />
|
||||||
),
|
),
|
||||||
name: "Downloads",
|
name: "Downloads",
|
||||||
|
content:
|
||||||
content: !isNil(data.data) && !isEmpty(data.data) && (
|
!isNil(data.data) && !isEmpty(data.data) ? (
|
||||||
<DownloadsPanel
|
<DownloadsPanel key={5} />
|
||||||
data={data.data.acquisition.directconnect}
|
) : (
|
||||||
comicObjectId={comicObjectId}
|
<div className="column is-three-fifths">
|
||||||
key={5}
|
<article className="message is-info">
|
||||||
/>
|
<div className="message-body is-size-6 is-family-secondary">
|
||||||
),
|
AirDC++ is not configured. Please configure it in{" "}
|
||||||
|
<code>Settings</code>.
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
shouldShow: true,
|
shouldShow: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
import React, { useEffect, useContext, ReactElement } from "react";
|
import React, { useEffect, useContext, ReactElement, useState } from "react";
|
||||||
import { getBundlesForComic } from "../../actions/airdcpp.actions";
|
import { getBundlesForComic } from "../../actions/airdcpp.actions";
|
||||||
import { RootState } from "threetwo-ui-typings";
|
import { RootState } from "threetwo-ui-typings";
|
||||||
import { isEmpty, isNil, map } from "lodash";
|
import { isArray, isEmpty, isNil, isUndefined, map } from "lodash";
|
||||||
import prettyBytes from "pretty-bytes";
|
import prettyBytes from "pretty-bytes";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import ellipsize from "ellipsize";
|
import ellipsize from "ellipsize";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import axios from "axios";
|
||||||
|
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";
|
||||||
|
import { useStore } from "../../store";
|
||||||
|
import { useShallow } from "zustand/react/shallow";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
interface IDownloadsPanelProps {
|
interface IDownloadsPanelProps {
|
||||||
data: any;
|
key: number;
|
||||||
comicObjectId: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DownloadsPanel = (
|
export const DownloadsPanel = (
|
||||||
@@ -21,53 +26,85 @@ export const DownloadsPanel = (
|
|||||||
// // AirDCPP Socket initialization
|
// // AirDCPP Socket initialization
|
||||||
// const userSettings = useSelector((state: RootState) => state.settings.data);
|
// const userSettings = useSelector((state: RootState) => state.settings.data);
|
||||||
// const airDCPPConfiguration = useContext(AirDCPPSocketContext);
|
// const airDCPPConfiguration = useContext(AirDCPPSocketContext);
|
||||||
|
const { comicObjectId } = useParams<{ comicObjectId: string }>();
|
||||||
const {
|
const [bundles, setBundles] = useState([]);
|
||||||
airDCPPState: { socket, settings },
|
const { airDCPPSocketInstance } = useStore(
|
||||||
} = airDCPPConfiguration;
|
useShallow((state) => ({
|
||||||
|
airDCPPSocketInstance: state.airDCPPSocketInstance,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
// Fetch the downloaded files and currently-downloading file(s) from AirDC++
|
// Fetch the downloaded files and currently-downloading file(s) from AirDC++
|
||||||
useEffect(() => {
|
const { data: comicObject, isSuccess } = useQuery({
|
||||||
try {
|
queryKey: ["bundles"],
|
||||||
if (!isEmpty(userSettings)) {
|
queryFn: async () =>
|
||||||
// dispatch(
|
await axios({
|
||||||
// getBundlesForComic(props.comicObjectId, socket, {
|
url: `${LIBRARY_SERVICE_BASE_URI}/getComicBookById`,
|
||||||
// username: `${settings.directConnect.client.host.username}`,
|
method: "POST",
|
||||||
// password: `${settings.directConnect.client.host.password}`,
|
headers: {
|
||||||
// }),
|
"Content-Type": "application/json; charset=utf-8",
|
||||||
// );
|
},
|
||||||
}
|
data: {
|
||||||
} catch (error) {
|
id: `${comicObjectId}`,
|
||||||
throw new Error(error);
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const getBundles = async (comicObject) => {
|
||||||
|
if (comicObject?.data.acquisition.directconnect) {
|
||||||
|
const filteredBundles =
|
||||||
|
comicObject.data.acquisition.directconnect.downloads.map(
|
||||||
|
async ({ bundleId }) => {
|
||||||
|
return await airDCPPSocketInstance.get(`queue/bundles/${bundleId}`);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return await Promise.all(filteredBundles);
|
||||||
}
|
}
|
||||||
}, [airDCPPConfiguration]);
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getBundles(comicObject).then((result) => {
|
||||||
|
setBundles(result);
|
||||||
|
});
|
||||||
|
}, [isSuccess]);
|
||||||
|
|
||||||
const Bundles = (props) => {
|
const Bundles = (props) => {
|
||||||
return !isEmpty(props.data) ? (
|
console.log("asdas", props);
|
||||||
<div className="column is-full">
|
return (
|
||||||
<table className="table is-striped">
|
<div className="overflow-x-auto w-fit mt-4 rounded-lg border border-gray-200">
|
||||||
|
<table className="min-w-full divide-y-2 divide-gray-200 dark:divide-gray-200 text-md">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Filename</th>
|
<th className="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-slate-200">
|
||||||
<th>Size</th>
|
Filename
|
||||||
<th>Download Time</th>
|
</th>
|
||||||
<th>Bundle ID</th>
|
<th className="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-slate-200">
|
||||||
|
Size
|
||||||
|
</th>
|
||||||
|
<th className="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-slate-200">
|
||||||
|
Download Time
|
||||||
|
</th>
|
||||||
|
<th className="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-slate-200">
|
||||||
|
Bundle ID
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody className="divide-y divide-gray-200">
|
||||||
{map(props.data, (bundle) => (
|
{map(props.data, (bundle) => (
|
||||||
<tr key={bundle.id}>
|
<tr key={bundle.id} className="text-sm">
|
||||||
<td>
|
<td className="whitespace-nowrap px-2 py-2 text-gray-700 dark:text-slate-300">
|
||||||
<h5>{ellipsize(bundle.name, 58)}</h5>
|
<h5>{ellipsize(bundle.name, 58)}</h5>
|
||||||
<span className="is-size-7">{bundle.target}</span>
|
<span className="is-size-7">{bundle.target}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{prettyBytes(bundle.size)}</td>
|
<td className="whitespace-nowrap px-2 py-2 text-gray-700 dark:text-slate-300">
|
||||||
<td>
|
{prettyBytes(bundle.size)}
|
||||||
|
</td>
|
||||||
|
<td className="whitespace-nowrap px-2 py-2 text-gray-700 dark:text-slate-300">
|
||||||
{dayjs
|
{dayjs
|
||||||
.unix(bundle.time_finished)
|
.unix(bundle.time_finished)
|
||||||
.format("h:mm on ddd, D MMM, YYYY")}
|
.format("h:mm on ddd, D MMM, YYYY")}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td className="whitespace-nowrap px-2 py-2 text-gray-700 dark:text-slate-300">
|
||||||
<span className="tag is-warning">{bundle.id}</span>
|
<span className="tag is-warning">{bundle.id}</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -75,29 +112,15 @@ export const DownloadsPanel = (
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<div className="column is-full"> {"No Downloads Found"} </div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return !isNil(props.data) ? (
|
return (
|
||||||
<>
|
<div className="columns is-multiline">
|
||||||
<div className="columns is-multiline">
|
{!isEmpty(airDCPPSocketInstance) && !isEmpty(bundles) && (
|
||||||
{!isEmpty(socket) ? (
|
<Bundles data={bundles} />
|
||||||
<Bundles data={bundles} />
|
)}
|
||||||
) : (
|
</div>
|
||||||
<div className="column is-three-fifths">
|
);
|
||||||
<article className="message is-info">
|
|
||||||
<div className="message-body is-size-6 is-family-secondary">
|
|
||||||
AirDC++ is not configured. Please configure it in{" "}
|
|
||||||
<code>Settings</code>.
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DownloadsPanel;
|
export default DownloadsPanel;
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ export const RawFileDetails = (props): ReactElement => {
|
|||||||
<>
|
<>
|
||||||
<div className="max-w-2xl ml-5">
|
<div className="max-w-2xl ml-5">
|
||||||
<div className="px-4 sm:px-6">
|
<div className="px-4 sm:px-6">
|
||||||
<p className="text-gray-500">
|
<p className="text-gray-500 dark:text-gray-400">
|
||||||
<span className="text-xl">{rawFileDetails.name}</span>
|
<span className="text-xl">{rawFileDetails.name}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-4 py-5 sm:px-6">
|
<div className="px-4 py-5 sm:px-6">
|
||||||
<dl className="grid grid-cols-1 gap-x-4 gap-y-4 sm:grid-cols-2">
|
<dl className="grid grid-cols-1 gap-x-4 gap-y-4 sm:grid-cols-2">
|
||||||
<div className="sm:col-span-1">
|
<div className="sm:col-span-1">
|
||||||
<dt className="text-sm font-medium text-gray-500">
|
<dt className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||||
Raw File Details
|
Raw File Details
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="mt-1 text-sm text-gray-900">
|
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
||||||
{rawFileDetails.containedIn +
|
{rawFileDetails.containedIn +
|
||||||
"/" +
|
"/" +
|
||||||
rawFileDetails.name +
|
rawFileDetails.name +
|
||||||
@@ -30,10 +30,10 @@ export const RawFileDetails = (props): ReactElement => {
|
|||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:col-span-1">
|
<div className="sm:col-span-1">
|
||||||
<dt className="text-sm font-medium text-gray-500">
|
<dt className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||||
Inferred Issue Metadata
|
Inferred Issue Metadata
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="mt-1 text-sm text-gray-900">
|
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
||||||
Series Name: {inferredMetadata.issue.name}
|
Series Name: {inferredMetadata.issue.name}
|
||||||
{!isEmpty(inferredMetadata.issue.number) ? (
|
{!isEmpty(inferredMetadata.issue.number) ? (
|
||||||
<span className="tag is-primary is-light">
|
<span className="tag is-primary is-light">
|
||||||
@@ -43,8 +43,10 @@ export const RawFileDetails = (props): ReactElement => {
|
|||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:col-span-1">
|
<div className="sm:col-span-1">
|
||||||
<dt className="text-sm font-medium text-gray-500">MIMEType</dt>
|
<dt className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||||
<dd className="mt-1 text-sm text-gray-900">
|
MIMEType
|
||||||
|
</dt>
|
||||||
|
<dd className="mt-1 text-sm text-gray-500 dark:text-slate-900">
|
||||||
{/* File extension */}
|
{/* File extension */}
|
||||||
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
|
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
|
||||||
<span className="pr-1 pt-1">
|
<span className="pr-1 pt-1">
|
||||||
@@ -56,11 +58,12 @@ export const RawFileDetails = (props): ReactElement => {
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</dd>
|
</dd>
|
||||||
<dd className="text-sm text-gray-900"></dd>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:col-span-1">
|
<div className="sm:col-span-1">
|
||||||
<dt className="text-sm font-medium text-gray-500">File Size</dt>
|
<dt className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||||
<dd className="mt-1 text-sm text-gray-900">
|
File Size
|
||||||
|
</dt>
|
||||||
|
<dd className="mt-1 text-sm text-gray-500 dark:text-slate-900">
|
||||||
{/* size */}
|
{/* size */}
|
||||||
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
|
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
|
||||||
<span className="pr-1 pt-1">
|
<span className="pr-1 pt-1">
|
||||||
@@ -74,16 +77,18 @@ export const RawFileDetails = (props): ReactElement => {
|
|||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:col-span-2">
|
<div className="sm:col-span-2">
|
||||||
<dt className="text-sm font-medium text-gray-500">
|
<dt className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||||
Import Details
|
Import Details
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="mt-1 text-sm text-gray-900">
|
<dd className="mt-1 text-sm text-gray-900 dark:text-gray-400">
|
||||||
{format(parseISO(created_at), "dd MMMM, yyyy")},{" "}
|
{format(parseISO(created_at), "dd MMMM, yyyy")},{" "}
|
||||||
{format(parseISO(created_at), "h aaaa")}
|
{format(parseISO(created_at), "h aaaa")}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:col-span-2">
|
<div className="sm:col-span-2">
|
||||||
<dt className="text-sm font-medium text-gray-500">Attachments</dt>
|
<dt className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||||
|
Actions
|
||||||
|
</dt>
|
||||||
<dd className="mt-1 text-sm text-gray-900">
|
<dd className="mt-1 text-sm text-gray-900">
|
||||||
<ul
|
<ul
|
||||||
role="list"
|
role="list"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const TabControls = (props): ReactElement => {
|
|||||||
{filteredTabs.map(({ id, name, icon }) => (
|
{filteredTabs.map(({ id, name, icon }) => (
|
||||||
<a
|
<a
|
||||||
key={id}
|
key={id}
|
||||||
className="inline-flex shrink-0 items-center gap-2 border-b border-transparent px-1 py-1 text-md font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700"
|
className="inline-flex shrink-0 items-center gap-2 border-b border-transparent px-1 py-1 text-md font-medium text-gray-500 dark:text-gray-400 hover:border-gray-300 hover:text-gray-700"
|
||||||
aria-current="page"
|
aria-current="page"
|
||||||
onClick={() => setActive(id)}
|
onClick={() => setActive(id)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -130,7 +130,6 @@ export const Dashboard = (): ReactElement => {
|
|||||||
hasDetails
|
hasDetails
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<dt className="sr-only">Address</dt>
|
|
||||||
<dd className="text-sm my-1 flex flex-row gap-1">
|
<dd className="text-sm my-1 flex flex-row gap-1">
|
||||||
{/* Issue number */}
|
{/* Issue number */}
|
||||||
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2.5 py-0.5 rounded-md dark:text-slate-900 dark:bg-slate-400">
|
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2.5 py-0.5 rounded-md dark:text-slate-900 dark:bg-slate-400">
|
||||||
@@ -154,11 +153,11 @@ export const Dashboard = (): ReactElement => {
|
|||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row items-center gap-1 my-2">
|
<div className="flex flex-row items-center gap-1 my-1">
|
||||||
<div className="sm:inline-flex sm:shrink-0 sm:items-center sm:gap-2">
|
<div className="sm:inline-flex sm:shrink-0 sm:items-center sm:gap-2">
|
||||||
{/* ComicInfo.xml presence */}
|
{/* ComicInfo.xml presence */}
|
||||||
{!isNil(comicInfo) && !isEmpty(comicInfo) && (
|
{!isNil(comicInfo) && !isEmpty(comicInfo) && (
|
||||||
<i className="h-7 w-7 icon-[solar--code-file-bold-duotone] text-slate-500 dark:text-slate-300"></i>
|
<i className="h-7 w-7 my-1 icon-[solar--code-file-bold-duotone] text-yellow-500 dark:text-yellow-300"></i>
|
||||||
)}
|
)}
|
||||||
{/* ComicVine metadata presence */}
|
{/* ComicVine metadata presence */}
|
||||||
{isComicVineMetadataAvailable && (
|
{isComicVineMetadataAvailable && (
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ export const Import = (props: IProps): ReactElement => {
|
|||||||
<span className="h-px flex-1 bg-slate-200 dark:bg-slate-400"></span>
|
<span className="h-px flex-1 bg-slate-200 dark:bg-slate-400"></span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="overflow-x-auto mt-4 rounded-lg border border-gray-200">
|
<div className="overflow-x-auto w-fit mt-4 rounded-lg border border-gray-200">
|
||||||
<table className="min-w-full divide-y-2 divide-gray-200 dark:divide-gray-200 text-md">
|
<table className="min-w-full divide-y-2 divide-gray-200 dark:divide-gray-200 text-md">
|
||||||
<thead className="ltr:text-left rtl:text-right">
|
<thead className="ltr:text-left rtl:text-right">
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ const renderCard = (props: ICardProps): ReactElement => {
|
|||||||
<div className="mt-2 px-2">
|
<div className="mt-2 px-2">
|
||||||
<dl>
|
<dl>
|
||||||
<div>
|
<div>
|
||||||
<dd className="text-md text-slate-500 dark:text-black">
|
<dd className="text-sm text-slate-500 dark:text-black">
|
||||||
{props.title}
|
{props.title}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user