feat(web): Add helper tooltips for inputs and link them to docs (#663)

* Fixed button border in settings/download-clients

Button border for "Add new" is now uniform with other "Add new"-buttons

* enhancement(docs) add helper tooltips

-  Add helper tooltips for inputs and link them to docs #161

* fix build error

* tooltips: changed positition

* hide tooltip below 640 width

* made tooltips better

now attaching to TextField names

* Added icon variation for MultiSelect and CheckboxField

* cleaned up old code

* refactor

Co-authored-by: ze0s <zze0s@users.noreply.github.com>

* added tooltips for DownloadClientForms

* added tooltips for indexerforms

* div for passwordfieldwide

* tooltips for details.tsx

* added tooltips to actions.tsx

* added tooltips to indexerforms.tsx

* replaced info icon with a more rudimentary one

* linting, removed duplicate tailwind display properties

* remove margin for flex centering

* fixed tooltip alignment on all fields

* add tooltip to PwFieldWide in indexer edit window

* refactor: simplify tooltips

* refactor: scope tooltip css

* refactor: tooltip default clickable

---------

Co-authored-by: martylukyy <35452459+martylukyy@users.noreply.github.com>
Co-authored-by: ze0s <43699394+zze0s@users.noreply.github.com>
This commit is contained in:
soup 2023-02-03 17:03:49 +01:00 committed by GitHub
parent a50332394c
commit 3fdd7cf5e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 256 additions and 90 deletions

View file

@ -27,6 +27,7 @@
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"react-select": "^5.3.2", "react-select": "^5.3.2",
"react-table": "^7.8.0", "react-table": "^7.8.0",
"react-tooltip": "^5.5.2",
"stacktracey": "^2.1.8" "stacktracey": "^2.1.8"
}, },
"scripts": { "scripts": {

View file

@ -1,5 +1,6 @@
import { Field, FieldProps } from "formik"; import { Field, FieldProps } from "formik";
import { classNames } from "../../utils"; import { classNames } from "../../utils";
import { CustomTooltip } from "../tooltips/CustomTooltip";
interface ErrorFieldProps { interface ErrorFieldProps {
name: string; name: string;
@ -16,17 +17,29 @@ const ErrorField = ({ name, classNames }: ErrorFieldProps) => (
</div> </div>
); );
interface RequiredFieldProps {
required?: boolean
}
const RequiredField = ({ required }: RequiredFieldProps) => (
<>
{required && <span className="ml-1 text-red-500">*</span>}
</>
);
interface CheckboxFieldProps { interface CheckboxFieldProps {
name: string; name: string;
label: string; label: string;
sublabel?: string; sublabel?: string;
disabled?: boolean; disabled?: boolean;
tooltip?: JSX.Element;
} }
const CheckboxField = ({ const CheckboxField = ({
name, name,
label, label,
sublabel, sublabel,
tooltip,
disabled disabled
}: CheckboxFieldProps) => ( }: CheckboxFieldProps) => (
<div className="relative flex items-start"> <div className="relative flex items-start">
@ -43,12 +56,17 @@ const CheckboxField = ({
/> />
</div> </div>
<div className="ml-3 text-sm"> <div className="ml-3 text-sm">
<label htmlFor={name} className="font-medium text-gray-900 dark:text-gray-100"> <label htmlFor={name} className="flex mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label} <div className="flex">
{label}
{tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</label> </label>
<p className="text-gray-500">{sublabel}</p> <p className="text-gray-500">{sublabel}</p>
</div> </div>
</div> </div>
); );
export { ErrorField, CheckboxField }; export { ErrorField, RequiredField, CheckboxField };

View file

@ -2,6 +2,7 @@ import { Field, FieldProps } from "formik";
import { classNames } from "../../utils"; import { classNames } from "../../utils";
import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/solid"; import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/solid";
import { useToggle } from "../../hooks/hooks"; import { useToggle } from "../../hooks/hooks";
import { CustomTooltip } from "../tooltips/CustomTooltip";
type COL_WIDTHS = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; type COL_WIDTHS = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
@ -14,6 +15,7 @@ interface TextFieldProps {
autoComplete?: string; autoComplete?: string;
hidden?: boolean; hidden?: boolean;
disabled?: boolean; disabled?: boolean;
tooltip?: JSX.Element;
} }
export const TextField = ({ export const TextField = ({
@ -24,6 +26,7 @@ export const TextField = ({
columns, columns,
autoComplete, autoComplete,
hidden, hidden,
tooltip,
disabled disabled
}: TextFieldProps) => ( }: TextFieldProps) => (
<div <div
@ -33,8 +36,13 @@ export const TextField = ({
)} )}
> >
{label && ( {label && (
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide"> <label htmlFor={name} className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label} <div className="flex">
{label}
{tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</label> </label>
)} )}
<Field name={name}> <Field name={name}>
@ -45,7 +53,7 @@ export const TextField = ({
<div> <div>
<input <input
{...field} {...field}
id={name} name={name}
type="text" type="text"
defaultValue={defaultValue} defaultValue={defaultValue}
autoComplete={autoComplete} autoComplete={autoComplete}
@ -207,6 +215,7 @@ interface NumberFieldProps {
required?: boolean; required?: boolean;
min?: number; min?: number;
max?: number; max?: number;
tooltip?: JSX.Element;
} }
export const NumberField = ({ export const NumberField = ({
@ -216,12 +225,18 @@ export const NumberField = ({
step, step,
min, min,
max, max,
tooltip,
disabled, disabled,
required required
}: NumberFieldProps) => ( }: NumberFieldProps) => (
<div className="col-span-12 sm:col-span-6"> <div className="col-span-12 sm:col-span-6">
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide"> <label htmlFor={name} className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label} <div className="flex">
{label}
{tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</label> </label>
<Field name={name} type="number"> <Field name={name} type="number">

View file

@ -4,9 +4,10 @@ import { classNames } from "../../utils";
import { useToggle } from "../../hooks/hooks"; import { useToggle } from "../../hooks/hooks";
import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/solid"; import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/solid";
import { Switch } from "@headlessui/react"; import { Switch } from "@headlessui/react";
import { ErrorField } from "./common"; import { ErrorField, RequiredField } from "./common";
import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select"; import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select";
import { SelectFieldProps } from "./select"; import { SelectFieldProps } from "./select";
import { CustomTooltip } from "../tooltips/CustomTooltip";
interface TextFieldWideProps { interface TextFieldWideProps {
name: string; name: string;
@ -16,6 +17,7 @@ interface TextFieldWideProps {
defaultValue?: string; defaultValue?: string;
required?: boolean; required?: boolean;
hidden?: boolean; hidden?: boolean;
tooltip?: JSX.Element;
validate?: FieldValidator; validate?: FieldValidator;
} }
@ -26,13 +28,17 @@ export const TextFieldWide = ({
placeholder, placeholder,
defaultValue, defaultValue,
required, required,
tooltip,
hidden, hidden,
validate validate
}: TextFieldWideProps) => ( }: TextFieldWideProps) => (
<div hidden={hidden} className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4"> <div hidden={hidden} className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
<div> <div>
<label htmlFor={name} className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2"> <label htmlFor={name} className="flex text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
{label} {required && <span className="text-gray-500">*</span>} <div className="flex">
{label} {tooltip && (<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>)}
<RequiredField required={required} />
</div>
</label> </label>
</div> </div>
<div className="sm:col-span-2"> <div className="sm:col-span-2">
@ -71,6 +77,7 @@ interface PasswordFieldWideProps {
help?: string; help?: string;
required?: boolean; required?: boolean;
defaultVisible?: boolean; defaultVisible?: boolean;
tooltip?: JSX.Element;
validate?: FieldValidator; validate?: FieldValidator;
} }
@ -82,6 +89,7 @@ export const PasswordFieldWide = ({
help, help,
required, required,
defaultVisible, defaultVisible,
tooltip,
validate validate
}: PasswordFieldWideProps) => { }: PasswordFieldWideProps) => {
const [isVisible, toggleVisibility] = useToggle(defaultVisible); const [isVisible, toggleVisibility] = useToggle(defaultVisible);
@ -89,8 +97,11 @@ export const PasswordFieldWide = ({
return ( return (
<div className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4"> <div className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
<div> <div>
<label htmlFor={name} className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2"> <label htmlFor={name} className="flex text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
{label} {required && <span className="text-gray-500">*</span>} <div className="flex">
{label} {tooltip && (<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>)}
<RequiredField required={required} />
</div>
</label> </label>
</div> </div>
<div className="sm:col-span-2"> <div className="sm:col-span-2">
@ -132,6 +143,7 @@ interface NumberFieldWideProps {
placeholder?: string; placeholder?: string;
defaultValue?: number; defaultValue?: number;
required?: boolean; required?: boolean;
tooltip?: JSX.Element;
} }
export const NumberFieldWide = ({ export const NumberFieldWide = ({
@ -140,6 +152,7 @@ export const NumberFieldWide = ({
placeholder, placeholder,
help, help,
defaultValue, defaultValue,
tooltip,
required required
}: NumberFieldWideProps) => ( }: NumberFieldWideProps) => (
<div className="px-4 space-y-1 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-4"> <div className="px-4 space-y-1 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-4">
@ -148,7 +161,10 @@ export const NumberFieldWide = ({
htmlFor={name} htmlFor={name}
className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2" className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2"
> >
{label} {required && <span className="text-gray-500">*</span>} <div className="flex">
{label} {tooltip && (<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>)}
<RequiredField required={required} />
</div>
</label> </label>
</div> </div>
<div className="sm:col-span-2"> <div className="sm:col-span-2">
@ -187,12 +203,14 @@ interface SwitchGroupWideProps {
description?: string; description?: string;
defaultValue?: boolean; defaultValue?: boolean;
className?: string; className?: string;
tooltip?: JSX.Element;
} }
export const SwitchGroupWide = ({ export const SwitchGroupWide = ({
name, name,
label, label,
description, description,
tooltip,
defaultValue defaultValue
}: SwitchGroupWideProps) => ( }: SwitchGroupWideProps) => (
<ul className="mt-2 px-4 divide-y divide-gray-200 dark:divide-gray-700"> <ul className="mt-2 px-4 divide-y divide-gray-200 dark:divide-gray-700">
@ -200,7 +218,9 @@ export const SwitchGroupWide = ({
<div className="flex flex-col"> <div className="flex flex-col">
<Switch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-white" <Switch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-white"
passive> passive>
{label} <div className="flex">
{label} {tooltip && (<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>)}
</div>
</Switch.Label> </Switch.Label>
{description && ( {description && (
<Switch.Description className="text-sm text-gray-500 dark:text-gray-700"> <Switch.Description className="text-sm text-gray-500 dark:text-gray-700">
@ -350,15 +370,19 @@ export const SelectFieldWide = ({
name, name,
label, label,
optionDefaultText, optionDefaultText,
tooltip,
options options
}: SelectFieldProps) => ( }: SelectFieldProps) => (
<div className="flex items-center justify-between space-y-1 px-4 py-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4"> <div className="flex items-center justify-between space-y-1 px-4 py-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
<div> <div>
<label <label
htmlFor={name} htmlFor={name}
className="block text-sm font-medium text-gray-900 dark:text-white" className="flex text-sm font-medium text-gray-900 dark:text-white"
> >
{label} <div className="flex">
{label}
{tooltip && (<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>)}
</div>
</label> </label>
</div> </div>
<div className="sm:col-span-2"> <div className="sm:col-span-2">

View file

@ -1,4 +1,4 @@
import { Fragment } from "react"; import React, { Fragment } from "react";
import { Field, FieldProps } from "formik"; import { Field, FieldProps } from "formik";
import { Listbox, Transition } from "@headlessui/react"; import { Listbox, Transition } from "@headlessui/react";
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/24/solid"; import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/24/solid";
@ -6,6 +6,7 @@ import { MultiSelect as RMSC } from "react-multi-select-component";
import { classNames, COL_WIDTHS } from "../../utils"; import { classNames, COL_WIDTHS } from "../../utils";
import { SettingsContext } from "../../utils/Context"; import { SettingsContext } from "../../utils/Context";
import { CustomTooltip } from "../tooltips/CustomTooltip";
export interface MultiSelectOption { export interface MultiSelectOption {
value: string | number; value: string | number;
@ -21,6 +22,7 @@ interface MultiSelectProps {
columns?: COL_WIDTHS; columns?: COL_WIDTHS;
creatable?: boolean; creatable?: boolean;
disabled?: boolean; disabled?: boolean;
tooltip?: JSX.Element;
} }
export const MultiSelect = ({ export const MultiSelect = ({
@ -29,6 +31,7 @@ export const MultiSelect = ({
options, options,
columns, columns,
creatable, creatable,
tooltip,
disabled disabled
}: MultiSelectProps) => { }: MultiSelectProps) => {
const settingsContext = SettingsContext.useValue(); const settingsContext = SettingsContext.useValue();
@ -46,10 +49,13 @@ export const MultiSelect = ({
)} )}
> >
<label <label
className="block mb-2 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-200" htmlFor={label} className="flex mb-2 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-200">
htmlFor={label} <div className="flex">
> {label}
{label} {tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</label> </label>
<Field name={name} type="select" multiple={true}> <Field name={name} type="select" multiple={true}>
@ -155,7 +161,7 @@ export function DownloadClientSelect({
{({ open }) => ( {({ open }) => (
<> <>
<Listbox.Label className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide"> <Listbox.Label className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
Client Client
</Listbox.Label> </Listbox.Label>
<div className="mt-2 relative"> <div className="mt-2 relative">
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm"> <Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
@ -244,11 +250,13 @@ export interface SelectFieldProps {
label: string; label: string;
optionDefaultText: string; optionDefaultText: string;
options: SelectFieldOption[]; options: SelectFieldOption[];
tooltip?: JSX.Element;
} }
export const Select = ({ export const Select = ({
name, name,
label, label,
tooltip,
optionDefaultText, optionDefaultText,
options options
}: SelectFieldProps) => { }: SelectFieldProps) => {
@ -265,8 +273,13 @@ export const Select = ({
> >
{({ open }) => ( {({ open }) => (
<> <>
<Listbox.Label className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide"> <Listbox.Label className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label} <div className="flex">
{label}
{tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</Listbox.Label> </Listbox.Label>
<div className="mt-2 relative"> <div className="mt-2 relative">
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2.5 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm"> <Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2.5 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">

View file

@ -3,6 +3,7 @@ import type { FieldInputProps, FieldMetaProps, FieldProps, FormikProps, FormikVa
import { Field } from "formik"; import { Field } from "formik";
import { Switch as HeadlessSwitch } from "@headlessui/react"; import { Switch as HeadlessSwitch } from "@headlessui/react";
import { classNames } from "../../utils"; import { classNames } from "../../utils";
import { CustomTooltip } from "../tooltips/CustomTooltip";
type SwitchProps<V = unknown> = { type SwitchProps<V = unknown> = {
label?: string label?: string
@ -69,19 +70,26 @@ interface SwitchGroupProps {
description?: string; description?: string;
className?: string; className?: string;
heading?: boolean; heading?: boolean;
tooltip?: JSX.Element;
} }
const SwitchGroup = ({ const SwitchGroup = ({
name, name,
label, label,
description, description,
tooltip,
heading heading
}: SwitchGroupProps) => ( }: SwitchGroupProps) => (
<HeadlessSwitch.Group as="ol" className="py-4 flex items-center justify-between"> <HeadlessSwitch.Group as="ol" className="py-4 flex items-center justify-between">
{label && <div className="flex flex-col"> {label && <div className="flex flex-col">
<HeadlessSwitch.Label as={heading ? "h2" : "p"} className={classNames("font-medium text-gray-900 dark:text-gray-100", heading ? "text-lg" : "text-sm")} <HeadlessSwitch.Label as={heading ? "h2" : "p"} className={classNames("flex float-left cursor-default mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide", heading ? "text-lg" : "text-sm")}
passive> passive>
{label} <div className="flex">
{label}
{tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</HeadlessSwitch.Label> </HeadlessSwitch.Label>
{description && ( {description && (
<HeadlessSwitch.Description className="text-sm mt-1 text-gray-500 dark:text-gray-400"> <HeadlessSwitch.Description className="text-sm mt-1 text-gray-500 dark:text-gray-400">

View file

@ -0,0 +1,11 @@
.react-tooltip code {
background-color: rgb(63, 71, 94);
padding-left: 4px;
padding-right: 4px;
padding-top: 2px;
padding-bottom: 2px;
}
.react-tooltip a:hover {
text-decoration-line: underline;
}

View file

@ -0,0 +1,27 @@
import { PlacesType, Tooltip } from "react-tooltip";
import "./CustomTooltip.css";
interface CustomTooltipProps {
anchorId: string;
children?: React.ReactNode;
clickable?: boolean;
place?: PlacesType;
}
export const CustomTooltip = ({
anchorId,
children,
clickable = true,
place = "top"
}: CustomTooltipProps) => {
const id = `${anchorId}-tooltip`;
return (
<div className="flex items-center">
<svg id={id} className="ml-1 w-4 h-4" viewBox="0 0 72 72"><path d="M32 2C15.432 2 2 15.432 2 32s13.432 30 30 30s30-13.432 30-30S48.568 2 32 2m5 49.75H27v-24h10v24m-5-29.5a5 5 0 1 1 0-10a5 5 0 0 1 0 10" fill="currentcolor"/></svg>
<Tooltip style= {{ maxWidth: "350px", fontSize: "12px", textTransform: "none", fontWeight: "normal", borderRadius: "0.375rem", backgroundColor: "#34343A", color: "#fff", opacity: "1" }} delayShow={100} delayHide={150} place={place} anchorId={id} data-html={true} clickable={clickable}>
{children}
</Tooltip>
</div>
);
};

View file

@ -62,6 +62,7 @@ function FormFieldsDeluge() {
name="host" name="host"
label="Host" label="Host"
help="Eg. client.domain.ltd, domain.ltd/client, domain.ltd:port" help="Eg. client.domain.ltd, domain.ltd/client, domain.ltd:port"
tooltip={<div><p>See guides for how to connect to Deluge for various server types in our docs.</p><br /><p>Dedicated servers:</p><a href='https://autobrr.com/configuration/download-clients/dedicated#deluge' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/dedicated#deluge</a><p>Shared seedbox providers:</p><a href='https://autobrr.com/configuration/download-clients/shared-seedboxes#deluge' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/shared-seedboxes#deluge</a></div>}
/> />
<NumberFieldWide <NumberFieldWide
@ -96,6 +97,7 @@ function FormFieldsArr() {
name="host" name="host"
label="Host" label="Host"
help="Full url http(s)://domain.ltd and/or subdomain/subfolder" help="Full url http(s)://domain.ltd and/or subdomain/subfolder"
tooltip={<div><p>See guides for how to connect to the *arr suite for various server types in our docs.</p><br /><p>Dedicated servers:</p><a href='https://autobrr.com/configuration/download-clients/dedicated/#sonarr' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/dedicated/</a><p>Shared seedbox providers:</p><a href='https://autobrr.com/configuration/download-clients/shared-seedboxes#sonarr' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/shared-seedboxes</a></div>}
/> />
<PasswordFieldWide name="settings.apikey" label="API key" /> <PasswordFieldWide name="settings.apikey" label="API key" />
@ -123,6 +125,7 @@ function FormFieldsQbit() {
name="host" name="host"
label="Host" label="Host"
help="Eg. http(s)://client.domain.ltd, http(s)://domain.ltd/qbittorrent, http://domain.ltd:port" help="Eg. http(s)://client.domain.ltd, http(s)://domain.ltd/qbittorrent, http://domain.ltd:port"
tooltip={<div><p>See guides for how to connect to qBittorrent for various server types in our docs.</p><br /><p>Dedicated servers:</p><a href='https://autobrr.com/configuration/download-clients/dedicated#qbittorrent' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/dedicated#qbittorrent</a><p>Shared seedbox providers:</p><a href='https://autobrr.com/configuration/download-clients/shared-seedboxes#qbittorrent' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/shared-seedboxes#qbittorrent</a></div>}
/> />
{port > 0 && ( {port > 0 && (
@ -172,7 +175,7 @@ function FormFieldsPorla() {
<PasswordFieldWide name="settings.apikey" label="Auth token" /> <PasswordFieldWide name="settings.apikey" label="Auth token" />
</div> </div>
) );
} }
function FormFieldsRTorrent() { function FormFieldsRTorrent() {
@ -182,6 +185,7 @@ function FormFieldsRTorrent() {
name="host" name="host"
label="Host" label="Host"
help="Eg. http(s)://client.domain.ltd/RPC2, http(s)://domain.ltd/client, http(s)://domain.ltd/RPC2" help="Eg. http(s)://client.domain.ltd/RPC2, http(s)://domain.ltd/client, http(s)://domain.ltd/RPC2"
tooltip={<div><p>See guides for how to connect to rTorrent for various server types in our docs.</p><br /><p>Dedicated servers:</p><a href='https://autobrr.com/configuration/download-clients/dedicated#rtorrent--rutorrent' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/dedicated#rtorrent--rutorrent</a><p>Shared seedbox providers:</p><a href='https://autobrr.com/configuration/download-clients/shared-seedboxes#rtorrent' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/shared-seedboxes#rtorrent</a></div>}
/> />
</div> </div>
); );
@ -198,6 +202,7 @@ function FormFieldsTransmission() {
name="host" name="host"
label="Host" label="Host"
help="Eg. client.domain.ltd, domain.ltd/client, domain.ltd" help="Eg. client.domain.ltd, domain.ltd/client, domain.ltd"
tooltip={<div><p>See guides for how to connect to Transmission for various server types in our docs.</p><br /><p>Dedicated servers:</p><a href='https://autobrr.com/configuration/download-clients/dedicated#transmission' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/dedicated#transmission</a><p>Shared seedbox providers:</p><a href='https://autobrr.com/configuration/download-clients/shared-seedboxes#transmisison' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/shared-seedboxes#transmisison</a></div>}
/> />
<NumberFieldWide name="port" label="Port" help="Port for Transmission" /> <NumberFieldWide name="port" label="Port" help="Port for Transmission" />
@ -253,7 +258,7 @@ function FormFieldsRulesBasic() {
<SwitchGroupWide name="settings.rules.enabled" label="Enabled"/> <SwitchGroupWide name="settings.rules.enabled" label="Enabled"/>
{settings && settings.rules?.enabled === true && ( {settings && settings.rules?.enabled === true && (
<NumberFieldWide name="settings.rules.max_active_downloads" label="Max active downloads"/> <NumberFieldWide name="settings.rules.max_active_downloads" label="Max active downloads" tooltip={<span><p>Limit the amount of active downloads (0 is unlimited), to give the maximum amount of bandwidth and disk for the downloads.</p><a href='https://autobrr.com/configuration/download-clients/dedicated#deluge-rules' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/dedicated#deluge-rules</a><br /><br /><p>See recommendations for various server types here:</p><a href='https://autobrr.com/filters/examples#build-buffer' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters/examples#build-buffer</a></span>} />
)} )}
</div> </div>
); );
@ -282,7 +287,7 @@ function FormFieldsRules() {
<NumberFieldWide <NumberFieldWide
name="settings.rules.max_active_downloads" name="settings.rules.max_active_downloads"
label="Max active downloads" label="Max active downloads"
/> tooltip={<><p>Limit the amount of active downloads (0 is unlimited), to give the maximum amount of bandwidth and disk for the downloads.</p><a href='https://autobrr.com/configuration/download-clients/dedicated#qbittorrent-rules' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/download-clients/dedicated#qbittorrent-rules</a><br /><br /><p>See recommendations for various server types here:</p><a href='https://autobrr.com/filters/examples#build-buffer' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters/examples#build-buffer</a></>} />
<SwitchGroupWide <SwitchGroupWide
name="settings.rules.ignore_slow_torrents" name="settings.rules.ignore_slow_torrents"
label="Ignore slow torrents" label="Ignore slow torrents"
@ -295,6 +300,7 @@ function FormFieldsRules() {
label="Ignore condition" label="Ignore condition"
optionDefaultText="Select ignore condition" optionDefaultText="Select ignore condition"
options={DownloadRuleConditionOptions} options={DownloadRuleConditionOptions}
tooltip={<p>Choose whether to respect or ignore the <code className="text-blue-400">Max active downloads</code> setting before checking speed thresholds.</p>}
/> />
<NumberFieldWide <NumberFieldWide
name="settings.rules.download_speed_threshold" name="settings.rules.download_speed_threshold"

View file

@ -17,6 +17,8 @@ import { SlideOver } from "../../components/panels";
import Toast from "../../components/notifications/Toast"; import Toast from "../../components/notifications/Toast";
import { SelectFieldCreatable } from "../../components/inputs/select_wide"; import { SelectFieldCreatable } from "../../components/inputs/select_wide";
import { CustomTooltip } from "../../components/tooltips/CustomTooltip";
const Input = (props: InputProps) => ( const Input = (props: InputProps) => (
<components.Input <components.Input
{...props} {...props}
@ -81,7 +83,7 @@ const IrcSettingFields = (ind: IndexerDefinition, indexer: string) => {
{ind.irc.settings.map((f: IndexerSetting, idx: number) => { {ind.irc.settings.map((f: IndexerSetting, idx: number) => {
switch (f.type) { switch (f.type) {
case "text": case "text":
return <TextFieldWide name={`irc.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} validate={validateField(f)} />; return <TextFieldWide name={`irc.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} validate={validateField(f)} tooltip={<div><p>Please read our IRC guide if you are unfamiliar with IRC.</p><a href='https://autobrr.com/configuration/irc' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/configuration/irc</a></div>} />;
case "secret": case "secret":
if (f.name === "invite_command") { if (f.name === "invite_command") {
return <PasswordFieldWide name={`irc.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultVisible={true} defaultValue={f.default} validate={validateField(f)} />; return <PasswordFieldWide name={`irc.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} defaultVisible={true} defaultValue={f.default} validate={validateField(f)} />;
@ -171,7 +173,7 @@ const SettingFields = (ind: IndexerDefinition, indexer: string) => {
); );
case "secret": case "secret":
return ( return (
<PasswordFieldWide name={`settings.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} validate={validateField(f)} /> <PasswordFieldWide name={`settings.${f.name}`} label={f.label} required={f.required} key={idx} help={f.help} validate={validateField(f)} tooltip={<CustomTooltip anchorId={`settings.${f.name}`} clickable={true}><div><p>This field does not take a full URL. Only use alphanumeric strings like <code>uqcdi67cibkx3an8cmdm</code>.</p><br /><a href='https://autobrr.com/faqs#common-action-rejections' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/faqs#common-action-rejections</a></div></CustomTooltip>} />
); );
} }
return null; return null;
@ -548,7 +550,7 @@ export function IndexerUpdateForm({ isOpen, toggle, indexer }: UpdateProps) {
); );
case "secret": case "secret":
return ( return (
<PasswordFieldWide name={`settings.${f.name}`} label={f.label} key={idx} help={f.help} /> <PasswordFieldWide name={`settings.${f.name}`} label={f.label} key={idx} help={f.help} tooltip={<CustomTooltip anchorId={`settings.${f.name}`} clickable={true}><div><p>This field does not take a full URL. Only use alphanumeric strings like <code>uqcdi67cibkx3an8cmdm</code>.</p><br /><a href='https://autobrr.com/faqs#common-action-rejections' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/faqs#common-action-rejections</a></div></CustomTooltip>} />
); );
} }
return null; return null;

View file

@ -3,6 +3,7 @@ import { createRoot } from "react-dom/client";
import "@fontsource/inter/variable.css"; import "@fontsource/inter/variable.css";
import "./index.css"; import "./index.css";
import "react-tooltip/dist/react-tooltip.css";
import { App } from "./App"; import { App } from "./App";
import { InitializeGlobalContext } from "./utils/Context"; import { InitializeGlobalContext } from "./utils/Context";

View file

@ -12,6 +12,7 @@ import { Dialog, Switch as SwitchBasic, Transition } from "@headlessui/react";
import { ChevronRightIcon } from "@heroicons/react/24/solid"; import { ChevronRightIcon } from "@heroicons/react/24/solid";
import { DeleteModal } from "../../components/modals"; import { DeleteModal } from "../../components/modals";
import { CollapsableSection } from "./details"; import { CollapsableSection } from "./details";
import { CustomTooltip } from "../../components/tooltips/CustomTooltip";
interface FilterActionsProps { interface FilterActionsProps {
filter: Filter; filter: Filter;
@ -173,8 +174,8 @@ const TypeForm = ({ action, idx, clients }: TypeFormProps) => {
name={`actions.${idx}.save_path`} name={`actions.${idx}.save_path`}
label="Save path" label="Save path"
columns={6} columns={6}
placeholder="eg. /full/path/to/watch_folder" placeholder="eg. /full/path/to/download_folder"
/> tooltip={<CustomTooltip anchorId={`actions.${idx}.save_path`} clickable={true}><div><p>Set a custom save path for this action. Automatic Torrent Management will take care of this if using qBittorrent with categories.</p><br /><p>The field can use macros to transform/add values from metadata:</p><a href='https://autobrr.com/filters/actions#macros' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters/actions#macros</a></div></CustomTooltip>} />
</div> </div>
</div> </div>
@ -184,13 +185,13 @@ const TypeForm = ({ action, idx, clients }: TypeFormProps) => {
label="Category" label="Category"
columns={6} columns={6}
placeholder="eg. category" placeholder="eg. category"
/> tooltip={<CustomTooltip anchorId={`actions.${idx}.category`} clickable={true}><div><p>The field can use macros to transform/add values from metadata:</p><a href='https://autobrr.com/filters/actions#macros' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters/actions#macros</a></div></CustomTooltip>} />
<TextField <TextField
name={`actions.${idx}.tags`} name={`actions.${idx}.tags`}
label="Tags" label="Tags"
columns={6} columns={6}
placeholder="eg. tag1,tag2" placeholder="eg. tag1,tag2"
/> tooltip={<CustomTooltip anchorId={`actions.${idx}.tags`} clickable={true}><div><p>The field can use macros to transform/add values from metadata:</p><a href='https://autobrr.com/filters/actions#macros' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters/actions#macros</a></div></CustomTooltip>} />
</div> </div>
<CollapsableSection title="Rules" subtitle="client options"> <CollapsableSection title="Rules" subtitle="client options">
@ -235,16 +236,15 @@ const TypeForm = ({ action, idx, clients }: TypeFormProps) => {
<SwitchGroup <SwitchGroup
name={`actions.${idx}.ignore_rules`} name={`actions.${idx}.ignore_rules`}
label="Ignore client rules" label="Ignore client rules"
description="Download if max active reached" tooltip={<CustomTooltip anchorId={`actions.${idx}.ignore_rules`} clickable={true}><div><p>Choose to ignore rules set in <a className='text-blue-400 visited:text-blue-400' href="../../settings/clients">Client Settings</a>.</p></div></CustomTooltip>} />
/>
</div> </div>
<div className="col-span-6"> <div className="col-span-6">
<Select <Select
name={`actions.${idx}.content_layout`} name={`actions.${idx}.content_layout`}
label="Content Layout" label="Content Layout"
optionDefaultText="Select content layout" optionDefaultText="Select content layout"
options={ActionContentLayoutOptions} options={ActionContentLayoutOptions}></Select>
/>
<div className="mt-2"> <div className="mt-2">
<SwitchGroup <SwitchGroup
name={`actions.${idx}.skip_hash_check`} name={`actions.${idx}.skip_hash_check`}
@ -302,7 +302,7 @@ const TypeForm = ({ action, idx, clients }: TypeFormProps) => {
name={`actions.${idx}.save_path`} name={`actions.${idx}.save_path`}
label="Save path" label="Save path"
columns={6} columns={6}
placeholder="eg. /full/path/to/watch_folder" placeholder="eg. /full/path/to/download_folder"
/> />
</div> </div>
</div> </div>
@ -361,7 +361,7 @@ const TypeForm = ({ action, idx, clients }: TypeFormProps) => {
name={`actions.${idx}.save_path`} name={`actions.${idx}.save_path`}
label="Save path" label="Save path"
columns={6} columns={6}
placeholder="eg. /full/path/to/watch_folder" placeholder="eg. /full/path/to/download_folder"
/> />
</div> </div>
</div> </div>
@ -382,7 +382,7 @@ const TypeForm = ({ action, idx, clients }: TypeFormProps) => {
name={`actions.${idx}.save_path`} name={`actions.${idx}.save_path`}
label="Save path" label="Save path"
columns={6} columns={6}
placeholder="eg. /full/path/to/watch_folder" placeholder="eg. /full/path/to/download_folder"
/> />
</div> </div>
</div> </div>
@ -561,9 +561,10 @@ function FilterActionsItem({ action, clients, idx, initialEdit, remove }: Filter
label="Type" label="Type"
optionDefaultText="Select type" optionDefaultText="Select type"
options={ActionTypeOptions} options={ActionTypeOptions}
tooltip={<CustomTooltip anchorId={`actions.${idx}.type`} clickable={true}><div><p>Select the download client type for this action.</p></div></CustomTooltip>}
/> />
<TextField name={`actions.${idx}.name`} label="Name" columns={6}/> <TextField name={`actions.${idx}.name`} label="Name" columns={6} />
</div> </div>
<TypeForm action={action} clients={clients} idx={idx}/> <TypeForm action={action} clients={clients} idx={idx}/>

View file

@ -25,6 +25,8 @@ import { APIClient } from "../../api/APIClient";
import { useToggle } from "../../hooks/hooks"; import { useToggle } from "../../hooks/hooks";
import { classNames } from "../../utils"; import { classNames } from "../../utils";
import { CustomTooltip } from "../../components/tooltips/CustomTooltip";
import { import {
CheckboxField, CheckboxField,
IndexerMultiSelect, IndexerMultiSelect,
@ -346,13 +348,12 @@ export function General() {
<TitleSubtitle title="Rules" subtitle="Specify rules on how torrents should be handled/selected." /> <TitleSubtitle title="Rules" subtitle="Specify rules on how torrents should be handled/selected." />
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<TextField name="min_size" label="Min size" columns={6} placeholder="eg. 100MiB, 80GB" /> <TextField name="min_size" label="Min size" columns={6} placeholder="eg. 100MiB, 80GB" tooltip={<div><p>Supports units such as MB, MiB, GB, etc.</p><a href='https://autobrr.com/filters#rules' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#rules</a></div>} />
<TextField name="max_size" label="Max size" columns={6} placeholder="eg. 100MiB, 80GB" /> <TextField name="max_size" label="Max size" columns={6} placeholder="eg. 100MiB, 80GB" tooltip={<div><p>Supports units such as MB, MiB, GB, etc.</p><a href='https://autobrr.com/filters#rules' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#rules</a></div>} />
<NumberField name="delay" label="Delay" placeholder="Number of seconds to delay actions" /> <NumberField name="delay" label="Delay" placeholder="Number of seconds to delay actions" tooltip={<div><p>Number of seconds to wait before running actions.</p><a href='https://autobrr.com/filters#rules' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#rules</a></div>} />
<NumberField name="priority" label="Priority" placeholder="Higher number = higher prio" required={true} /> <NumberField name="priority" label="Priority" placeholder="Higher number = higher prio" required={true} tooltip={<div><p>Filters are checked in order of priority. Higher number = higher priority.</p><a href='https://autobrr.com/filters#rules' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#rules</a></div>} />
<NumberField name="max_downloads" label="Max downloads" placeholder="Takes any number (0 is infinite)" min={0} required={true} tooltip={<div><p>Number of max downloads as specified by the respective unit.</p><a href='https://autobrr.com/filters#rules' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#rules</a></div>} />
<NumberField name="max_downloads" label="Max downloads" placeholder="Takes any number (0 is infinite)" min={0} required={true} /> <Select name="max_downloads_unit" label="Max downloads per" options={downloadsPerUnitOptions} optionDefaultText="Select unit" tooltip={<div><p>The unit of time for counting the maximum downloads per filter.</p><a href='https://autobrr.com/filters#rules' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#rules</a></div>} />
<Select name="max_downloads_unit" label="Max downloads per" options={downloadsPerUnitOptions} optionDefaultText="Select unit" />
</div> </div>
</div> </div>
@ -368,16 +369,16 @@ export function MoviesTv() {
return ( return (
<div> <div>
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<TextField name="shows" label="Movies / Shows" columns={8} placeholder="eg. Movie,Show 1,Show?2" /> <TextField name="shows" label="Movies / Shows" columns={8} placeholder="eg. Movie,Show 1,Show?2" tooltip={<div><p>You can use basic filtering like wildcards <code>*</code> or replace single characters with <code>?</code></p><a href='https://autobrr.com/filters#tvmovies' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#tvmovies</a></div>} />
<TextField name="years" label="Years" columns={4} placeholder="eg. 2018,2019-2021" />
<TextField name="years" label="Years" columns={4} placeholder="eg. 2018,2019-2021" tooltip={<div><p>This field takes a range of years and/or comma separated single years.</p><a href='https://autobrr.com/filters#tvmovies' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#tvmovies</a></div>} />
</div> </div>
<div className="mt-6 lg:pb-8"> <div className="mt-6 lg:pb-8">
<TitleSubtitle title="Seasons and Episodes" subtitle="Set season and episode match constraints." /> <TitleSubtitle title="Seasons and Episodes" subtitle="Set season and episode match constraints." />
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<TextField name="seasons" label="Seasons" columns={8} placeholder="eg. 1,3,2-6" /> <TextField name="seasons" label="Seasons" columns={8} placeholder="eg. 1,3,2-6" tooltip={<div><p>See docs for information about how to <b>only</b> grab season packs:</p><a href='https://autobrr.com/filters/examples#only-season-packs' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters/examples#only-season-packs</a></div>} />
<TextField name="episodes" label="Episodes" columns={4} placeholder="eg. 2,4,10-20" /> <TextField name="episodes" label="Episodes" columns={4} placeholder="eg. 2,4,10-20" tooltip={<div><p>See docs for information about how to <b>only</b> grab episodes:</p><a href='https://autobrr.com/filters/examples/#skip-season-packs' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters/examples/#skip-season-packs</a></div>} />
</div> </div>
<div className="mt-6"> <div className="mt-6">
@ -389,23 +390,23 @@ export function MoviesTv() {
<TitleSubtitle title="Quality" subtitle="Set resolution, source, codec and related match constraints." /> <TitleSubtitle title="Quality" subtitle="Set resolution, source, codec and related match constraints." />
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<MultiSelect name="resolutions" options={RESOLUTION_OPTIONS} label="resolutions" columns={6} creatable={true} /> <MultiSelect name="resolutions" options={RESOLUTION_OPTIONS} label="resolutions" columns={6} creatable={true} tooltip={<div><p>Will match releases which contain any of the selected resolutions.</p><a href='https://autobrr.com/filters#quality' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality</a></div>} />
<MultiSelect name="sources" options={SOURCES_OPTIONS} label="sources" columns={6} creatable={true} /> <MultiSelect name="sources" options={SOURCES_OPTIONS} label="sources" columns={6} creatable={true} tooltip={<div><p>Will match releases which contain any of the selected sources.</p><a href='https://autobrr.com/filters#quality' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality</a></div>} />
</div> </div>
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<MultiSelect name="codecs" options={CODECS_OPTIONS} label="codecs" columns={6} creatable={true} /> <MultiSelect name="codecs" options={CODECS_OPTIONS} label="codecs" columns={6} creatable={true} tooltip={<div><p>Will match releases which contain any of the selected codecs.</p><a href='https://autobrr.com/filters#quality' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality</a></div>} />
<MultiSelect name="containers" options={CONTAINER_OPTIONS} label="containers" columns={6} creatable={true} /> <MultiSelect name="containers" options={CONTAINER_OPTIONS} label="containers" columns={6} creatable={true} tooltip={<div><p>Will match releases which contain any of the selected containers.</p><a href='https://autobrr.com/filters#quality' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality</a></div>} />
</div> </div>
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<MultiSelect name="match_hdr" options={HDR_OPTIONS} label="Match HDR" columns={6} creatable={true} /> <MultiSelect name="match_hdr" options={HDR_OPTIONS} label="Match HDR" columns={6} creatable={true} tooltip={<div><p>Will match releases which contain any of the selected HDR designations.</p><a href='https://autobrr.com/filters#quality' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality</a></div>} />
<MultiSelect name="except_hdr" options={HDR_OPTIONS} label="Except HDR" columns={6} creatable={true} /> <MultiSelect name="except_hdr" options={HDR_OPTIONS} label="Except HDR" columns={6} creatable={true} tooltip={<div><p>Won't match releases which contain any of the selected HDR designations (takes priority over Match HDR).</p><a href='https://autobrr.com/filters#quality' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality</a></div>} />
</div> </div>
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<MultiSelect name="match_other" options={OTHER_OPTIONS} label="Match Other" columns={6} creatable={true} /> <MultiSelect name="match_other" options={OTHER_OPTIONS} label="Match Other" columns={6} creatable={true} tooltip={<div><p>Will match releases which contain any of the selected designations.</p><a href='https://autobrr.com/filters#quality' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality</a></div>} />
<MultiSelect name="except_other" options={OTHER_OPTIONS} label="Except Other" columns={6} creatable={true} /> <MultiSelect name="except_other" options={OTHER_OPTIONS} label="Except Other" columns={6} creatable={true} tooltip={<div><p>Won't match releases which contain any of the selected Other designations (takes priority over Match Other).</p><a href='https://autobrr.com/filters#quality' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality</a></div>} />
</div> </div>
</div> </div>
</div> </div>
@ -416,26 +417,26 @@ export function Music({ values }: AdvancedProps) {
return ( return (
<div> <div>
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<TextField name="artists" label="Artists" columns={4} placeholder="eg. Artist One" /> <TextField name="artists" label="Artists" columns={4} placeholder="eg. Artist One" tooltip={<div><p>You can use basic filtering like wildcards <code>*</code> or replace single characters with <code>?</code></p><a href='https://autobrr.com/filters#music' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#music</a></div>} />
<TextField name="albums" label="Albums" columns={4} placeholder="eg. That Album" /> <TextField name="albums" label="Albums" columns={4} placeholder="eg. That Album" tooltip={<div><p>You can use basic filtering like wildcards <code>*</code> or replace single characters with <code>?</code></p><a href='https://autobrr.com/filters#music' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#music</a></div>} />
<TextField name="years" label="Years" columns={4} placeholder="eg. 2018,2019-2021" /> <TextField name="years" label="Years" columns={4} placeholder="eg. 2018,2019-2021" tooltip={<div><p>This field takes a range of years and/or comma separated single years.</p><a href='https://autobrr.com/filters#music' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#music</a></div>} />
</div> </div>
<div className="mt-6 lg:pb-8"> <div className="mt-6 lg:pb-8">
<TitleSubtitle title="Quality" subtitle="Format, source, log etc." /> <TitleSubtitle title="Quality" subtitle="Format, source, log etc." />
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<MultiSelect name="formats" options={FORMATS_OPTIONS} label="Format" columns={6} disabled={values.perfect_flac} /> <MultiSelect name="formats" options={FORMATS_OPTIONS} label="Format" columns={6} disabled={values.perfect_flac} tooltip={<div><p> Will only match releases with any of the selected formats. This is overridden by Perfect FLAC.</p><a href='https://autobrr.com/filters#quality-1' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality-1</a></div>} />
<MultiSelect name="quality" options={QUALITY_MUSIC_OPTIONS} label="Quality" columns={6} disabled={values.perfect_flac} /> <MultiSelect name="quality" options={QUALITY_MUSIC_OPTIONS} label="Quality" columns={6} disabled={values.perfect_flac} tooltip={<div><p> Will only match releases with any of the selected qualities. This is overridden by Perfect FLAC.</p><a href='https://autobrr.com/filters#quality-1' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality-1</a></div>} />
</div> </div>
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<MultiSelect name="media" options={SOURCES_MUSIC_OPTIONS} label="Media" columns={6} disabled={values.perfect_flac} /> <MultiSelect name="media" options={SOURCES_MUSIC_OPTIONS} label="Media" columns={6} disabled={values.perfect_flac} tooltip={<div><p> Will only match releases with any of the selected sources. This is overridden by Perfect FLAC.</p><a href='https://autobrr.com/filters#quality-1' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality-1</a></div>} />
<MultiSelect name="match_release_types" options={RELEASE_TYPE_MUSIC_OPTIONS} label="Type" columns={6} /> <MultiSelect name="match_release_types" options={RELEASE_TYPE_MUSIC_OPTIONS} label="Type" columns={6} tooltip={<div><p> Will only match releases with any of the selected types.</p><a href='https://autobrr.com/filters#quality-1' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality-1</a></div>} />
</div> </div>
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<NumberField name="log_score" label="Log score" placeholder="eg. 100" min={0} max={100} required={true} disabled={values.perfect_flac} /> <NumberField name="log_score" label="Log score" placeholder="eg. 100" min={0} max={100} required={true} disabled={values.perfect_flac} tooltip={<div><p> Log scores go from 0 to 100. This is overridden by Perfect FLAC.</p><a href='https://autobrr.com/filters#quality-1' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality-1</a></div>} />
</div> </div>
</div> </div>
@ -445,7 +446,7 @@ export function Music({ values }: AdvancedProps) {
<div role="group" aria-labelledby="label-email"> <div role="group" aria-labelledby="label-email">
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline"> <div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
{/* <div> {/* <div>
<div className="text-base font-medium text-gray-900 sm:text-sm sm:text-gray-700" id="label-email"> <div className="text-base font-medium text-gray-900 sm:text-sm sm:text-gray-700" >
Extra Extra
</div> </div>
</div> */} </div> */}
@ -453,7 +454,7 @@ export function Music({ values }: AdvancedProps) {
<div className="max-w-lg space-y-4"> <div className="max-w-lg space-y-4">
<CheckboxField name="log" label="Log" sublabel="Must include Log." disabled={values.perfect_flac} /> <CheckboxField name="log" label="Log" sublabel="Must include Log." disabled={values.perfect_flac} />
<CheckboxField name="cue" label="Cue" sublabel="Must include Cue." disabled={values.perfect_flac} /> <CheckboxField name="cue" label="Cue" sublabel="Must include Cue." disabled={values.perfect_flac} />
<CheckboxField name="perfect_flac" label="Perfect FLAC" sublabel="Override all options about quality, source, format, and cue/log/log score."/> <CheckboxField name="perfect_flac" label="Perfect FLAC" sublabel="Override all options about quality, source, format, and cue/log/log score." tooltip={<div><p>Override all options about quality, source, format, and cue/log/log score.</p><a href='https://autobrr.com/filters#quality-1' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#quality-1</a></div>} />
</div> </div>
</div> </div>
</div> </div>
@ -475,8 +476,8 @@ export function Advanced({ values }: AdvancedProps) {
<div className="grid col-span-12 gap-6"> <div className="grid col-span-12 gap-6">
<WarningAlert text="autobrr has extensive filtering built-in - only use this if nothing else works. If you need help please ask." /> <WarningAlert text="autobrr has extensive filtering built-in - only use this if nothing else works. If you need help please ask." />
<TextField name="match_releases" label="Match releases" columns={6} placeholder="eg. *some?movie*,*some?show*s01*" /> <TextField name="match_releases" label="Match releases" columns={6} placeholder="eg. *some?movie*,*some?show*s01*" tooltip={<div><p>This field has full regex support (Golang flavour).</p><a href='https://autobrr.com/filters#advanced' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#advanced</a><br/><br/><p>Remember to tick <b>Use Regex</b> below if using more than <code>*</code> and <code>?</code>.</p></div>} />
<TextField name="except_releases" label="Except releases" columns={6} placeholder="eg. *bad?movie*,*bad?show*s03*" /> <TextField name="except_releases" label="Except releases" columns={6} placeholder="eg. *bad?movie*,*bad?show*s03*" tooltip={<div><p>This field has full regex support (Golang flavour).</p><a href='https://autobrr.com/filters#advanced' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#advanced</a><br/><br/><p>Remember to tick <b>Use Regex</b> below if using more than <code>*</code> and <code>?</code>.</p></div>} />
{values.match_releases ? ( {values.match_releases ? (
<WarningAlert <WarningAlert
alert="Ask yourself:" alert="Ask yourself:"
@ -501,22 +502,23 @@ export function Advanced({ values }: AdvancedProps) {
</div> </div>
</CollapsableSection> </CollapsableSection>
<CollapsableSection defaultOpen={true} title="Groups" subtitle="Match only certain groups and/or ignore other groups."> <CollapsableSection defaultOpen={true} title="Groups" subtitle="Match only certain groups and/or ignore other groups.">
<TextField name="match_release_groups" label="Match release groups" columns={6} placeholder="eg. group1,group2" /> <TextField name="match_release_groups" label="Match release groups" columns={6} placeholder="eg. group1,group2" tooltip={<div><p>Comma separated list of release groups to match.</p><a href='https://autobrr.com/filters#advanced' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#advanced</a></div>} />
<TextField name="except_release_groups" label="Except release groups" columns={6} placeholder="eg. badgroup1,badgroup2" /> <TextField name="except_release_groups" label="Except release groups" columns={6} placeholder="eg. badgroup1,badgroup2" tooltip={<div><p>Comma separated list of release groups to ignore (takes priority over Match releases).</p><a href='https://autobrr.com/filters#advanced' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#advanced</a></div>} />
</CollapsableSection> </CollapsableSection>
<CollapsableSection defaultOpen={true} title="Categories and tags" subtitle="Match or ignore categories or tags."> <CollapsableSection defaultOpen={true} title="Categories and tags" subtitle="Match or ignore categories or tags.">
<TextField name="match_categories" label="Match categories" columns={6} placeholder="eg. *category*,category1" /> <TextField name="match_categories" label="Match categories" columns={6} placeholder="eg. *category*,category1" tooltip={<div><p>Comma separated list of categories to match.</p><a href='https://autobrr.com/filters/categories' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters/categories</a></div>} />
<TextField name="except_categories" label="Except categories" columns={6} placeholder="eg. *category*" /> <TextField name="except_categories" label="Except categories" columns={6} placeholder="eg. *category*" tooltip={<div><p>Comma separated list of categories to ignore (takes priority over Match releases).</p><a href='https://autobrr.com/filters/categories' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters/categories</a></div>} />
<TextField name="tags" label="Match tags" columns={6} placeholder="eg. tag1,tag2" /> <TextField name="tags" label="Match tags" columns={6} placeholder="eg. tag1,tag2" tooltip={<div><p>Comma separated list of tags to match.</p><a href='https://autobrr.com/filters#advanced' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#advanced</a></div>} />
<TextField name="except_tags" label="Except tags" columns={6} placeholder="eg. tag1,tag2" /> <TextField name="except_tags" label="Except tags" columns={6} placeholder="eg. tag1,tag2" tooltip={<div><p>Comma separated list of tags to ignore (takes priority over Match releases).</p><a href='https://autobrr.com/filters#advanced' className='text-blue-400 visited:text-blue-400' target='_blank'>hhttps://autobrr.com/filters#advanced</a></div>} />
</CollapsableSection> </CollapsableSection>
<CollapsableSection defaultOpen={true} title="Uploaders" subtitle="Match or ignore uploaders."> <CollapsableSection defaultOpen={true} title="Uploaders" subtitle="Match or ignore uploaders.">
<TextField name="match_uploaders" label="Match uploaders" columns={6} placeholder="eg. uploader1,uploader2" /> <TextField name="match_uploaders" label="Match uploaders" columns={6} placeholder="eg. uploader1,uploader2" tooltip={<div><p>Comma separated list of uploaders to match.</p><a href='https://autobrr.com/filters#advanced' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#advanced</a></div>} />
<TextField name="except_uploaders" label="Except uploaders" columns={6} placeholder="eg. anonymous1,anonymous2" /> <TextField name="except_uploaders" label="Except uploaders" columns={6} placeholder="eg. anonymous1,anonymous2" tooltip={<div><p>Comma separated list of uploaders to ignore (takes priority over Match releases).</p><a href='https://autobrr.com/filters#advanced' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#advanced</a></div>} />
</CollapsableSection> </CollapsableSection>
<CollapsableSection defaultOpen={true} title="Language" subtitle="Match or ignore languages."> <CollapsableSection defaultOpen={true} title="Language" subtitle="Match or ignore languages.">
@ -543,7 +545,7 @@ export function Advanced({ values }: AdvancedProps) {
<CollapsableSection defaultOpen={true} title="Freeleech" subtitle="Match only freeleech and freeleech percent."> <CollapsableSection defaultOpen={true} title="Freeleech" subtitle="Match only freeleech and freeleech percent.">
<div className="col-span-6"> <div className="col-span-6">
<SwitchGroup name="freeleech" label="Freeleech" /> <SwitchGroup name="freeleech" label="Freeleech" description="Enabling freeleech locks freeleech percent to 100. Use either." tooltip={<div><p>Comma separated list of uploaders to ignore (takes priority over Match releases).</p><a href='https://autobrr.com/filters#advanced' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters#advanced</a></div>} />
</div> </div>
<TextField name="freeleech_percent" label="Freeleech percent" columns={6} placeholder="eg. 50,75-100" disabled={values.freeleech}/> <TextField name="freeleech_percent" label="Freeleech percent" columns={6} placeholder="eg. 50,75-100" disabled={values.freeleech}/>
@ -603,12 +605,12 @@ export function CollapsableSection({ title, subtitle, children, defaultOpen }: C
type="button" type="button"
className="inline-flex items-center px-4 py-2 border-transparent text-sm font-medium text-white" className="inline-flex items-center px-4 py-2 border-transparent text-sm font-medium text-white"
> >
{isOpen ? <ChevronDownIcon className="h-6 w-6 text-gray-500" aria-hidden="true" /> : <ChevronRightIcon className="h-6 w-6 text-gray-500" aria-hidden="true" />} {isOpen ? <ChevronDownIcon className="-mr-4 h-6 w-6 text-gray-500" aria-hidden="true" /> : <ChevronRightIcon className="-mr-4 h-6 w-6 text-gray-500" aria-hidden="true" />}
</button> </button>
</div> </div>
</div> </div>
{isOpen && ( {isOpen && (
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-2 grid grid-cols-12 gap-6">
{children} {children}
</div> </div>
)} )}
@ -623,7 +625,7 @@ export function External() {
<div> <div>
<div className="mt-6"> <div className="mt-6">
<SwitchGroup name="external_script_enabled" heading={true} label="Script" description="Run external script and check status as part of filtering." /> <SwitchGroup name="external_script_enabled" heading={true} label="Script" description="Run external script and check status as part of filtering." tooltip={<div><p>For custom commands you should specify the full path to the binary/program you want to run. And you can include your own static variables:</p><a href='https://autobrr.com/filters/actions#custom-commands--exec' className='text-blue-400 visited:text-blue-400' target='_blank'>https://autobrr.com/filters/actions#custom-commands--exec</a></div>}/>
<div className="mt-6 grid grid-cols-12 gap-6"> <div className="mt-6 grid grid-cols-12 gap-6">
<TextField <TextField

View file

@ -104,7 +104,7 @@ function DownloadClientSettings() {
<div className="ml-4 mt-4 flex-shrink-0"> <div className="ml-4 mt-4 flex-shrink-0">
<button <button
type="button" type="button"
className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-sm text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={toggleAddClient} onClick={toggleAddClient}
> >
Add new Add new

View file

@ -1997,6 +1997,22 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@floating-ui/core@npm:^1.0.5":
version: 1.1.0
resolution: "@floating-ui/core@npm:1.1.0"
checksum: ac48969915247320e52d173480c224e2ded94d557ba4cc504547bb314d126348dcc0aeef05686673e1b289596e6ce15118edc84900dd310c613d805f83b4e27d
languageName: node
linkType: hard
"@floating-ui/dom@npm:^1.0.4":
version: 1.1.0
resolution: "@floating-ui/dom@npm:1.1.0"
dependencies:
"@floating-ui/core": ^1.0.5
checksum: 717551da6f470101cd1de0edc449b229fade7f94c2ff98d09e14ced041e27092aac94bd78756c4247a42b57129f187292f145f0001a81ece399a89b20b4be60b
languageName: node
linkType: hard
"@fontsource/inter@npm:^4.5.11": "@fontsource/inter@npm:^4.5.11":
version: 4.5.11 version: 4.5.11
resolution: "@fontsource/inter@npm:4.5.11" resolution: "@fontsource/inter@npm:4.5.11"
@ -4681,6 +4697,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"classnames@npm:^2.3.2":
version: 2.3.2
resolution: "classnames@npm:2.3.2"
checksum: 2c62199789618d95545c872787137262e741f9db13328e216b093eea91c85ef2bfb152c1f9e63027204e2559a006a92eb74147d46c800a9f96297ae1d9f96f4e
languageName: node
linkType: hard
"clean-css@npm:^5.2.2": "clean-css@npm:^5.2.2":
version: 5.3.0 version: 5.3.0
resolution: "clean-css@npm:5.3.0" resolution: "clean-css@npm:5.3.0"
@ -11120,6 +11143,19 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-tooltip@npm:^5.5.2":
version: 5.5.2
resolution: "react-tooltip@npm:5.5.2"
dependencies:
"@floating-ui/dom": ^1.0.4
classnames: ^2.3.2
peerDependencies:
react: ">=16.14.0"
react-dom: ">=16.14.0"
checksum: 2858744531317ce491bd234bdf80dd3412ea0b147a7d88bba55cebb9c42787acc5420a9b63c6134e0b1f5ab9477b8ac7bff3961c4738aaf8bff90c6c9dc0a649
languageName: node
linkType: hard
"react-transition-group@npm:^4.3.0": "react-transition-group@npm:^4.3.0":
version: 4.4.2 version: 4.4.2
resolution: "react-transition-group@npm:4.4.2" resolution: "react-transition-group@npm:4.4.2"
@ -12980,6 +13016,7 @@ __metadata:
react-scripts: ^5.0.1 react-scripts: ^5.0.1
react-select: ^5.3.2 react-select: ^5.3.2
react-table: ^7.8.0 react-table: ^7.8.0
react-tooltip: ^5.5.2
stacktracey: ^2.1.8 stacktracey: ^2.1.8
tailwindcss: ^3.1.3 tailwindcss: ^3.1.3
typescript: ^4.7.3 typescript: ^4.7.3