Dark mode refactor (#98)
* 🏗️ Acquisition Panel refactor WIP * 🔧 Formatted the search query box * 🔧 Implementing download method * 🏗️ Refactored the AirDC++ download panel * 🌜 Initial Dark Mode support * 🌜 Trying dark mode on the react-select * Update App.scss * 🏗️ Migrating Navbar to TailwindCSS * 🖼️ Added solar icons * 🔧 Added solar icons * 🔧 Added code for dark mode toggle * 🏗️ Wiring up the dark mode toggle * 🌜 Added Dark mode to the body * 🏗️ Building out the import page * 🪑 Cleaning up the table styles * 🏗️ Cleaned up past imports table * 🏗️ Refactored Import socket events * 🏗️ Refactored the card grid on dashboard * 🏗️ Building variants for Cards * 🏗️ Added a horizontal medium variant * 🏗️ Cleaning up forms and cards * 🔧 Styling form inputs * 🏗️ Form refactor * 🔠 Added a monospace font * 🪑 Refactoring the table * 🧹 Formatting in connection confirmation panels * 🏗️ Refactoring table for library * 🏗️ Added icons and details to metadata * 🏗️ Cleaned the table further * 🏗️ Fixed fonts, and comic detail page first draft * ❌ Removing yarn.lockfile
This commit was merged in pull request #98.
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
import React, { useCallback, ReactElement, useEffect, useState } from "react";
|
||||
import {
|
||||
downloadAirDCPPItem,
|
||||
getBundlesForComic,
|
||||
sleep,
|
||||
} from "../../actions/airdcpp.actions";
|
||||
import { getBundlesForComic, sleep } from "../../actions/airdcpp.actions";
|
||||
import { SearchQuery, PriorityEnum, SearchResponse } from "threetwo-ui-typings";
|
||||
import { RootState, SearchInstance } from "threetwo-ui-typings";
|
||||
import ellipsize from "ellipsize";
|
||||
@@ -13,6 +9,7 @@ import { isEmpty, isNil, map } from "lodash";
|
||||
import { useStore } from "../../store";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
|
||||
interface IAcquisitionPanelProps {
|
||||
query: any;
|
||||
@@ -28,14 +25,22 @@ export const AcquisitionPanel = (
|
||||
airDCPPSocketInstance,
|
||||
airDCPPClientConfiguration,
|
||||
airDCPPSessionInformation,
|
||||
airDCPPDownloadTick,
|
||||
} = useStore(
|
||||
useShallow((state) => ({
|
||||
airDCPPSocketInstance: state.airDCPPSocketInstance,
|
||||
airDCPPClientConfiguration: state.airDCPPClientConfiguration,
|
||||
airDCPPSessionInformation: state.airDCPPSessionInformation,
|
||||
airDCPPDownloadTick: state.airDCPPDownloadTick,
|
||||
})),
|
||||
);
|
||||
|
||||
interface SearchData {
|
||||
query: Pick<SearchQuery, "pattern"> & Partial<Omit<SearchQuery, "pattern">>;
|
||||
hub_urls: string[] | undefined | null;
|
||||
priority: PriorityEnum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hubs list from an AirDCPP Socket
|
||||
*/
|
||||
@@ -43,34 +48,15 @@ export const AcquisitionPanel = (
|
||||
queryKey: ["hubs"],
|
||||
queryFn: async () => await airDCPPSocketInstance.get(`hubs`),
|
||||
});
|
||||
|
||||
const { comicObjectId } = props;
|
||||
const issueName = props.query.issue.name || "";
|
||||
// const { settings } = props;
|
||||
const sanitizedIssueName = issueName.replace(/[^a-zA-Z0-9 ]/g, " ");
|
||||
|
||||
// Selectors for picking state
|
||||
// const airDCPPSearchResults = useSelector((state: RootState) => {
|
||||
// return state.airdcpp.searchResults;
|
||||
// });
|
||||
// const isAirDCPPSearchInProgress = useSelector(
|
||||
// (state: RootState) => state.airdcpp.isAirDCPPSearchInProgress,
|
||||
// );
|
||||
// const searchInfo = useSelector(
|
||||
// (state: RootState) => state.airdcpp.searchInfo,
|
||||
// );
|
||||
// const searchInstance: SearchInstance = useSelector(
|
||||
// (state: RootState) => state.airdcpp.searchInstance,
|
||||
// );
|
||||
|
||||
// const settings = useSelector((state: RootState) => state.settings.data);
|
||||
// const airDCPPConfiguration = useContext(AirDCPPSocketContext);
|
||||
interface SearchData {
|
||||
query: Pick<SearchQuery, "pattern"> & Partial<Omit<SearchQuery, "pattern">>;
|
||||
hub_urls: string[] | undefined | null;
|
||||
priority: PriorityEnum;
|
||||
}
|
||||
const [dcppQuery, setDcppQuery] = useState({});
|
||||
const [airDCPPSearchResults, setAirDCPPSearchResults] = useState([]);
|
||||
const [airDCPPSearchStatus, setAirDCPPSearchStatus] = useState(false);
|
||||
const [airDCPPSearchInstance, setAirDCPPSearchInstance] = useState({});
|
||||
const [airDCPPSearchInfo, setAirDCPPSearchInfo] = useState({});
|
||||
|
||||
// Construct a AirDC++ query based on metadata inferred, upon component mount
|
||||
// Pre-populate the search input with the search string, so that
|
||||
@@ -88,15 +74,18 @@ export const AcquisitionPanel = (
|
||||
setDcppQuery(dcppSearchQuery);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Method to perform a search via an AirDC++ websocket
|
||||
* @param {SearchData} data - a SearchData query
|
||||
* @param {any} ADCPPSocket - an intialized AirDC++ socket instance
|
||||
*/
|
||||
const search = async (data: SearchData, ADCPPSocket: any) => {
|
||||
try {
|
||||
if (!ADCPPSocket.isConnected()) {
|
||||
await ADCPPSocket();
|
||||
}
|
||||
const instance: SearchInstance = await ADCPPSocket.post("search");
|
||||
// dispatch({
|
||||
// type: AIRDCPP_SEARCH_IN_PROGRESS,
|
||||
// });
|
||||
setAirDCPPSearchStatus(true);
|
||||
|
||||
// We want to get notified about every new result in order to make the user experience better
|
||||
await ADCPPSocket.addListener(
|
||||
@@ -142,6 +131,9 @@ export const AcquisitionPanel = (
|
||||
const currentInstance = await ADCPPSocket.get(
|
||||
`search/${instance.id}`,
|
||||
);
|
||||
setAirDCPPSearchInstance(currentInstance);
|
||||
setAirDCPPSearchInfo(searchInfo);
|
||||
console.log("Asdas", airDCPPSearchInfo);
|
||||
if (currentInstance.result_count === 0) {
|
||||
// ...nothing was received, show an informative message to the user
|
||||
console.log("No more search results.");
|
||||
@@ -149,11 +141,8 @@ export const AcquisitionPanel = (
|
||||
|
||||
// The search can now be considered to be "complete"
|
||||
// If there's an "in progress" indicator in the UI, that could also be disabled here
|
||||
// dispatch({
|
||||
// type: AIRDCPP_HUB_SEARCHES_SENT,
|
||||
// searchInfo,
|
||||
// instance,
|
||||
// });
|
||||
setAirDCPPSearchInstance(instance);
|
||||
setAirDCPPSearchStatus(false);
|
||||
},
|
||||
instance.id,
|
||||
);
|
||||
@@ -164,6 +153,68 @@ export const AcquisitionPanel = (
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Method to download a bundle associated with a search result from AirDC++
|
||||
* @param {Number} searchInstanceId - description
|
||||
* @param {String} resultId - description
|
||||
* @param {String} comicObjectId - description
|
||||
* @param {String} name - description
|
||||
* @param {Number} size - description
|
||||
* @param {any} type - description
|
||||
* @param {any} ADCPPSocket - description
|
||||
* @returns {void} - description
|
||||
*/
|
||||
const download = async (
|
||||
searchInstanceId: Number,
|
||||
resultId: String,
|
||||
comicObjectId: String,
|
||||
name: String,
|
||||
size: Number,
|
||||
type: any,
|
||||
ADCPPSocket: any,
|
||||
): void => {
|
||||
try {
|
||||
if (!ADCPPSocket.isConnected()) {
|
||||
await ADCPPSocket.connect();
|
||||
}
|
||||
let bundleDBImportResult = {};
|
||||
const downloadResult = await ADCPPSocket.post(
|
||||
`search/${searchInstanceId}/results/${resultId}/download`,
|
||||
);
|
||||
|
||||
if (!isNil(downloadResult)) {
|
||||
bundleDBImportResult = await axios({
|
||||
method: "POST",
|
||||
url: `http://localhost:3000/api/library/applyAirDCPPDownloadMetadata`,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
data: {
|
||||
bundleId: downloadResult.bundle_info.id,
|
||||
comicObjectId,
|
||||
name,
|
||||
size,
|
||||
type,
|
||||
},
|
||||
});
|
||||
|
||||
// dispatch({
|
||||
// type: AIRDCPP_RESULT_DOWNLOAD_INITIATED,
|
||||
// downloadResult,
|
||||
// bundleDBImportResult,
|
||||
// });
|
||||
//
|
||||
// dispatch({
|
||||
// type: IMS_COMIC_BOOK_DB_OBJECT_FETCHED,
|
||||
// comicBookDetail: bundleDBImportResult.data,
|
||||
// IMS_inProgress: false,
|
||||
// });
|
||||
}
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
const getDCPPSearchResults = async (searchQuery) => {
|
||||
const manualQuery = {
|
||||
query: {
|
||||
@@ -209,7 +260,6 @@ export const AcquisitionPanel = (
|
||||
},
|
||||
[],
|
||||
);
|
||||
console.log("yaman", airDCPPSearchResults);
|
||||
return (
|
||||
<>
|
||||
<div className="comic-detail columns">
|
||||
@@ -245,7 +295,7 @@ export const AcquisitionPanel = (
|
||||
<button
|
||||
type="submit"
|
||||
className={
|
||||
false
|
||||
airDCPPSearchStatus
|
||||
? "button is-loading is-warning"
|
||||
: "button is-success is-light"
|
||||
}
|
||||
@@ -273,6 +323,60 @@ export const AcquisitionPanel = (
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* AirDC++ search instance details */}
|
||||
{!isNil(airDCPPSearchInstance) &&
|
||||
!isEmpty(airDCPPSearchInfo) &&
|
||||
!isNil(hubs) && (
|
||||
<div className="columns">
|
||||
<div className="column is-one-quarter is-size-7">
|
||||
<div className="card">
|
||||
<div className="card-content">
|
||||
<dl>
|
||||
<dt>
|
||||
<div className="tags mb-1">
|
||||
{hubs.map((value, idx) => (
|
||||
<span className="tag is-warning" key={idx}>
|
||||
{value.identity.name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</dt>
|
||||
<dt>
|
||||
Query:
|
||||
<span className="has-text-weight-semibold">
|
||||
{airDCPPSearchInfo.query.pattern}
|
||||
</span>
|
||||
</dt>
|
||||
<dd>
|
||||
Extensions:
|
||||
<span className="has-text-weight-semibold">
|
||||
{airDCPPSearchInfo.query.extensions.join(", ")}
|
||||
</span>
|
||||
</dd>
|
||||
<dd>
|
||||
File type:
|
||||
<span className="has-text-weight-semibold">
|
||||
{airDCPPSearchInfo.query.file_type}
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="column is-one-quarter is-size-7">
|
||||
<div className="card">
|
||||
<div className="card-content">
|
||||
<dl>
|
||||
<dt>Search Instance: {airDCPPSearchInstance.id}</dt>
|
||||
<dt>Owned by {airDCPPSearchInstance.owner}</dt>
|
||||
<dd>Expires in: {airDCPPSearchInstance.expires_in}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* AirDC++ results */}
|
||||
<div className="columns">
|
||||
{!isNil(airDCPPSearchResults) && !isEmpty(airDCPPSearchResults) ? (
|
||||
@@ -342,12 +446,14 @@ export const AcquisitionPanel = (
|
||||
<button
|
||||
className="button is-small is-light is-success"
|
||||
onClick={() =>
|
||||
downloadDCPPResult(
|
||||
searchInstance.id,
|
||||
download(
|
||||
airDCPPSearchInstance.id,
|
||||
result.id,
|
||||
comicObjectId,
|
||||
result.name,
|
||||
result.size,
|
||||
result.type,
|
||||
airDCPPSocketInstance,
|
||||
)
|
||||
}
|
||||
>
|
||||
|
||||
@@ -69,17 +69,29 @@ export const Menu = (props): ReactElement => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
const customStyles = {
|
||||
option: (base, { data, isDisabled, isFocused, isSelected }) => {
|
||||
return {
|
||||
...base,
|
||||
backgroundColor: isFocused ? "gray" : "black",
|
||||
};
|
||||
},
|
||||
control: (base) => ({
|
||||
...base,
|
||||
backgroundColor: "black",
|
||||
border: "1px solid #CCC",
|
||||
}),
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
className="basic-single"
|
||||
classNamePrefix="select"
|
||||
components={{ Placeholder }}
|
||||
placeholder={
|
||||
<span>
|
||||
<i className="fa-solid fa-list"></i> Actions
|
||||
</span>
|
||||
}
|
||||
styles={customStyles}
|
||||
name="actions"
|
||||
isSearchable={false}
|
||||
options={filteredActionOptions}
|
||||
|
||||
@@ -47,6 +47,8 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
||||
inferredMetadata,
|
||||
sourcedMetadata: { comicvine, locg, comicInfo },
|
||||
acquisition,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
},
|
||||
userSettings,
|
||||
} = data;
|
||||
@@ -246,67 +248,58 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
|
||||
// 2. from the CV-scraped version
|
||||
|
||||
return (
|
||||
<section className="container">
|
||||
<section className="container mx-auto">
|
||||
<div className="section">
|
||||
{!isNil(data) && !isEmpty(data) && (
|
||||
<>
|
||||
<h1 className="title">{issueName}</h1>
|
||||
<div className="columns is-multiline">
|
||||
<div className="column is-narrow">
|
||||
<div>
|
||||
<div className="flex flex-row mt-5">
|
||||
<Card
|
||||
imageUrl={url}
|
||||
orientation={"vertical"}
|
||||
orientation={"cover-only"}
|
||||
hasDetails={false}
|
||||
cardContainerStyle={{ maxWidth: 275 }}
|
||||
/>
|
||||
{/* action dropdown */}
|
||||
<div className="mt-4 is-size-7">
|
||||
<Menu
|
||||
data={data.data}
|
||||
handlers={{ setSlidingPanelContentId, setVisible }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* raw file details */}
|
||||
<div className="column">
|
||||
|
||||
{/* raw file details */}
|
||||
{!isUndefined(rawFileDetails) &&
|
||||
!isEmpty(rawFileDetails.cover) && (
|
||||
<>
|
||||
<div className="grid">
|
||||
<RawFileDetails
|
||||
data={{
|
||||
rawFileDetails: rawFileDetails,
|
||||
inferredMetadata: inferredMetadata,
|
||||
created_at: createdAt,
|
||||
updated_at: updatedAt,
|
||||
}}
|
||||
/>
|
||||
{/* Read comic button */}
|
||||
<button
|
||||
className="button is-success is-light"
|
||||
onClick={() => openModal(rawFileDetails.filePath)}
|
||||
>
|
||||
<i className="fa-solid fa-book-open mr-2"></i>
|
||||
Read
|
||||
</button>
|
||||
|
||||
{/* <Modal
|
||||
style={{ content: { marginTop: "2rem" } }}
|
||||
isOpen={modalIsOpen}
|
||||
onAfterOpen={afterOpenModal}
|
||||
onRequestClose={closeModal}
|
||||
contentLabel="Example Modal"
|
||||
>
|
||||
<button onClick={closeModal}>close</button>
|
||||
{extractedComicBook && (
|
||||
<ComicViewer
|
||||
pages={extractedComicBook}
|
||||
direction="ltr"
|
||||
className={{
|
||||
closeButton: "border: 1px solid red;",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Modal> */}
|
||||
</>
|
||||
style={{ content: { marginTop: "2rem" } }}
|
||||
isOpen={modalIsOpen}
|
||||
onAfterOpen={afterOpenModal}
|
||||
onRequestClose={closeModal}
|
||||
contentLabel="Example Modal"
|
||||
>
|
||||
<button onClick={closeModal}>close</button>
|
||||
{extractedComicBook && (
|
||||
<ComicViewer
|
||||
pages={extractedComicBook}
|
||||
direction="ltr"
|
||||
className={{
|
||||
closeButton: "border: 1px solid red;",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Modal> */}
|
||||
</div>
|
||||
)}
|
||||
{/* action dropdown */}
|
||||
{/* <div className="mt-4 is-size-7">
|
||||
<Menu
|
||||
data={data.data}
|
||||
handlers={{ setSlidingPanelContentId, setVisible }}
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ import React, { ReactElement } from "react";
|
||||
|
||||
export const DownloadProgressTick = (props): ReactElement => {
|
||||
return (
|
||||
<div >
|
||||
<h4 className="is-size-6">{props.data.name}</h4>
|
||||
<div>
|
||||
<h4 className="is-size-5">{props.data.name}</h4>
|
||||
<div>
|
||||
<span className="is-size-3 has-text-weight-semibold">
|
||||
<span className="is-size-4 has-text-weight-semibold">
|
||||
{prettyBytes(props.data.downloaded_bytes)} of{" "}
|
||||
{prettyBytes(props.data.size)}{" "}
|
||||
</span>
|
||||
@@ -20,13 +20,12 @@ export const DownloadProgressTick = (props): ReactElement => {
|
||||
%
|
||||
</progress>
|
||||
</div>
|
||||
<div className="is-size-5">
|
||||
{prettyBytes(props.data.speed)} per second.
|
||||
</div>
|
||||
<div className="is-size-5">
|
||||
<div className="is-size-6 mt-1 mb-2">
|
||||
<p>{prettyBytes(props.data.speed)} per second.</p>
|
||||
Time left:
|
||||
{Math.round(parseInt(props.data.seconds_left) / 60)}
|
||||
</div>
|
||||
|
||||
<div>{props.data.target}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,78 +2,137 @@ import React, { ReactElement } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import { isEmpty } from "lodash";
|
||||
import { format, parseISO } from "date-fns";
|
||||
|
||||
export const RawFileDetails = (props): ReactElement => {
|
||||
const { rawFileDetails, inferredMetadata } = props.data;
|
||||
const { rawFileDetails, inferredMetadata, created_at, updated_at } =
|
||||
props.data;
|
||||
const PaperClipIcon = () => <></>;
|
||||
return (
|
||||
<>
|
||||
<div className="comic-detail raw-file-details column is-three-fifths">
|
||||
<dl>
|
||||
<dt>Raw File Details</dt>
|
||||
<dd className="is-size-7">
|
||||
{rawFileDetails.containedIn +
|
||||
"/" +
|
||||
rawFileDetails.name +
|
||||
rawFileDetails.extension}
|
||||
</dd>
|
||||
<dd>
|
||||
<div className="field is-grouped mt-2">
|
||||
<div className="control">
|
||||
<div className="tags has-addons">
|
||||
<span className="tag">Size</span>
|
||||
<span className="tag is-info is-light">
|
||||
{prettyBytes(rawFileDetails.fileSize)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="control">
|
||||
<div className="tags has-addons">
|
||||
<span className="tag">Extension</span>
|
||||
<div className="max-w-2xl ml-5">
|
||||
<div className="px-4 sm:px-6">
|
||||
<p className="text-gray-500">
|
||||
<span className="text-xl">{rawFileDetails.name}</span>
|
||||
</p>
|
||||
</div>
|
||||
<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">
|
||||
<div className="sm:col-span-1">
|
||||
<dt className="text-sm font-medium text-gray-500">
|
||||
Raw File Details
|
||||
</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900">
|
||||
{rawFileDetails.containedIn +
|
||||
"/" +
|
||||
rawFileDetails.name +
|
||||
rawFileDetails.extension}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="sm:col-span-1">
|
||||
<dt className="text-sm font-medium text-gray-500">
|
||||
Inferred Issue Metadata
|
||||
</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900">
|
||||
Series Name: {inferredMetadata.issue.name}
|
||||
{!isEmpty(inferredMetadata.issue.number) ? (
|
||||
<span className="tag is-primary is-light">
|
||||
{rawFileDetails.extension}
|
||||
{inferredMetadata.issue.number}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="control">
|
||||
<div className="tags has-addons">
|
||||
<span className="tag">MIME type</span>
|
||||
<span className="tag is-primary is-light">
|
||||
) : null}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="sm:col-span-1">
|
||||
<dt className="text-sm font-medium text-gray-500">MIMEType</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900">
|
||||
{/* 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="pr-1 pt-1">
|
||||
<i className="icon-[solar--zip-file-bold-duotone] w-5 h-5"></i>
|
||||
</span>
|
||||
|
||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||
{rawFileDetails.mimeType}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</dd>
|
||||
<dd className="text-sm text-gray-900"></dd>
|
||||
</div>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div className="content comic-detail raw-file-details mt-3 column is-three-fifths">
|
||||
<dl>
|
||||
{/* inferred metadata */}
|
||||
<dt>Inferred Issue Metadata</dt>
|
||||
<dd>
|
||||
<div className="field is-grouped mt-2">
|
||||
<div className="control">
|
||||
<div className="tags has-addons">
|
||||
<span className="tag">Name</span>
|
||||
<span className="tag is-info is-light">
|
||||
{inferredMetadata.issue.name}
|
||||
<div className="sm:col-span-1">
|
||||
<dt className="text-sm font-medium text-gray-500">File Size</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900">
|
||||
{/* 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="pr-1 pt-1">
|
||||
<i className="icon-[solar--mirror-right-bold-duotone] w-5 h-5"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{!isEmpty(inferredMetadata.issue.number) ? (
|
||||
<div className="control">
|
||||
<div className="tags has-addons">
|
||||
<span className="tag">Number</span>
|
||||
<span className="tag is-primary is-light">
|
||||
{inferredMetadata.issue.number}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<span className="text-md text-slate-500 dark:text-slate-900">
|
||||
{prettyBytes(rawFileDetails.fileSize)}
|
||||
</span>
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
</dd>
|
||||
</dl>
|
||||
<div className="sm:col-span-2">
|
||||
<dt className="text-sm font-medium text-gray-500">
|
||||
Import Details
|
||||
</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900">
|
||||
{format(parseISO(created_at), "dd MMMM, yyyy")},{" "}
|
||||
{format(parseISO(created_at), "h aaaa")}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
<dt className="text-sm font-medium text-gray-500">Attachments</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900">
|
||||
<ul
|
||||
role="list"
|
||||
className="divide-y divide-gray-200 rounded-md border border-gray-200"
|
||||
>
|
||||
<li className="flex items-center justify-between py-3 pl-3 pr-4 text-sm">
|
||||
<div className="flex w-0 flex-1 items-center">
|
||||
<PaperClipIcon
|
||||
className="h-5 w-5 flex-shrink-0 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="ml-2 w-0 flex-1 truncate">
|
||||
resume_back_end_developer.pdf
|
||||
</span>
|
||||
</div>
|
||||
<div className="ml-4 flex-shrink-0">
|
||||
<a
|
||||
href="#"
|
||||
className="font-medium text-indigo-600 hover:text-indigo-500"
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex items-center justify-between py-3 pl-3 pr-4 text-sm">
|
||||
<div className="flex w-0 flex-1 items-center">
|
||||
{/* Read comic button */}
|
||||
<button
|
||||
className="button is-success is-light"
|
||||
onClick={() => {}}
|
||||
>
|
||||
<i className="fa-solid fa-book-open mr-2"></i>
|
||||
Read
|
||||
</button>
|
||||
</div>
|
||||
<div className="ml-4 flex-shrink-0">
|
||||
<a
|
||||
href="#"
|
||||
className="font-medium text-indigo-600 hover:text-indigo-500"
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
@@ -102,5 +161,7 @@ RawFileDetails.propTypes = {
|
||||
subtitle: PropTypes.string,
|
||||
}),
|
||||
}),
|
||||
created_at: PropTypes.string,
|
||||
updated_at: PropTypes.string,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -11,19 +11,20 @@ export const TabControls = (props): ReactElement => {
|
||||
setActive(filteredTabs[0].id);
|
||||
}, [acquisition]);
|
||||
|
||||
console.log(filteredTabs);
|
||||
return (
|
||||
<>
|
||||
<div className="tabs">
|
||||
<ul>
|
||||
{filteredTabs.map(({ id, name, icon }) => (
|
||||
<li
|
||||
key={id}
|
||||
className={id === active ? "is-active" : ""}
|
||||
onClick={() => setActive(id)}
|
||||
>
|
||||
{/* Downloads tab and count badge */}
|
||||
<a>
|
||||
<div className="hidden sm:block mt-7">
|
||||
<div className="border-b border-gray-200">
|
||||
<nav className="-mb-px flex gap-6" aria-label="Tabs">
|
||||
{filteredTabs.map(({ id, name, icon }) => (
|
||||
<a
|
||||
key={id}
|
||||
className="inline-flex shrink-0 items-center gap-2 border-b-2 border-transparent px-1 pb-4 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700"
|
||||
aria-current="page"
|
||||
onClick={() => setActive(id)}
|
||||
>
|
||||
{/* Downloads tab and count badge */}
|
||||
{/* <a>
|
||||
{id === 6 && !isNil(acquisition.directconnect) ? (
|
||||
<span className="download-icon-labels">
|
||||
<i className="fa-solid fa-download"></i>
|
||||
@@ -35,10 +36,12 @@ export const TabControls = (props): ReactElement => {
|
||||
<span className="icon is-small">{icon}</span>
|
||||
)}
|
||||
{name}
|
||||
</a> */}
|
||||
{name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
{filteredTabs.map(({ id, content }) => {
|
||||
return active === id ? content : null;
|
||||
|
||||
Reference in New Issue
Block a user