mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 08:49:13 +00:00
fix(web): mobile ux improvements (#296)
* fix(ListboxFilter): Added z-index in order to properly render dropdown box. fix(ReleaseTable): Added flex-direction: column to wrap on mobile devices (otherwise it's set to row) feat(DownloadClientSettingsListItem): Made it possible to toggle client state directly, without opening the update form. * fix(TitleCell): Improved responsiveness across all relevant screen selectors. * fix(FilterDetails): Fixed incorrect overflow property ordering.
This commit is contained in:
parent
bb3ea6ff18
commit
4677057bee
5 changed files with 105 additions and 84 deletions
|
@ -17,7 +17,7 @@ export const AgeCell = ({ value }: CellProps) => (
|
||||||
|
|
||||||
export const TitleCell = ({ value }: CellProps) => (
|
export const TitleCell = ({ value }: CellProps) => (
|
||||||
<div
|
<div
|
||||||
className="text-sm font-medium box-content text-gray-900 dark:text-gray-300 max-w-[128px] sm:max-w-none overflow-auto py-4"
|
className="text-sm font-medium box-content text-gray-900 dark:text-gray-300 max-w-[128px] sm:max-w-[256px] md:max-w-[360px] lg:max-w-[640px] xl:max-w-[840px] overflow-auto py-4"
|
||||||
title={value}
|
title={value}
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
|
|
|
@ -223,84 +223,82 @@ export default function FilterDetails() {
|
||||||
</header>
|
</header>
|
||||||
<div className="max-w-screen-xl mx-auto pb-12 px-4 sm:px-6 lg:px-8">
|
<div className="max-w-screen-xl mx-auto pb-12 px-4 sm:px-6 lg:px-8">
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow">
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow">
|
||||||
<div className="relative mx-auto md:px-6 xl:px-4">
|
<div className="pt-1 px-4 pb-6 block">
|
||||||
<div className="pt-1 pb-6 block overflow-auto">
|
<div className="border-b border-gray-200 dark:border-gray-700">
|
||||||
<div className="border-b border-gray-200 dark:border-gray-700">
|
<nav className="-mb-px flex space-x-6 sm:space-x-8 overflow-x-auto">
|
||||||
<nav className="-mb-px flex space-x-6 sm:space-x-8">
|
{tabs.map((tab) => (
|
||||||
{tabs.map((tab) => (
|
<TabNavLink item={tab} key={tab.href} />
|
||||||
<TabNavLink item={tab} key={tab.href} />
|
))}
|
||||||
))}
|
</nav>
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Formik
|
|
||||||
initialValues={{
|
|
||||||
id: filter.id,
|
|
||||||
name: filter.name,
|
|
||||||
enabled: filter.enabled || false,
|
|
||||||
min_size: filter.min_size,
|
|
||||||
max_size: filter.max_size,
|
|
||||||
delay: filter.delay,
|
|
||||||
priority: filter.priority,
|
|
||||||
max_downloads: filter.max_downloads,
|
|
||||||
max_downloads_unit: filter.max_downloads_unit,
|
|
||||||
use_regex: filter.use_regex || false,
|
|
||||||
shows: filter.shows,
|
|
||||||
years: filter.years,
|
|
||||||
resolutions: filter.resolutions || [],
|
|
||||||
sources: filter.sources || [],
|
|
||||||
codecs: filter.codecs || [],
|
|
||||||
containers: filter.containers || [],
|
|
||||||
match_hdr: filter.match_hdr || [],
|
|
||||||
except_hdr: filter.except_hdr || [],
|
|
||||||
match_other: filter.match_other || [],
|
|
||||||
except_other: filter.except_other || [],
|
|
||||||
seasons: filter.seasons,
|
|
||||||
episodes: filter.episodes,
|
|
||||||
match_releases: filter.match_releases,
|
|
||||||
except_releases: filter.except_releases,
|
|
||||||
match_release_groups: filter.match_release_groups,
|
|
||||||
except_release_groups: filter.except_release_groups,
|
|
||||||
match_categories: filter.match_categories,
|
|
||||||
except_categories: filter.except_categories,
|
|
||||||
tags: filter.tags,
|
|
||||||
except_tags: filter.except_tags,
|
|
||||||
match_uploaders: filter.match_uploaders,
|
|
||||||
except_uploaders: filter.except_uploaders,
|
|
||||||
freeleech: filter.freeleech,
|
|
||||||
freeleech_percent: filter.freeleech_percent,
|
|
||||||
formats: filter.formats || [],
|
|
||||||
quality: filter.quality || [],
|
|
||||||
media: filter.media || [],
|
|
||||||
match_release_types: filter.match_release_types || [],
|
|
||||||
log_score: filter.log_score,
|
|
||||||
log: filter.log,
|
|
||||||
cue: filter.cue,
|
|
||||||
perfect_flac: filter.perfect_flac,
|
|
||||||
artists: filter.artists,
|
|
||||||
albums: filter.albums,
|
|
||||||
origins: filter.origins || [],
|
|
||||||
indexers: filter.indexers || [],
|
|
||||||
actions: filter.actions || []
|
|
||||||
} as Filter}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
>
|
|
||||||
{({ values, dirty, resetForm }) => (
|
|
||||||
<Form>
|
|
||||||
<Routes>
|
|
||||||
<Route index element={<General />} />
|
|
||||||
<Route path="movies-tv" element={<MoviesTv />} />
|
|
||||||
<Route path="music" element={<Music />} />
|
|
||||||
<Route path="advanced" element={<Advanced />} />
|
|
||||||
<Route path="actions" element={<FilterActions filter={filter} values={values} />}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
<FormButtonsGroup values={values} deleteAction={deleteAction} dirty={dirty} reset={resetForm} />
|
|
||||||
<DEBUG values={values} />
|
|
||||||
</Form>
|
|
||||||
)}
|
|
||||||
</Formik>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
id: filter.id,
|
||||||
|
name: filter.name,
|
||||||
|
enabled: filter.enabled || false,
|
||||||
|
min_size: filter.min_size,
|
||||||
|
max_size: filter.max_size,
|
||||||
|
delay: filter.delay,
|
||||||
|
priority: filter.priority,
|
||||||
|
max_downloads: filter.max_downloads,
|
||||||
|
max_downloads_unit: filter.max_downloads_unit,
|
||||||
|
use_regex: filter.use_regex || false,
|
||||||
|
shows: filter.shows,
|
||||||
|
years: filter.years,
|
||||||
|
resolutions: filter.resolutions || [],
|
||||||
|
sources: filter.sources || [],
|
||||||
|
codecs: filter.codecs || [],
|
||||||
|
containers: filter.containers || [],
|
||||||
|
match_hdr: filter.match_hdr || [],
|
||||||
|
except_hdr: filter.except_hdr || [],
|
||||||
|
match_other: filter.match_other || [],
|
||||||
|
except_other: filter.except_other || [],
|
||||||
|
seasons: filter.seasons,
|
||||||
|
episodes: filter.episodes,
|
||||||
|
match_releases: filter.match_releases,
|
||||||
|
except_releases: filter.except_releases,
|
||||||
|
match_release_groups: filter.match_release_groups,
|
||||||
|
except_release_groups: filter.except_release_groups,
|
||||||
|
match_categories: filter.match_categories,
|
||||||
|
except_categories: filter.except_categories,
|
||||||
|
tags: filter.tags,
|
||||||
|
except_tags: filter.except_tags,
|
||||||
|
match_uploaders: filter.match_uploaders,
|
||||||
|
except_uploaders: filter.except_uploaders,
|
||||||
|
freeleech: filter.freeleech,
|
||||||
|
freeleech_percent: filter.freeleech_percent,
|
||||||
|
formats: filter.formats || [],
|
||||||
|
quality: filter.quality || [],
|
||||||
|
media: filter.media || [],
|
||||||
|
match_release_types: filter.match_release_types || [],
|
||||||
|
log_score: filter.log_score,
|
||||||
|
log: filter.log,
|
||||||
|
cue: filter.cue,
|
||||||
|
perfect_flac: filter.perfect_flac,
|
||||||
|
artists: filter.artists,
|
||||||
|
albums: filter.albums,
|
||||||
|
origins: filter.origins || [],
|
||||||
|
indexers: filter.indexers || [],
|
||||||
|
actions: filter.actions || []
|
||||||
|
} as Filter}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
{({ values, dirty, resetForm }) => (
|
||||||
|
<Form>
|
||||||
|
<Routes>
|
||||||
|
<Route index element={<General />} />
|
||||||
|
<Route path="movies-tv" element={<MoviesTv />} />
|
||||||
|
<Route path="music" element={<Music />} />
|
||||||
|
<Route path="advanced" element={<Advanced />} />
|
||||||
|
<Route path="actions" element={<FilterActions filter={filter} values={values} />}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
|
<FormButtonsGroup values={values} deleteAction={deleteAction} dirty={dirty} reset={resetForm} />
|
||||||
|
<DEBUG values={values} />
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -49,7 +49,7 @@ const ListboxFilter = ({
|
||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<Listbox.Options
|
<Listbox.Options
|
||||||
className="absolute w-full mt-1 overflow-auto text-base bg-white dark:bg-gray-800 rounded-md shadow-lg max-h-60 border border-opacity-5 border-black dark:border-gray-700 dark:border-opacity-40 focus:outline-none sm:text-sm"
|
className="absolute z-10 w-full mt-1 overflow-auto text-base bg-white dark:bg-gray-800 rounded-md shadow-lg max-h-60 border border-opacity-5 border-black dark:border-gray-700 dark:border-opacity-40 focus:outline-none sm:text-sm"
|
||||||
>
|
>
|
||||||
<FilterOption label="All" />
|
<FilterOption label="All" />
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -181,7 +181,7 @@ export const ReleaseTable = () => {
|
||||||
// Render the UI for your table
|
// Render the UI for your table
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex mb-6">
|
<div className="flex mb-6 flex-col sm:flex-row">
|
||||||
{headerGroups.map((headerGroup) =>
|
{headerGroups.map((headerGroup) =>
|
||||||
headerGroup.headers.map((column) => (
|
headerGroup.headers.map((column) => (
|
||||||
column.Filter ? (
|
column.Filter ? (
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { useToggle } from "../../hooks/hooks";
|
import { useToggle } from "../../hooks/hooks";
|
||||||
import { Switch } from "@headlessui/react";
|
import { Switch } from "@headlessui/react";
|
||||||
import { useQuery } from "react-query";
|
import { useMutation, useQuery, useQueryClient } from "react-query";
|
||||||
import { classNames } from "../../utils";
|
import { classNames } from "../../utils";
|
||||||
import { DownloadClientAddForm, DownloadClientUpdateForm } from "../../forms";
|
import { DownloadClientAddForm, DownloadClientUpdateForm } from "../../forms";
|
||||||
import { EmptySimple } from "../../components/emptystates";
|
import { EmptySimple } from "../../components/emptystates";
|
||||||
import { APIClient } from "../../api/APIClient";
|
import { APIClient } from "../../api/APIClient";
|
||||||
import { DownloadClientTypeNameMap } from "../../domain/constants";
|
import { DownloadClientTypeNameMap } from "../../domain/constants";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
import Toast from "../../components/notifications/Toast";
|
||||||
|
|
||||||
interface DLSettingsItemProps {
|
interface DLSettingsItemProps {
|
||||||
client: DownloadClient;
|
client: DownloadClient;
|
||||||
|
@ -15,14 +17,35 @@ interface DLSettingsItemProps {
|
||||||
function DownloadClientSettingsListItem({ client, idx }: DLSettingsItemProps) {
|
function DownloadClientSettingsListItem({ client, idx }: DLSettingsItemProps) {
|
||||||
const [updateClientIsOpen, toggleUpdateClient] = useToggle(false);
|
const [updateClientIsOpen, toggleUpdateClient] = useToggle(false);
|
||||||
|
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const mutation = useMutation(
|
||||||
|
(client: DownloadClient) => APIClient.download_clients.update(client),
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries(["downloadClients"]);
|
||||||
|
toast.custom((t) => <Toast type="success" body={`${client.name} was updated successfully`} t={t}/>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const onToggleMutation = (newState: boolean) => {
|
||||||
|
mutation.mutate({
|
||||||
|
...client,
|
||||||
|
enabled: newState
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={client.name} className={idx % 2 === 0 ? "light:bg-white" : "light:bg-gray-50"}>
|
<tr key={client.name} className={idx % 2 === 0 ? "light:bg-white" : "light:bg-gray-50"}>
|
||||||
<DownloadClientUpdateForm client={client} isOpen={updateClientIsOpen} toggle={toggleUpdateClient} />
|
<DownloadClientUpdateForm
|
||||||
|
client={client}
|
||||||
|
isOpen={updateClientIsOpen}
|
||||||
|
toggle={toggleUpdateClient}
|
||||||
|
/>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
<Switch
|
<Switch
|
||||||
checked={client.enabled}
|
checked={client.enabled}
|
||||||
onChange={toggleUpdateClient}
|
onChange={onToggleMutation}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
client.enabled ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-600",
|
client.enabled ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-600",
|
||||||
"relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
"relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue