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:
2023-12-21 16:23:29 -05:00
committed by GitHub
parent 68442894d0
commit 4f49e538a8
35 changed files with 1405 additions and 12089 deletions

View File

@@ -7,6 +7,7 @@ import T2Table from "../shared/T2Table";
import ellipsize from "ellipsize";
import { useQuery, keepPreviousData } from "@tanstack/react-query";
import axios from "axios";
import { format, fromUnixTime, parseISO } from "date-fns";
/**
* Component that tabulates the contents of the user's ThreeTwo Library.
@@ -53,45 +54,42 @@ export const Library = (): ReactElement => {
const ComicInfoXML = (value) => {
return value.data ? (
<div className="comicvine-metadata mt-3">
<dl>
<span className="tags has-addons is-size-7">
<span className="tag">Series</span>
<span className="tag is-warning is-light">
{ellipsize(value.data.series[0], 25)}
<dl className="flex flex-col text-md p-3 ml-4 my-3 rounded-lg dark:bg-yellow-500 bg-yellow-300 w-max">
{/* Series Name */}
<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--bookmark-square-minimalistic-bold-duotone] w-5 h-5"></i>
</span>
<span className="text-md text-slate-900 dark:text-slate-900">
{ellipsize(value.data.series[0], 45)}
</span>
</span>
<div className="flex flex-row mt-2 gap-2">
{/* Pages */}
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
<span className="pr-1 pt-1">
<i className="icon-[solar--notebook-minimalistic-bold-duotone] w-5 h-5"></i>
</span>
<span className="text-md text-slate-900 dark:text-slate-900">
Pages: {value.data.pagecount[0]}
</span>
</span>
</dl>
<dl>
<div className="field is-grouped is-grouped-multiline">
<div className="control">
<span className="tags has-addons is-size-7 mt-2">
<span className="tag">Pages</span>
<span className="tag is-info is-light has-text-weight-bold">
{value.data.pagecount[0]}
</span>
</span>
</div>
<div className="control">
<span className="tags has-addons is-size-7 mt-2">
<span className="tag">Issue</span>
{!isNil(value.data.number) && (
<span className="tag has-text-weight-bold is-success is-light">
{parseInt(value.data.number[0], 10)}
</span>
)}
</span>
</div>
</div>
</dl>
</div>
{/* Issue number */}
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
<span className="pr-1 pt-1">
<i className="icon-[solar--hashtag-outline] w-3.5 h-3.5"></i>
</span>
<span className="text-slate-900 dark:text-slate-900">
{!isNil(value.data.number) && (
<span>{parseInt(value.data.number[0], 10)}</span>
)}
</span>
</span>
</div>
</dl>
) : null;
};
const WantedStatus = ({ value }) => {
return !value ? <span className="tag is-info is-light">Wanted</span> : null;
};
const columns = useMemo(
() => [
{
@@ -110,14 +108,10 @@ export const Library = (): ReactElement => {
{
header: "ComicInfo.xml",
accessorKey: "_source.sourcedMetadata.comicInfo",
align: "center",
minWidth: 250,
cell: (info) =>
!isEmpty(info.getValue()) ? (
<ComicInfoXML data={info.getValue()} />
) : (
<span className="tag mt-5">No ComicInfo.xml</span>
),
) : null,
},
],
},
@@ -125,28 +119,41 @@ export const Library = (): ReactElement => {
header: "Additional Metadata",
columns: [
{
header: "Publisher",
accessorKey: "_source.sourcedMetadata.comicvine.volumeInformation",
header: "Date of Import",
accessorKey: "_source.createdAt",
cell: (info) => {
return (
!isNil(info.getValue()) && (
<h6 className="is-size-7 has-text-weight-bold">
{info.getValue().publisher.name}
</h6>
)
);
return !isNil(info.getValue()) ? (
<div className="text-xs w-max ml-3 my-3 text-slate-600">
<p>{format(parseISO(info.getValue()), "dd MMMM, yyyy")} </p>
{format(parseISO(info.getValue()), "h aaaa")}
</div>
) : null;
},
},
{
header: "Something",
accessorKey: "_source.acquisition.source.wanted",
cell: (info) => {
!isUndefined(info.getValue()) ? (
<WantedStatus value={info.getValue().toString()} />
) : (
"Nothing"
);
},
header: "Downloads",
accessorKey: "_source.acquisition",
cell: (info) => (
<div className="flex flex-col gap-2 ml-3 my-3">
<span className="inline-flex items-center w-fit bg-slate-50 text-slate-800 text-xs px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
<span className="pr-1 pt-1">
<i className="icon-[solar--folder-path-connect-bold-duotone] w-5 h-5"></i>
</span>
<span className="text-md text-slate-900 dark:text-slate-900">
DC++: {info.getValue().directconnect.downloads.length}
</span>
</span>
<span className="inline-flex w-fit items-center bg-slate-50 text-slate-800 text-xs px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
<span className="pr-1 pt-1">
<i className="icon-[solar--magnet-bold-duotone] w-5 h-5"></i>
</span>
<span className="text-md text-slate-900 dark:text-slate-900">
Torrent: {info.getValue().torrent.downloads.length}
</span>
</span>
</div>
),
},
],
},
@@ -189,11 +196,23 @@ export const Library = (): ReactElement => {
// value: PropTypes.bool.isRequired,
// };
return (
<section className="container">
<div className="section">
<div className="header-area">
<h1 className="title">Library</h1>
</div>
<div>
<section>
<header className="bg-slate-200 dark:bg-slate-500">
<div className="mx-auto max-w-screen-xl px-2 py-2 sm:px-6 sm:py-8 lg:px-8 lg:py-4">
<div className="sm:flex sm:items-center sm:justify-between">
<div className="text-center sm:text-left">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white sm:text-3xl">
Library
</h1>
<p className="mt-1.5 text-sm text-gray-500 dark:text-white">
Browse your comic book collection.
</p>
</div>
</div>
</div>
</header>
{!isUndefined(searchResults?.hits) ? (
<div>
<div className="library">
@@ -210,29 +229,36 @@ export const Library = (): ReactElement => {
</div>
</div>
) : (
<div className="columns">
<div className="column is-two-thirds">
<article className="message is-link">
<div className="message-body">
<>
<article
role="alert"
className="rounded-lg max-w-screen-md border-s-4 border-yellow-500 bg-yellow-50 p-4 dark:border-s-4 dark:border-yellow-600 dark:bg-yellow-300 dark:text-slate-600"
>
<div>
<p>
No comics were found in the library, Elasticsearch reports no
indices. Try importing a few comics into the library and come
back.
</div>
</article>
{!isUndefined(searchResults?.data?.meta?.body) ? (
<pre>
{JSON.stringify(
searchResults.data.meta.body.error.root_cause,
null,
4,
)}
</pre>
) : null}
</p>
</div>
</article>
<div className="block max-w-md p-6 bg-white border border-gray-200 my-3 rounded-lg shadow dark:bg-slate-400 dark:border-gray-700">
<pre className="text-sm font-hasklig text-slate-700 dark:text-slate-700">
{!isUndefined(searchResults?.data?.meta?.body) ? (
<p>
{JSON.stringify(
searchResults.data.meta.body.error.root_cause,
null,
4,
)}
</p>
) : null}
</pre>
</div>
</div>
</>
)}
</div>
</section>
</section>
</div>
);
};

View File

@@ -25,36 +25,42 @@ export const SearchBar = (): ReactElement => {
// );
}, []);
return (
<div className="box">
<Form
onSubmit={handleSubmit}
initialValues={{}}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit}>
<div className="field is-grouped">
<div className="control search is-expanded">
<Field name="search">
{({ input, meta }) => {
return (
<input
{...input}
className="input main-search-bar is-medium"
placeholder="Type an issue/volume name"
/>
);
}}
</Field>
</div>
<div className="control">
<button className="button is-medium" type="submit">
Search
</button>
</div>
</div>
</form>
)}
/>
</div>
<Form
onSubmit={handleSubmit}
initialValues={{}}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit}>
<Field name="search">
{({ input, meta }) => {
return (
<div className="flex flex-row w-full">
<div className="flex flex-row bg-slate-300 dark:bg-slate-500 rounded-l-lg p-2 min-w-full">
<div className="w-10 text-gray-400">
<i className="icon-[solar--magnifer-bold-duotone] h-7 w-7" />
</div>
<input
{...input}
className="bg-slate-300 dark:bg-slate-500 outline-none text-lg text-gray-700 w-full"
type="text"
id="search"
placeholder="Type an issue/volume name"
/>
</div>
<button
className="sm:mt-0 rounded-r-lg border border-green-400 dark:border-green-200 bg-green-200 px-3 py-1 text-gray-500 hover:bg-transparent hover:text-green-600 focus:outline-none focus:ring active:text-indigo-500"
type="submit"
>
Search
</button>
</div>
);
}}
</Field>
</form>
)}
/>
);
};