mirror of
https://github.com/idanoo/autobrr
synced 2025-07-26 02:09:13 +00:00
enhancement(web): ui overhaul (#1155)
* Various WebUI changes and fixes. * feat(tooltip): make tooltip display upwards * fix(tooltip): place tooltip to the right * fix(web): add missing ml-px to SwitchGroup header current: https://i.imgur.com/2WXstPV.png new: https://i.imgur.com/QGQ49mP.png * fix(web): collapse sections * fix(web): improve freeleech section * fix(web): rename action to action_components Renamed the 'action' folder to 'action_components' to resolve import issues due to case sensitivity. * fix(web): align CollapsibleSection Old Advanced tab: https://i.imgur.com/MXaJ5eJ.png New Advanced tab: https://i.imgur.com/4nPJJRw.png Music tab for comparison: https://i.imgur.com/I59X7ot.png * fix(web): remove invalid CSS class * revert: vertical padding on switchgroup added py-0 on the freeleech part instead * feat(settings): add back log files * fix(settings): irc channels and font sizes * fix(components): radio select roundness * fix(styling): various minor changes * fix(filters): remove jitter fields --------- Co-authored-by: ze0s <43699394+zze0s@users.noreply.github.com> Co-authored-by: soup <soup@r4tio.dev> Co-authored-by: ze0s <ze0s@riseup.net>
This commit is contained in:
parent
a274d9ddce
commit
e842a7bd42
84 changed files with 4378 additions and 4361 deletions
|
@ -5,126 +5,55 @@
|
|||
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { toast } from "react-hot-toast";
|
||||
import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select";
|
||||
import { Link } from "react-router-dom";
|
||||
import Select from "react-select";
|
||||
|
||||
import { APIClient } from "@api/APIClient";
|
||||
import { GithubRelease } from "@app/types/Update";
|
||||
import Toast from "@components/notifications/Toast";
|
||||
import { LogLevelOptions, SelectOption } from "@domain/constants";
|
||||
|
||||
import { Section, RowItem } from "./_components";
|
||||
import * as common from "@components/inputs/common";
|
||||
import { LogFiles } from "@screens/Logs";
|
||||
|
||||
interface RowItemProps {
|
||||
label: string;
|
||||
value?: string;
|
||||
title?: string;
|
||||
emptyText?: string;
|
||||
newUpdate?: GithubRelease;
|
||||
}
|
||||
|
||||
const RowItem = ({ label, value, title, emptyText }: RowItemProps) => {
|
||||
return (
|
||||
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-4 sm:gap-4 sm:px-6">
|
||||
<dt className="font-medium text-gray-900 dark:text-white text-sm" title={title}>{label}</dt>
|
||||
<dd className="mt-1 text-gray-900 dark:text-white text-sm sm:mt-0 sm:col-span-2 break-all truncate">
|
||||
<span className="px-1.5 py-1 bg-gray-200 dark:bg-gray-700 rounded shadow">{value ? value : emptyText}</span>
|
||||
</dd>
|
||||
</div>
|
||||
);
|
||||
type SelectWrapperProps = {
|
||||
id: string;
|
||||
value: unknown;
|
||||
onChange: any;
|
||||
options: unknown[];
|
||||
};
|
||||
|
||||
interface RowItemNumberProps {
|
||||
label: string;
|
||||
value?: string | number;
|
||||
title?: string;
|
||||
unit?: string;
|
||||
}
|
||||
|
||||
const RowItemNumber = ({ label, value, title, unit }: RowItemNumberProps) => {
|
||||
return (
|
||||
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-4 sm:gap-4 sm:px-6">
|
||||
<dt className="font-medium text-gray-900 dark:text-white text-sm" title={title}>{label}</dt>
|
||||
<dd className="mt-1 text-gray-900 dark:text-white text-sm sm:mt-0 sm:col-span-2 break-all truncate">
|
||||
<span className="px-1.5 py-1 bg-gray-200 dark:bg-gray-700 rounded shadow truncate">{value}</span>
|
||||
{unit &&
|
||||
<span className="ml-1 text-sm text-gray-700 dark:text-gray-400">{unit}</span>
|
||||
}
|
||||
</dd>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Input = (props: InputProps) => {
|
||||
return (
|
||||
<components.Input
|
||||
{...props}
|
||||
inputClassName="outline-none border-none shadow-none focus:ring-transparent"
|
||||
className="text-gray-400 dark:text-gray-100"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Control = (props: ControlProps) => {
|
||||
return (
|
||||
<components.Control
|
||||
{...props}
|
||||
className="p-1 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:text-gray-100 sm:text-sm"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Menu = (props: MenuProps) => {
|
||||
return (
|
||||
<components.Menu
|
||||
{...props}
|
||||
className="dark:bg-gray-800 border border-gray-300 dark:border-gray-700 dark:text-gray-400 rounded-md shadow-sm"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Option = (props: OptionProps) => {
|
||||
return (
|
||||
<components.Option
|
||||
{...props}
|
||||
className="dark:text-gray-400 dark:bg-gray-800 dark:hover:bg-gray-900 dark:focus:bg-gray-900"
|
||||
children={props.children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const RowItemSelect = ({ id, title, label, value, options, onChange }: any) => {
|
||||
return (
|
||||
<div className="py-4 sm:py-5 sm:grid sm:grid-cols-4 sm:gap-4 sm:px-6">
|
||||
<dt className="font-medium text-gray-500 dark:text-white" title={title}>{label}:</dt>
|
||||
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2 break-all">
|
||||
<Select
|
||||
id={id}
|
||||
components={{ Input, Control, Menu, Option }}
|
||||
placeholder="Choose a type"
|
||||
styles={{
|
||||
singleValue: (base) => ({
|
||||
...base,
|
||||
color: "unset"
|
||||
})
|
||||
}}
|
||||
theme={(theme) => ({
|
||||
...theme,
|
||||
spacing: {
|
||||
...theme.spacing,
|
||||
controlHeight: 30,
|
||||
baseUnit: 2
|
||||
}
|
||||
})}
|
||||
value={value && options.find((o: any) => o.value == value)}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
/>
|
||||
</dd>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const SelectWrapper = ({ id, value, onChange, options }: SelectWrapperProps) => (
|
||||
<Select
|
||||
id={id}
|
||||
components={{
|
||||
Input: common.SelectInput,
|
||||
Control: common.SelectControl,
|
||||
Menu: common.SelectMenu,
|
||||
Option: common.SelectOption,
|
||||
IndicatorSeparator: common.IndicatorSeparator,
|
||||
DropdownIndicator: common.DropdownIndicator
|
||||
}}
|
||||
placeholder="Choose a type"
|
||||
styles={{
|
||||
singleValue: (base) => ({
|
||||
...base,
|
||||
color: "unset"
|
||||
})
|
||||
}}
|
||||
theme={(theme) => ({
|
||||
...theme,
|
||||
spacing: {
|
||||
...theme.spacing,
|
||||
controlHeight: 30,
|
||||
baseUnit: 2
|
||||
}
|
||||
})}
|
||||
value={value && options.find((o: any) => o.value == value)}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
/>
|
||||
);
|
||||
|
||||
function LogSettings() {
|
||||
const { isLoading, data } = useQuery({
|
||||
|
@ -140,58 +69,58 @@ function LogSettings() {
|
|||
const setLogLevelUpdateMutation = useMutation({
|
||||
mutationFn: (value: string) => APIClient.config.update({ log_level: value }),
|
||||
onSuccess: () => {
|
||||
toast.custom((t) => <Toast type="success" body={"Config successfully updated!"} t={t}/>);
|
||||
toast.custom((t) => <Toast type="success" body={"Config successfully updated!"} t={t} />);
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: ["config"] });
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700 lg:col-span-9">
|
||||
<div className="py-6 px-4 sm:p-6 lg:pb-8">
|
||||
<div>
|
||||
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Logs</h2>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Set level, size etc.
|
||||
</p>
|
||||
</div>
|
||||
<Section
|
||||
title="Logs"
|
||||
description={
|
||||
<>
|
||||
Configure log level, log size rotation, etc. You can download your old log files
|
||||
{" "}
|
||||
<Link
|
||||
to="/logs"
|
||||
className="text-gray-700 dark:text-gray-200 underline font-semibold underline-offset-2 decoration-blue-500 decoration hover:text-black hover:dark:text-gray-100"
|
||||
>
|
||||
on the Logs page
|
||||
</Link>.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="-mx-4 lg:col-span-9">
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-750">
|
||||
{!isLoading && data && (
|
||||
<form className="divide-y divide-gray-200 dark:divide-gray-750" action="#" method="POST">
|
||||
<RowItem label="Path" value={data?.log_path} title="Set in config.toml" emptyText="Not set!"/>
|
||||
<RowItem
|
||||
className="sm:col-span-1"
|
||||
label="Level"
|
||||
title="Log level"
|
||||
value={
|
||||
<SelectWrapper
|
||||
id="log_level"
|
||||
value={data?.log_level}
|
||||
options={LogLevelOptions}
|
||||
onChange={(value: SelectOption) => setLogLevelUpdateMutation.mutate(value.value)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<RowItem label="Max Size" value={data?.log_max_size} title="Set in config.toml" rightSide="MB"/>
|
||||
<RowItem label="Max Backups" value={data?.log_max_backups} title="Set in config.toml"/>
|
||||
</form>
|
||||
)}
|
||||
|
||||
<div className="px-6 pt-4">
|
||||
<LogFiles/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="px-4 py-5 sm:p-0">
|
||||
<form className="divide-y divide-gray-200 dark:divide-gray-700 lg:col-span-9" action="#" method="POST">
|
||||
{!isLoading && data && (
|
||||
<dl className="sm:divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<RowItem label="Path" value={data?.log_path} title="Set in config.toml" emptyText="Not set!" />
|
||||
<RowItemSelect id="log_level" label="Level" value={data?.log_level} title="Log level" options={LogLevelOptions} onChange={(value: SelectOption) => setLogLevelUpdateMutation.mutate(value.value)} />
|
||||
<RowItemNumber label="Max Size" value={data?.log_max_size} title="Set in config.toml" unit="MB" />
|
||||
<RowItemNumber label="Max Backups" value={data?.log_max_backups} title="Set in config.toml" />
|
||||
</dl>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex flex-col py-4 px-4 sm:px-6">
|
||||
<LogFiles />
|
||||
</div>
|
||||
|
||||
{/*<div className="mt-4 flex justify-end py-4 px-4 sm:px-6">*/}
|
||||
{/* <button*/}
|
||||
{/* type="button"*/}
|
||||
{/* className="inline-flex justify-center rounded-md border border-gray-300 bg-white py-2 px-4 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"*/}
|
||||
{/* >*/}
|
||||
{/* Cancel*/}
|
||||
{/* </button>*/}
|
||||
{/* <button*/}
|
||||
{/* type="submit"*/}
|
||||
{/* className="ml-5 inline-flex justify-center rounded-md border border-transparent bg-blue-700 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-blue-800 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"*/}
|
||||
{/* >*/}
|
||||
{/* Save*/}
|
||||
{/* </button>*/}
|
||||
{/*</div>*/}
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue