mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 16:59:12 +00:00
feat(web): mobile UI improvements (#359)
* enhancement: improved alerts component contrast enhancement: simplified and improved radio switch group look fix: fixed inconsistent spacing in input components (there's still some work left to be done) fix: made slideover panel display on full width on mobile devices enhancement: made forms more accessible to mobile users, adapter changes in accordance with the previous input components fix fix: fixed misspelling in NotificationForms filename chore: cleaned up code fix: made filter table top edges less round and improved look fix: fixed a bug where when a modal/slideover component was opened, a 1px white bar would be shown in one of the modal parent elements (for the fix see L89 in screens/settings/DwonloadClient.tsx) enhancement: improved responsiveness for irc network list * Fixed 2 small comma warnings from ESLint Co-authored-by: anonymous <anonymous>
This commit is contained in:
parent
f961115dac
commit
3da594ec75
26 changed files with 758 additions and 640 deletions
|
@ -99,7 +99,7 @@ export const APIClient = {
|
|||
toggleEnable: (id: number, enabled: boolean) => appClient.Patch(`api/feeds/${id}/enabled`, { enabled }),
|
||||
update: (feed: Feed) => appClient.Put(`api/feeds/${feed.id}`, feed),
|
||||
delete: (id: number) => appClient.Delete(`api/feeds/${id}`),
|
||||
test: (feed: Feed) => appClient.Post("api/feeds/test", feed),
|
||||
test: (feed: Feed) => appClient.Post("api/feeds/test", feed)
|
||||
},
|
||||
indexers: {
|
||||
// returns indexer options for all currently present/enabled indexers
|
||||
|
|
|
@ -2,26 +2,26 @@
|
|||
import { ExclamationIcon } from "@heroicons/react/solid";
|
||||
|
||||
interface props {
|
||||
title: string;
|
||||
title?: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export function AlertWarning({ title, text }: props) {
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="rounded-md bg-yellow-50 p-4">
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<ExclamationIcon
|
||||
className="h-5 w-5 text-yellow-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h3 className="text-sm font-medium text-yellow-800">{title}</h3>
|
||||
<div className="mt-2 text-sm text-yellow-700">
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
<div className="my-4 rounded-md bg-yellow-50 dark:bg-yellow-100 p-4 border border-yellow-300 dark:border-none">
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<ExclamationIcon
|
||||
className="h-5 w-5 text-yellow-400 dark:text-yellow-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
{title ? (
|
||||
<h3 className="mb-1 text-md font-medium text-yellow-800">{title}</h3>
|
||||
) : null}
|
||||
<div className="text-sm text-yellow-800">
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -11,7 +11,7 @@ const DEBUG: FC<DebugProps> = ({ values }) => {
|
|||
|
||||
return (
|
||||
<div className="w-full p-2 flex flex-col mt-6 bg-gray-100 dark:bg-gray-900">
|
||||
<pre className="dark:text-gray-400">{JSON.stringify(values, null, 2)}</pre>
|
||||
<pre className="dark:text-gray-400 break-all whitespace-pre-wrap">{JSON.stringify(values, null, 2)}</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -25,7 +25,7 @@ export const TextFieldWide = ({
|
|||
required,
|
||||
hidden
|
||||
}: TextFieldWideProps) => (
|
||||
<div hidden={hidden} className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
||||
<div hidden={hidden} className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
<div>
|
||||
<label htmlFor={name} 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>}
|
||||
|
@ -80,7 +80,7 @@ export const PasswordFieldWide = ({
|
|||
const [isVisible, toggleVisibility] = useToggle(defaultVisible);
|
||||
|
||||
return (
|
||||
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
||||
<div className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
<div>
|
||||
<label htmlFor={name} 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>}
|
||||
|
@ -134,7 +134,7 @@ export const NumberFieldWide = ({
|
|||
defaultValue,
|
||||
required
|
||||
}: NumberFieldWideProps) => (
|
||||
<div className="px-4 space-y-1 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
||||
<div className="px-4 space-y-1 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor={name}
|
||||
|
@ -187,7 +187,7 @@ export const SwitchGroupWide = ({
|
|||
description,
|
||||
defaultValue
|
||||
}: SwitchGroupWideProps) => (
|
||||
<ul className="mt-2 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<ul className="mt-2 px-4 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<Switch.Group as="li" className="py-4 flex items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<Switch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-white"
|
||||
|
@ -212,7 +212,7 @@ export const SwitchGroupWide = ({
|
|||
type="button"
|
||||
value={field.value}
|
||||
checked={field.checked ?? false}
|
||||
onChange={value => {
|
||||
onChange={(value: unknown) => {
|
||||
form.setFieldValue(field?.name ?? "", value);
|
||||
}}
|
||||
className={classNames(
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Fragment } from "react";
|
||||
import { Field, useFormikContext } from "formik";
|
||||
import { RadioGroup } from "@headlessui/react";
|
||||
import { classNames } from "../../utils";
|
||||
|
@ -25,14 +24,13 @@ function RadioFieldsetWide({ name, legend, options }: props) {
|
|||
setFieldValue
|
||||
} = useFormikContext<anyObj>();
|
||||
|
||||
|
||||
const onChange = (value: string) => {
|
||||
setFieldValue(name, value);
|
||||
};
|
||||
|
||||
return (
|
||||
<fieldset>
|
||||
<div className="space-y-2 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:px-6 sm:py-5">
|
||||
<div className="space-y-2 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:py-4">
|
||||
<div>
|
||||
<legend className="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{legend}
|
||||
|
@ -60,49 +58,41 @@ function RadioFieldsetWide({ name, legend, options }: props) {
|
|||
? "rounded-bl-md rounded-br-md"
|
||||
: "",
|
||||
checked
|
||||
? "bg-indigo-50 dark:bg-gray-700 border-indigo-200 dark:border-blue-600 z-10"
|
||||
? "border-1 bg-indigo-100 dark:bg-blue-900 border-indigo-400 dark:border-blue-600 z-10"
|
||||
: "border-gray-200 dark:border-gray-700",
|
||||
"relative border p-4 flex cursor-pointer focus:outline-none"
|
||||
)
|
||||
}
|
||||
>
|
||||
{({ active, checked }) => (
|
||||
<Fragment>
|
||||
{({ checked }) => (
|
||||
<>
|
||||
<span
|
||||
className={classNames(
|
||||
checked
|
||||
? "bg-indigo-600 dark:bg-blue-600 border-transparent"
|
||||
: "bg-white border-gray-300 dark:border-gray-300",
|
||||
active
|
||||
? "ring-2 ring-offset-2 ring-indigo-500 dark:ring-blue-500"
|
||||
: "",
|
||||
"h-4 w-4 mt-0.5 cursor-pointer rounded-full border flex items-center justify-center"
|
||||
? "bg-indigo-600 dark:bg-blue-500 border-transparent"
|
||||
: "bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-300",
|
||||
"h-6 w-6 mt-1 cursor-pointer rounded-full border flex items-center justify-center"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span className="rounded-full bg-white w-1.5 h-1.5" />
|
||||
</span>
|
||||
/>
|
||||
<div className="ml-3 flex flex-col">
|
||||
<RadioGroup.Label
|
||||
as="span"
|
||||
className={classNames(
|
||||
checked ? "text-indigo-900 dark:text-blue-500" : "text-gray-900 dark:text-gray-300",
|
||||
"block text-sm font-medium"
|
||||
"block text-md text-gray-900 dark:text-gray-300",
|
||||
checked ? "font-bold" : "font-medium"
|
||||
)}
|
||||
>
|
||||
{setting.label}
|
||||
</RadioGroup.Label>
|
||||
<RadioGroup.Description
|
||||
as="span"
|
||||
className={classNames(
|
||||
checked ? "text-indigo-700 dark:text-blue-500" : "text-gray-500",
|
||||
"block text-sm"
|
||||
)}
|
||||
className="block text-sm text-gray-700 dark:text-gray-400"
|
||||
>
|
||||
{setting.description}
|
||||
</RadioGroup.Description>
|
||||
</div>
|
||||
</Fragment>
|
||||
</>
|
||||
)}
|
||||
</RadioGroup.Option>
|
||||
))}
|
||||
|
|
|
@ -354,7 +354,7 @@ export const SelectWide = ({
|
|||
return (
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
|
||||
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
||||
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-4">
|
||||
<Field name={name} type="select">
|
||||
{({
|
||||
field,
|
||||
|
|
|
@ -35,7 +35,7 @@ export const DeleteModal: FC<DeleteModalProps> = ({ isOpen, buttonRef, toggle, d
|
|||
</Transition.Child>
|
||||
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
​
|
||||
</span>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
|
|
|
@ -36,7 +36,7 @@ function SlideOver<DataType>({
|
|||
testFn,
|
||||
isTesting,
|
||||
isTestSuccessful,
|
||||
isTestError,
|
||||
isTestError
|
||||
}: SlideOverProps<DataType>): React.ReactElement {
|
||||
const cancelModalButtonRef = useRef<HTMLInputElement | null>(null);
|
||||
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false);
|
||||
|
@ -64,7 +64,7 @@ function SlideOver<DataType>({
|
|||
<div className="absolute inset-0 overflow-hidden">
|
||||
<Dialog.Overlay className="absolute inset-0" />
|
||||
|
||||
<div className="fixed inset-y-0 right-0 pl-10 max-w-full flex sm:pl-16">
|
||||
<div className="fixed inset-y-0 right-0 max-w-full flex">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transform transition ease-in-out duration-500 sm:duration-700"
|
||||
|
|
|
@ -338,5 +338,5 @@ export const EventOptions: SelectOption[] = [
|
|||
label: "New update",
|
||||
value: "APP_UPDATE_AVAILABLE",
|
||||
description: "Get notified on updates"
|
||||
},
|
||||
}
|
||||
];
|
||||
|
|
|
@ -96,7 +96,7 @@ function FilterAddForm({ isOpen, toggle }: filterAddFormProps) {
|
|||
<div
|
||||
className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<div
|
||||
className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
||||
className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="name"
|
||||
|
|
|
@ -50,24 +50,31 @@ function FormFieldsDefault() {
|
|||
} = useFormikContext<InitialValues>();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<TextFieldWide name="host" label="Host" help="Eg. client.domain.ltd, domain.ltd/client, domain.ltd:port"/>
|
||||
<div className="flex flex-col space-y-4 px-1 py-6 sm:py-0 sm:space-y-0">
|
||||
<TextFieldWide
|
||||
name="host"
|
||||
label="Host"
|
||||
help="Eg. client.domain.ltd, domain.ltd/client, domain.ltd:port"
|
||||
/>
|
||||
|
||||
<NumberFieldWide name="port" label="Port" help="WebUI port for qBittorrent and daemon port for Deluge"/>
|
||||
<NumberFieldWide
|
||||
name="port"
|
||||
label="Port"
|
||||
help="WebUI port for qBittorrent and daemon port for Deluge"
|
||||
/>
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200 dark:divide-gray-700">
|
||||
<SwitchGroupWide name="tls" label="TLS"/>
|
||||
<SwitchGroupWide name="tls" label="TLS" />
|
||||
|
||||
{tls && (
|
||||
<Fragment>
|
||||
<SwitchGroupWide name="tls_skip_verify" label="Skip TLS verification (insecure)"/>
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
{tls && (
|
||||
<SwitchGroupWide
|
||||
name="tls_skip_verify"
|
||||
label="Skip TLS verification (insecure)"
|
||||
/>
|
||||
)}
|
||||
|
||||
<TextFieldWide name="username" label="Username"/>
|
||||
<PasswordFieldWide name="password" label="Password"/>
|
||||
</Fragment>
|
||||
<TextFieldWide name="username" label="Username" />
|
||||
<PasswordFieldWide name="password" label="Password" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -77,22 +84,24 @@ function FormFieldsArr() {
|
|||
} = useFormikContext<InitialValues>();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<TextFieldWide name="host" label="Host" help="Full url http(s)://domain.ltd and/or subdomain/subfolder"/>
|
||||
<div className="flex flex-col space-y-4 px-1 mb-4 sm:py-0 sm:space-y-0">
|
||||
<TextFieldWide
|
||||
name="host"
|
||||
label="Host"
|
||||
help="Full url http(s)://domain.ltd and/or subdomain/subfolder"
|
||||
/>
|
||||
|
||||
<PasswordFieldWide name="settings.apikey" label="API key"/>
|
||||
<PasswordFieldWide name="settings.apikey" label="API key" />
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<SwitchGroupWide name="settings.basic.auth" label="Basic auth"/>
|
||||
</div>
|
||||
<SwitchGroupWide name="settings.basic.auth" label="Basic auth" />
|
||||
|
||||
{settings.basic?.auth === true && (
|
||||
<Fragment>
|
||||
<TextFieldWide name="settings.basic.username" label="Username"/>
|
||||
<PasswordFieldWide name="settings.basic.password" label="Password"/>
|
||||
</Fragment>
|
||||
<>
|
||||
<TextFieldWide name="settings.basic.username" label="Username" />
|
||||
<PasswordFieldWide name="settings.basic.password" label="Password" />
|
||||
</>
|
||||
)}
|
||||
</Fragment>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -102,37 +111,42 @@ function FormFieldsQbit() {
|
|||
} = useFormikContext<InitialValues>();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<TextFieldWide name="host" label="Host" help="Eg. client.domain.ltd, domain.ltd/client, domain.ltd:port" />
|
||||
<div className="flex flex-col space-y-4 px-1 py-6 sm:py-0 sm:space-y-0">
|
||||
<TextFieldWide
|
||||
name="host"
|
||||
label="Host"
|
||||
help="Eg. client.domain.ltd, domain.ltd/client, domain.ltd:port"
|
||||
/>
|
||||
|
||||
{port > 0 && (
|
||||
<NumberFieldWide name="port" label="Port" help="WebUI port for qBittorrent" />
|
||||
<NumberFieldWide
|
||||
name="port"
|
||||
label="Port"
|
||||
help="WebUI port for qBittorrent"
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200 dark:divide-gray-700">
|
||||
<SwitchGroupWide name="tls" label="TLS" />
|
||||
<SwitchGroupWide name="tls" label="TLS" />
|
||||
|
||||
{tls && (
|
||||
<Fragment>
|
||||
<SwitchGroupWide name="tls_skip_verify" label="Skip TLS verification (insecure)" />
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
{tls && (
|
||||
<SwitchGroupWide
|
||||
name="tls_skip_verify"
|
||||
label="Skip TLS verification (insecure)"
|
||||
/>
|
||||
)}
|
||||
|
||||
<TextFieldWide name="username" label="Username" />
|
||||
<PasswordFieldWide name="password" label="Password" />
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<SwitchGroupWide name="settings.basic.auth" label="Basic auth" />
|
||||
</div>
|
||||
<SwitchGroupWide name="settings.basic.auth" label="Basic auth" />
|
||||
|
||||
{settings.basic?.auth === true && (
|
||||
<Fragment>
|
||||
<>
|
||||
<TextFieldWide name="settings.basic.username" label="Username" />
|
||||
<PasswordFieldWide name="settings.basic.password" label="Password" />
|
||||
</Fragment>
|
||||
</>
|
||||
)}
|
||||
</Fragment>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -142,24 +156,27 @@ function FormFieldsTransmission() {
|
|||
} = useFormikContext<InitialValues>();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<TextFieldWide name="host" label="Host" help="Eg. client.domain.ltd, domain.ltd/client, domain.ltd"/>
|
||||
<div className="flex flex-col space-y-4 px-1 py-6 sm:py-0 sm:space-y-0">
|
||||
<TextFieldWide
|
||||
name="host"
|
||||
label="Host"
|
||||
help="Eg. client.domain.ltd, domain.ltd/client, domain.ltd"
|
||||
/>
|
||||
|
||||
<NumberFieldWide name="port" label="Port" help="Port for Transmission"/>
|
||||
<NumberFieldWide name="port" label="Port" help="Port for Transmission" />
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200 dark:divide-gray-700">
|
||||
<SwitchGroupWide name="tls" label="TLS"/>
|
||||
<SwitchGroupWide name="tls" label="TLS" />
|
||||
|
||||
{tls && (
|
||||
<Fragment>
|
||||
<SwitchGroupWide name="tls_skip_verify" label="Skip TLS verification (insecure)"/>
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
{tls && (
|
||||
<SwitchGroupWide
|
||||
name="tls_skip_verify"
|
||||
label="Skip TLS verification (insecure)"
|
||||
/>
|
||||
)}
|
||||
|
||||
<TextFieldWide name="username" label="Username"/>
|
||||
<PasswordFieldWide name="password" label="Password"/>
|
||||
</Fragment>
|
||||
<TextFieldWide name="username" label="Username" />
|
||||
<PasswordFieldWide name="password" label="Password" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -186,21 +203,17 @@ function FormFieldsRulesBasic() {
|
|||
return (
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 py-5">
|
||||
|
||||
<div className="px-6 space-y-1">
|
||||
<div className="px-4 space-y-1">
|
||||
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">Rules</Dialog.Title>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Manage max downloads.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<SwitchGroupWide name="settings.rules.enabled" label="Enabled"/>
|
||||
</div>
|
||||
<SwitchGroupWide name="settings.rules.enabled" label="Enabled"/>
|
||||
|
||||
{settings && settings.rules?.enabled === true && (
|
||||
<Fragment>
|
||||
<NumberFieldWide name="settings.rules.max_active_downloads" label="Max active downloads"/>
|
||||
</Fragment>
|
||||
<NumberFieldWide name="settings.rules.max_active_downloads" label="Max active downloads"/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -212,34 +225,38 @@ function FormFieldsRules() {
|
|||
} = useFormikContext<InitialValues>();
|
||||
|
||||
return (
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 py-5">
|
||||
|
||||
<div className="px-6 space-y-1">
|
||||
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">Rules</Dialog.Title>
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 py-5 px-2">
|
||||
<div className="px-4 space-y-1">
|
||||
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">
|
||||
Rules
|
||||
</Dialog.Title>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Manage max downloads etc.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<SwitchGroupWide name="settings.rules.enabled" label="Enabled"/>
|
||||
</div>
|
||||
<SwitchGroupWide name="settings.rules.enabled" label="Enabled" />
|
||||
|
||||
{settings.rules?.enabled === true && (
|
||||
<Fragment>
|
||||
<NumberFieldWide name="settings.rules.max_active_downloads" label="Max active downloads"/>
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<SwitchGroupWide name="settings.rules.ignore_slow_torrents" label="Ignore slow torrents"/>
|
||||
</div>
|
||||
<>
|
||||
<NumberFieldWide
|
||||
name="settings.rules.max_active_downloads"
|
||||
label="Max active downloads"
|
||||
/>
|
||||
<SwitchGroupWide
|
||||
name="settings.rules.ignore_slow_torrents"
|
||||
label="Ignore slow torrents"
|
||||
/>
|
||||
|
||||
{settings.rules?.ignore_slow_torrents === true && (
|
||||
<Fragment>
|
||||
<NumberFieldWide name="settings.rules.download_speed_threshold" label="Download speed threshold"
|
||||
placeholder="in KB/s"
|
||||
help="If download speed is below this when max active downloads is hit, download anyways. KB/s"/>
|
||||
</Fragment>
|
||||
<NumberFieldWide
|
||||
name="settings.rules.download_speed_threshold"
|
||||
label="Download speed threshold"
|
||||
placeholder="in KB/s"
|
||||
help="If download speed is below this when max active downloads is hit, download anyways. KB/s"
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -248,7 +265,7 @@ function FormFieldsRules() {
|
|||
export const rulesComponentMap: componentMapType = {
|
||||
DELUGE_V1: <FormFieldsRulesBasic/>,
|
||||
DELUGE_V2: <FormFieldsRulesBasic/>,
|
||||
QBITTORRENT: <FormFieldsRules/>,
|
||||
QBITTORRENT: <FormFieldsRules/>
|
||||
};
|
||||
|
||||
interface formButtonsProps {
|
||||
|
@ -490,20 +507,14 @@ export function DownloadClientAddForm({ isOpen, toggle }: formProps) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y dark:divide-gray-700">
|
||||
<div className="flex flex-col space-y-4 px-1 py-6 sm:py-0 sm:space-y-0">
|
||||
<TextFieldWide name="name" label="Name"/>
|
||||
|
||||
<div
|
||||
className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200 dark:divide-gray-700">
|
||||
<SwitchGroupWide name="enabled" label="Enabled"/>
|
||||
</div>
|
||||
|
||||
<SwitchGroupWide name="enabled" label="Enabled"/>
|
||||
<RadioFieldsetWide
|
||||
name="type"
|
||||
legend="Type"
|
||||
options={DownloadClientTypeOptions}
|
||||
/>
|
||||
|
||||
<div>{componentMap[values.type]}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -697,17 +708,12 @@ export function DownloadClientUpdateForm({ client, isOpen, toggle }: updateFormP
|
|||
|
||||
<div className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y dark:divide-gray-700">
|
||||
<TextFieldWide name="name" label="Name"/>
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<SwitchGroupWide name="enabled" label="Enabled"/>
|
||||
</div>
|
||||
|
||||
<SwitchGroupWide name="enabled" label="Enabled"/>
|
||||
<RadioFieldsetWide
|
||||
name="type"
|
||||
legend="Type"
|
||||
options={DownloadClientTypeOptions}
|
||||
/>
|
||||
|
||||
<div>{componentMap[values.type]}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -116,7 +116,7 @@ export function FeedUpdateForm({ isOpen, toggle, feed }: UpdateProps) {
|
|||
|
||||
<div className="space-y-4 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div
|
||||
className="py-4 flex items-center justify-between space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
||||
className="py-4 flex items-center justify-between space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="type"
|
||||
|
|
|
@ -63,7 +63,7 @@ const IrcSettingFields = (ind: IndexerDefinition, indexer: string) => {
|
|||
<Fragment>
|
||||
{ind && ind.irc && ind.irc.settings && (
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 py-5">
|
||||
<div className="px-6 space-y-1">
|
||||
<div className="px-4 space-y-1">
|
||||
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">IRC</Dialog.Title>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-200">
|
||||
Networks, channels and invite commands are configured automatically.
|
||||
|
@ -94,7 +94,7 @@ const FeedSettingFields = (ind: IndexerDefinition, indexer: string) => {
|
|||
<Fragment>
|
||||
{ind && ind.torznab && ind.torznab.settings && (
|
||||
<div className="">
|
||||
<div className="px-6 space-y-1">
|
||||
<div className="px-4 space-y-1">
|
||||
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">Torznab</Dialog.Title>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-200">
|
||||
Torznab feed
|
||||
|
@ -329,13 +329,13 @@ export function IndexerAddForm({ isOpen, toggle }: AddProps) {
|
|||
</div>
|
||||
|
||||
<div className="py-6 space-y-4 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="py-4 flex items-center justify-between space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
||||
<div className="py-4 flex items-center justify-between space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="identifier"
|
||||
className="block text-sm font-medium text-gray-900 dark:text-white"
|
||||
>
|
||||
Indexer
|
||||
Indexer
|
||||
</label>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
|
@ -391,9 +391,7 @@ export function IndexerAddForm({ isOpen, toggle }: AddProps) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<SwitchGroupWide name="enabled" label="Enabled" />
|
||||
</div>
|
||||
<SwitchGroupWide name="enabled" label="Enabled" />
|
||||
|
||||
{SettingFields(indexer, values.identifier)}
|
||||
|
||||
|
@ -504,7 +502,7 @@ export function IndexerUpdateForm({ isOpen, toggle, indexer }: UpdateProps) {
|
|||
...o,
|
||||
[obj.name]: obj.value
|
||||
} as Record<string, string>),
|
||||
{} as Record<string, string>
|
||||
{} as Record<string, string>
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -519,16 +517,14 @@ export function IndexerUpdateForm({ isOpen, toggle, indexer }: UpdateProps) {
|
|||
initialValues={initialValues}
|
||||
>
|
||||
{() => (
|
||||
<div className="py-6 space-y-6 sm:py-0 sm:space-y-0 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2"
|
||||
>
|
||||
Name
|
||||
</label>
|
||||
</div>
|
||||
<div className="py-2 space-y-6 sm:py-0 sm:space-y-0 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2"
|
||||
>
|
||||
Name
|
||||
</label>
|
||||
<Field name="name">
|
||||
{({ field, meta }: FieldProps) => (
|
||||
<div className="sm:col-span-2">
|
||||
|
@ -542,11 +538,7 @@ export function IndexerUpdateForm({ isOpen, toggle, indexer }: UpdateProps) {
|
|||
)}
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200 dark:sm:divide-gray-700">
|
||||
<SwitchGroupWide name="enabled" label="Enabled" />
|
||||
</div>
|
||||
|
||||
<SwitchGroupWide name="enabled" label="Enabled" />
|
||||
{renderSettingFields(indexer.settings)}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -24,7 +24,7 @@ const ChannelsFieldArray = ({ channels }: ChannelsFieldArrayProps) => (
|
|||
<div className="p-6">
|
||||
<FieldArray name="channels">
|
||||
{({ remove, push }) => (
|
||||
<div className="flex flex-col border-2 border-dashed dark:border-gray-700 p-4">
|
||||
<div className="flex flex-col space-y-2 border-2 border-dashed dark:border-gray-700 p-4">
|
||||
{channels && channels.length > 0 ? (
|
||||
channels.map((_channel: IrcChannel, index: number) => (
|
||||
<div key={index} className="flex justify-between">
|
||||
|
@ -68,7 +68,7 @@ const ChannelsFieldArray = ({ channels }: ChannelsFieldArrayProps) => (
|
|||
))
|
||||
) : (
|
||||
<span className="text-center text-sm text-grey-darker dark:text-white">
|
||||
No channels!
|
||||
No channels!
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
|
@ -76,7 +76,7 @@ const ChannelsFieldArray = ({ channels }: ChannelsFieldArrayProps) => (
|
|||
className="border dark:border-gray-600 dark:bg-gray-700 my-4 px-4 py-2 text-sm text-gray-700 dark:text-white hover:bg-gray-50 dark:hover:bg-gray-600 rounded self-center text-center"
|
||||
onClick={() => push({ name: "", password: "" })}
|
||||
>
|
||||
Add Channel
|
||||
Add Channel
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
@ -159,34 +159,47 @@ export function IrcNetworkAddForm({ isOpen, toggle }: AddFormProps) {
|
|||
validate={validate}
|
||||
>
|
||||
{(values) => (
|
||||
<>
|
||||
<TextFieldWide name="name" label="Name" placeholder="Name" required={true} />
|
||||
<div className="flex flex-col space-y-4 px-1 py-6 sm:py-0 sm:space-y-0">
|
||||
<TextFieldWide
|
||||
name="name"
|
||||
label="Name"
|
||||
placeholder="Name"
|
||||
required={true}
|
||||
/>
|
||||
|
||||
<div className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y dark:divide-gray-700">
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200 dark:sm:divide-gray-700">
|
||||
<SwitchGroupWide name="enabled" label="Enabled" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextFieldWide name="server" label="Server" placeholder="Address: Eg irc.server.net" required={true} />
|
||||
<NumberFieldWide name="port" label="Port" placeholder="Eg 6667" required={true} />
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<SwitchGroupWide name="tls" label="TLS" />
|
||||
</div>
|
||||
|
||||
<PasswordFieldWide name="pass" label="Password" help="Network password" />
|
||||
|
||||
<TextFieldWide name="nickserv.account" label="NickServ Account" placeholder="NickServ Account" required={true} />
|
||||
<PasswordFieldWide name="nickserv.password" label="NickServ Password" />
|
||||
|
||||
<PasswordFieldWide name="invite_command" label="Invite command" />
|
||||
</div>
|
||||
</div>
|
||||
<SwitchGroupWide name="enabled" label="Enabled" />
|
||||
<TextFieldWide
|
||||
name="server"
|
||||
label="Server"
|
||||
placeholder="Address: Eg irc.server.net"
|
||||
required={true}
|
||||
/>
|
||||
<NumberFieldWide
|
||||
name="port"
|
||||
label="Port"
|
||||
placeholder="Eg 6667"
|
||||
required={true}
|
||||
/>
|
||||
<SwitchGroupWide name="tls" label="TLS" />
|
||||
<PasswordFieldWide
|
||||
name="pass"
|
||||
label="Password"
|
||||
help="Network password"
|
||||
/>
|
||||
<TextFieldWide
|
||||
name="nickserv.account"
|
||||
label="NickServ Account"
|
||||
placeholder="NickServ Account"
|
||||
required={true}
|
||||
/>
|
||||
<PasswordFieldWide
|
||||
name="nickserv.password"
|
||||
label="NickServ Password"
|
||||
/>
|
||||
<PasswordFieldWide name="invite_command" label="Invite command" />
|
||||
|
||||
<ChannelsFieldArray channels={values.channels} />
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</SlideOver>
|
||||
);
|
||||
|
@ -290,34 +303,51 @@ export function IrcNetworkUpdateForm({
|
|||
validate={validate}
|
||||
>
|
||||
{(values) => (
|
||||
<>
|
||||
<TextFieldWide name="name" label="Name" placeholder="Name" required={true} />
|
||||
<div className="flex flex-col space-y-4 px-1 py-6 sm:py-0 sm:space-y-0">
|
||||
<TextFieldWide
|
||||
name="name"
|
||||
label="Name"
|
||||
placeholder="Name"
|
||||
required={true}
|
||||
/>
|
||||
|
||||
<div className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y dark:divide-gray-700">
|
||||
<SwitchGroupWide name="enabled" label="Enabled" />
|
||||
<TextFieldWide
|
||||
name="server"
|
||||
label="Server"
|
||||
placeholder="Address: Eg irc.server.net"
|
||||
required={true}
|
||||
/>
|
||||
<NumberFieldWide
|
||||
name="port"
|
||||
label="Port"
|
||||
placeholder="Eg 6667"
|
||||
required={true}
|
||||
/>
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0">
|
||||
<SwitchGroupWide name="enabled" label="Enabled" />
|
||||
</div>
|
||||
<SwitchGroupWide name="tls" label="TLS" />
|
||||
|
||||
<div>
|
||||
<TextFieldWide name="server" label="Server" placeholder="Address: Eg irc.server.net" required={true} />
|
||||
<NumberFieldWide name="port" label="Port" placeholder="Eg 6667" required={true} />
|
||||
<PasswordFieldWide
|
||||
name="pass"
|
||||
label="Password"
|
||||
help="Network password"
|
||||
/>
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<SwitchGroupWide name="tls" label="TLS" />
|
||||
</div>
|
||||
<TextFieldWide
|
||||
name="nickserv.account"
|
||||
label="NickServ Account"
|
||||
placeholder="NickServ Account"
|
||||
required={true}
|
||||
/>
|
||||
<PasswordFieldWide
|
||||
name="nickserv.password"
|
||||
label="NickServ Password"
|
||||
/>
|
||||
|
||||
<PasswordFieldWide name="pass" label="Password" help="Network password" />
|
||||
|
||||
<TextFieldWide name="nickserv.account" label="NickServ Account" placeholder="NickServ Account" required={true} />
|
||||
<PasswordFieldWide name="nickserv.password" label="NickServ Password" />
|
||||
|
||||
<PasswordFieldWide name="invite_command" label="Invite command" />
|
||||
</div>
|
||||
</div>
|
||||
<PasswordFieldWide name="invite_command" label="Invite command" />
|
||||
|
||||
<ChannelsFieldArray channels={values.channels} />
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</SlideOver>
|
||||
);
|
||||
|
|
|
@ -62,11 +62,11 @@ const Option = (props: OptionProps) => {
|
|||
|
||||
function FormFieldsDiscord() {
|
||||
return (
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 py-5">
|
||||
<div className="px-6 space-y-1">
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 py-4">
|
||||
<div className="px-4 space-y-1">
|
||||
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">Settings</Dialog.Title>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Create a <a href="https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks" rel="noopener noreferrer" target="_blank" className="font-medium text-blue-500">webhook integration</a> in your server.
|
||||
Create a <a href="https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks" rel="noopener noreferrer" target="_blank" className="font-medium text-blue-500 underline underline-offset-1 hover:text-blue-400">webhook integration</a> in your server.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -82,11 +82,11 @@ function FormFieldsDiscord() {
|
|||
|
||||
function FormFieldsTelegram() {
|
||||
return (
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 py-5">
|
||||
<div className="px-6 space-y-1">
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 py-4">
|
||||
<div className="px-4 space-y-1">
|
||||
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">Settings</Dialog.Title>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Read how to <a href="https://core.telegram.org/bots#3-how-do-i-create-a-bot" rel="noopener noreferrer" target="_blank" className="font-medium text-blue-500">create a bot</a>.
|
||||
Read how to <a href="https://core.telegram.org/bots#3-how-do-i-create-a-bot" rel="noopener noreferrer" target="_blank" className="font-medium text-blue-500 underline underline-offset-1 hover:text-blue-400">create a bot</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -161,9 +161,15 @@ export function NotificationAddForm({ isOpen, toggle }: AddProps) {
|
|||
|
||||
return (
|
||||
<Transition.Root show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" static className="fixed inset-0 overflow-hidden" open={isOpen} onClose={toggle}>
|
||||
<Dialog
|
||||
as="div"
|
||||
static
|
||||
className="fixed inset-0 overflow-hidden"
|
||||
open={isOpen}
|
||||
onClose={toggle}
|
||||
>
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<Dialog.Overlay className="absolute inset-0"/>
|
||||
<Dialog.Overlay className="absolute inset-0" />
|
||||
|
||||
<div className="fixed inset-y-0 right-0 pl-10 max-w-full flex sm:pl-16">
|
||||
<Transition.Child
|
||||
|
@ -208,16 +214,20 @@ export function NotificationAddForm({ isOpen, toggle }: AddProps) {
|
|||
onClick={toggle}
|
||||
>
|
||||
<span className="sr-only">Close panel</span>
|
||||
<XIcon className="h-6 w-6" aria-hidden="true"/>
|
||||
<XIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TextFieldWide name="name" label="Name" required={true}/>
|
||||
<div className="flex flex-col space-y-4 px-1 py-6 sm:py-0 sm:space-y-0">
|
||||
<TextFieldWide
|
||||
name="name"
|
||||
label="Name"
|
||||
required={true}
|
||||
/>
|
||||
|
||||
<div className="space-y-4 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="py-4 flex items-center justify-between space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
||||
<div className="flex items-center justify-between space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="type"
|
||||
|
@ -232,10 +242,16 @@ export function NotificationAddForm({ isOpen, toggle }: AddProps) {
|
|||
field,
|
||||
form: { setFieldValue, resetForm }
|
||||
}: FieldProps) => (
|
||||
<Select {...field}
|
||||
<Select
|
||||
{...field}
|
||||
isClearable={true}
|
||||
isSearchable={true}
|
||||
components={{ Input, Control, Menu, Option }}
|
||||
components={{
|
||||
Input,
|
||||
Control,
|
||||
Menu,
|
||||
Option
|
||||
}}
|
||||
placeholder="Choose a type"
|
||||
styles={{
|
||||
singleValue: (base) => ({
|
||||
|
@ -257,39 +273,39 @@ export function NotificationAddForm({ isOpen, toggle }: AddProps) {
|
|||
|
||||
const opt = option as SelectOption;
|
||||
// setFieldValue("name", option?.label ?? "")
|
||||
setFieldValue(field.name, opt.value ?? "");
|
||||
setFieldValue(
|
||||
field.name,
|
||||
opt.value ?? ""
|
||||
);
|
||||
}}
|
||||
options={NotificationTypeOptions}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<SwitchGroupWide name="enabled" label="Enabled"/>
|
||||
</div>
|
||||
<SwitchGroupWide name="enabled" label="Enabled" />
|
||||
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 py-5">
|
||||
<div className="px-6 space-y-1">
|
||||
<Dialog.Title
|
||||
className="text-lg font-medium text-gray-900 dark:text-white">Events</Dialog.Title>
|
||||
<div className="border-t mt-2 border-gray-200 dark:border-gray-700 py-4">
|
||||
<div className="px-4 space-y-1">
|
||||
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">
|
||||
Events
|
||||
</Dialog.Title>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Select what events to trigger on
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:gap-4 sm:px-6 sm:py-5">
|
||||
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:gap-4 sm:py-4">
|
||||
<EventCheckBoxes />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{componentMap[values.type]}
|
||||
</div>
|
||||
|
||||
<div className="flex-shrink-0 px-4 border-t border-gray-200 dark:border-gray-700 py-5 sm:px-6">
|
||||
<div className="flex-shrink-0 px-4 border-t border-gray-200 dark:border-gray-700 py-4 sm:px-6">
|
||||
<div className="space-x-3 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
|
@ -314,12 +330,11 @@ export function NotificationAddForm({ isOpen, toggle }: AddProps) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<DEBUG values={values}/>
|
||||
<DEBUG values={values} />
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -432,8 +447,8 @@ export function NotificationUpdateForm({ isOpen, toggle, notification }: UpdateP
|
|||
<div>
|
||||
<TextFieldWide name="name" label="Name" required={true}/>
|
||||
|
||||
<div className="space-y-4 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="py-4 flex items-center justify-between space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
|
||||
<div className="space-y-2 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="py-4 flex items-center justify-between space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="type"
|
||||
|
@ -477,13 +492,9 @@ export function NotificationUpdateForm({ isOpen, toggle, notification }: UpdateP
|
|||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
|
||||
<SwitchGroupWide name="enabled" label="Enabled"/>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 py-5">
|
||||
<div className="px-6 space-y-1">
|
||||
<SwitchGroupWide name="enabled" label="Enabled"/>
|
||||
<div className="border-t border-gray-200 dark:border-gray-700 py-4">
|
||||
<div className="px-4 space-y-1">
|
||||
<Dialog.Title
|
||||
className="text-lg font-medium text-gray-900 dark:text-white">Events</Dialog.Title>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
|
@ -491,7 +502,7 @@ export function NotificationUpdateForm({ isOpen, toggle, notification }: UpdateP
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:gap-4 sm:px-6 sm:py-5">
|
||||
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:gap-4 sm:py-2">
|
||||
<EventCheckBoxes />
|
||||
</div>
|
||||
</div>
|
|
@ -46,7 +46,7 @@ function SubNavLink({ item }: NavLinkProps) {
|
|||
className={({ isActive }) => classNames(
|
||||
"border-transparent text-gray-900 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-gray-300 group border-l-4 px-3 py-2 flex items-center text-sm font-medium",
|
||||
isActive ?
|
||||
"bg-teal-50 dark:bg-gray-700 border-teal-500 dark:border-blue-500 text-teal-700 dark:text-white hover:bg-teal-50 dark:hover:bg-gray-500 hover:text-teal-700 dark:hover:text-gray-200" : ""
|
||||
"font-bold bg-teal-50 dark:bg-gray-700 border-teal-500 dark:border-blue-500 text-teal-700 dark:text-white hover:bg-teal-50 dark:hover:bg-gray-500 hover:text-teal-700 dark:hover:text-gray-200" : ""
|
||||
)}
|
||||
aria-current={splitLocation[2] === item.href ? "page" : undefined}
|
||||
>
|
||||
|
|
|
@ -609,6 +609,266 @@ export function FilterActions({ filter, values }: FilterActionsProps) {
|
|||
);
|
||||
}
|
||||
|
||||
interface TypeFormProps {
|
||||
action: Action;
|
||||
idx: number;
|
||||
clients: Array<DownloadClient>;
|
||||
}
|
||||
|
||||
const TypeForm = ({ action, idx, clients }: TypeFormProps) => {
|
||||
switch (action.type) {
|
||||
case "TEST":
|
||||
return (
|
||||
<AlertWarning
|
||||
text="The test action does nothing except to show if the filter works."
|
||||
/>
|
||||
);
|
||||
case "EXEC":
|
||||
return (
|
||||
<div>
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.exec_cmd`}
|
||||
label="Command"
|
||||
columns={6}
|
||||
placeholder="Path to program eg. /bin/test"
|
||||
/>
|
||||
<TextField
|
||||
name={`actions.${idx}.exec_args`}
|
||||
label="Arguments"
|
||||
columns={6}
|
||||
placeholder="Arguments eg. --test"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case "WATCH_FOLDER":
|
||||
return (
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.watch_folder`}
|
||||
label="Watch folder"
|
||||
columns={6}
|
||||
placeholder="Watch directory eg. /home/user/rwatch"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case "WEBHOOK":
|
||||
return (
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.webhook_host`}
|
||||
label="Host"
|
||||
columns={6}
|
||||
placeholder="Host eg. http://localhost/webhook"
|
||||
/>
|
||||
<TextField
|
||||
name={`actions.${idx}.webhook_data`}
|
||||
label="Data (json)"
|
||||
columns={6}
|
||||
placeholder={"Request data: { \"key\": \"value\" }"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case "QBITTORRENT":
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<DownloadClientSelect
|
||||
name={`actions.${idx}.client_id`}
|
||||
action={action}
|
||||
clients={clients}
|
||||
/>
|
||||
|
||||
<div className="col-span-6 sm:col-span-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.save_path`}
|
||||
label="Save path"
|
||||
columns={6}
|
||||
placeholder="eg. /full/path/to/watch_folder"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.category`}
|
||||
label="Category"
|
||||
columns={6}
|
||||
placeholder="eg. category"
|
||||
/>
|
||||
<TextField
|
||||
name={`actions.${idx}.tags`}
|
||||
label="Tags"
|
||||
columns={6}
|
||||
placeholder="eg. tag1,tag2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CollapsableSection title="Rules" subtitle="client options">
|
||||
<div className="col-span-12">
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_download_speed`}
|
||||
label="Limit download speed (KB/s)"
|
||||
/>
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_upload_speed`}
|
||||
label="Limit upload speed (KB/s)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_ratio`}
|
||||
label="Ratio limit"
|
||||
step={0.5}
|
||||
/>
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_seed_time`}
|
||||
label="Seed time limit (seconds)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-6">
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.paused`}
|
||||
label="Add paused"
|
||||
description="Add torrent as paused"
|
||||
/>
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.ignore_rules`}
|
||||
label="Ignore client rules"
|
||||
description="Download if max active reached"
|
||||
/>
|
||||
</div>
|
||||
</CollapsableSection>
|
||||
|
||||
<CollapsableSection title="Advanced" subtitle="Advanced options">
|
||||
<div className="col-span-12">
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<NumberField
|
||||
name={`actions.${idx}.reannounce_interval`}
|
||||
label="Reannounce interval. Run every X seconds"
|
||||
/>
|
||||
<NumberField
|
||||
name={`actions.${idx}.reannounce_max_attempts`}
|
||||
label="Run reannounce Y times"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-6">
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.reannounce_skip`}
|
||||
label="Skip reannounce"
|
||||
description="If reannounce is not needed, skip"
|
||||
/>
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.reannounce_delete`}
|
||||
label="Delete stalled"
|
||||
description="Delete stalled torrents after X attempts"
|
||||
/>
|
||||
</div>
|
||||
</CollapsableSection>
|
||||
</div>
|
||||
);
|
||||
case "DELUGE_V1":
|
||||
case "DELUGE_V2":
|
||||
return (
|
||||
<div>
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<DownloadClientSelect
|
||||
name={`actions.${idx}.client_id`}
|
||||
action={action}
|
||||
clients={clients}
|
||||
/>
|
||||
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.save_path`}
|
||||
label="Save path"
|
||||
columns={6}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 col-span-12 sm:col-span-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.label`}
|
||||
label="Label"
|
||||
columns={6}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_download_speed`}
|
||||
label="Limit download speed (KB/s)"
|
||||
/>
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_upload_speed`}
|
||||
label="Limit upload speed (KB/s)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<div className="col-span-6">
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.paused`}
|
||||
label="Add paused"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case "TRANSMISSION":
|
||||
return (
|
||||
<div>
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<DownloadClientSelect
|
||||
name={`actions.${idx}.client_id`}
|
||||
action={action}
|
||||
clients={clients}
|
||||
/>
|
||||
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.save_path`}
|
||||
label="Save path"
|
||||
columns={6}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<div className="col-span-6">
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.paused`}
|
||||
label="Add paused"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case "RADARR":
|
||||
case "SONARR":
|
||||
case "LIDARR":
|
||||
case "WHISPARR":
|
||||
return (
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<DownloadClientSelect
|
||||
name={`actions.${idx}.client_id`}
|
||||
action={action}
|
||||
clients={clients}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
interface FilterActionsItemProps {
|
||||
action: Action;
|
||||
clients: DownloadClient[];
|
||||
|
@ -617,255 +877,10 @@ interface FilterActionsItemProps {
|
|||
}
|
||||
|
||||
function FilterActionsItem({ action, clients, idx, remove }: FilterActionsItemProps) {
|
||||
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false);
|
||||
const [edit, toggleEdit] = useToggle(false);
|
||||
|
||||
const cancelButtonRef = useRef(null);
|
||||
|
||||
const TypeForm = (actionType: ActionType) => {
|
||||
switch (actionType) {
|
||||
case "TEST":
|
||||
return (
|
||||
<AlertWarning
|
||||
title="Notice"
|
||||
text="The test action does nothing except to show if the filter works."
|
||||
/>
|
||||
);
|
||||
case "EXEC":
|
||||
return (
|
||||
<div>
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.exec_cmd`}
|
||||
label="Command"
|
||||
columns={6}
|
||||
placeholder="Path to program eg. /bin/test"
|
||||
/>
|
||||
<TextField
|
||||
name={`actions.${idx}.exec_args`}
|
||||
label="Arguments"
|
||||
columns={6}
|
||||
placeholder="Arguments eg. --test"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case "WATCH_FOLDER":
|
||||
return (
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.watch_folder`}
|
||||
label="Watch folder"
|
||||
columns={6}
|
||||
placeholder="Watch directory eg. /home/user/rwatch"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case "WEBHOOK":
|
||||
return (
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.webhook_host`}
|
||||
label="Host"
|
||||
columns={6}
|
||||
placeholder="Host eg. http://localhost/webhook"
|
||||
/>
|
||||
<TextField
|
||||
name={`actions.${idx}.webhook_data`}
|
||||
label="Data (json)"
|
||||
columns={6}
|
||||
placeholder={"Request data: { \"key\": \"value\" }"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case "QBITTORRENT":
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<DownloadClientSelect
|
||||
name={`actions.${idx}.client_id`}
|
||||
action={action}
|
||||
clients={clients}
|
||||
/>
|
||||
|
||||
<div className="col-span-6 sm:col-span-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.save_path`}
|
||||
label="Save path"
|
||||
columns={6}
|
||||
placeholder="eg. /full/path/to/watch_folder"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<TextField name={`actions.${idx}.category`} label="Category" columns={6} placeholder="eg. category" />
|
||||
<TextField name={`actions.${idx}.tags`} label="Tags" columns={6} placeholder="eg. tag1,tag2" />
|
||||
</div>
|
||||
|
||||
<CollapsableSection title="Rules" subtitle="client options">
|
||||
<div className="col-span-12">
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_download_speed`}
|
||||
label="Limit download speed (KB/s)"
|
||||
/>
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_upload_speed`}
|
||||
label="Limit upload speed (KB/s)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_ratio`}
|
||||
label="Ratio limit"
|
||||
step={0.5}
|
||||
/>
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_seed_time`}
|
||||
label="Seed time limit (seconds)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-6">
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.paused`}
|
||||
label="Add paused"
|
||||
description="Add torrent as paused"
|
||||
/>
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.ignore_rules`}
|
||||
label="Ignore client rules"
|
||||
description="Download if max active reached"
|
||||
/>
|
||||
</div>
|
||||
</CollapsableSection>
|
||||
|
||||
<CollapsableSection title="Advanced" subtitle="Advanced options">
|
||||
<div className="col-span-12">
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<NumberField
|
||||
name={`actions.${idx}.reannounce_interval`}
|
||||
label="Reannounce interval. Run every X seconds"
|
||||
/>
|
||||
<NumberField
|
||||
name={`actions.${idx}.reannounce_max_attempts`}
|
||||
label="Run reannounce Y times"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-6">
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.reannounce_skip`}
|
||||
label="Skip reannounce"
|
||||
description="If reannounce is not needed, skip"
|
||||
/>
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.reannounce_delete`}
|
||||
label="Delete stalled"
|
||||
description="Delete stalled torrents after X attempts"
|
||||
/>
|
||||
</div>
|
||||
</CollapsableSection>
|
||||
</div>
|
||||
);
|
||||
case "DELUGE_V1":
|
||||
case "DELUGE_V2":
|
||||
return (
|
||||
<div>
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<DownloadClientSelect
|
||||
name={`actions.${idx}.client_id`}
|
||||
action={action}
|
||||
clients={clients}
|
||||
/>
|
||||
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.save_path`}
|
||||
label="Save path"
|
||||
columns={6}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 col-span-12 sm:col-span-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.label`}
|
||||
label="Label"
|
||||
columns={6}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_download_speed`}
|
||||
label="Limit download speed (KB/s)"
|
||||
/>
|
||||
<NumberField
|
||||
name={`actions.${idx}.limit_upload_speed`}
|
||||
label="Limit upload speed (KB/s)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<div className="col-span-6">
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.paused`}
|
||||
label="Add paused"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case "TRANSMISSION":
|
||||
return (
|
||||
<div>
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<DownloadClientSelect
|
||||
name={`actions.${idx}.client_id`}
|
||||
action={action}
|
||||
clients={clients}
|
||||
/>
|
||||
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
<TextField
|
||||
name={`actions.${idx}.save_path`}
|
||||
label="Save path"
|
||||
columns={6}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<div className="col-span-6">
|
||||
<SwitchGroup
|
||||
name={`actions.${idx}.paused`}
|
||||
label="Add paused"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case "RADARR":
|
||||
case "SONARR":
|
||||
case "LIDARR":
|
||||
case "WHISPARR":
|
||||
return (
|
||||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<DownloadClientSelect
|
||||
name={`actions.${idx}.client_id`}
|
||||
action={action}
|
||||
clients={clients}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false);
|
||||
const [edit, toggleEdit] = useToggle(false);
|
||||
|
||||
return (
|
||||
<li>
|
||||
|
@ -966,7 +981,7 @@ function FilterActionsItem({ action, clients, idx, remove }: FilterActionsItemPr
|
|||
<TextField name={`actions.${idx}.name`} label="Name" columns={6} />
|
||||
</div>
|
||||
|
||||
{TypeForm(action.type)}
|
||||
<TypeForm action={action} clients={clients} idx={idx} />
|
||||
|
||||
<div className="pt-6 divide-y divide-gray-200">
|
||||
<div className="mt-4 pt-4 flex justify-between">
|
||||
|
|
|
@ -72,7 +72,7 @@ interface FilterListProps {
|
|||
|
||||
function FilterList({ filters }: FilterListProps) {
|
||||
return (
|
||||
<div className="overflow-x-auto align-middle min-w-full rounded-lg shadow-lg">
|
||||
<div className="overflow-x-auto align-middle min-w-full rounded-t-md rounded-b-lg shadow-lg">
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-gray-50 dark:bg-gray-800 text-gray-500 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700">
|
||||
<tr>
|
||||
|
@ -80,7 +80,7 @@ function FilterList({ filters }: FilterListProps) {
|
|||
<th
|
||||
key={`th-${label}`}
|
||||
scope="col"
|
||||
className="px-6 py-2.5 text-left text-xs font-medium uppercase tracking-wider"
|
||||
className="px-6 pt-4 pb-3 text-left text-xs font-medium uppercase tracking-wider"
|
||||
>
|
||||
{label}
|
||||
</th>
|
||||
|
|
|
@ -1,26 +1,20 @@
|
|||
function ActionSettings() {
|
||||
return (
|
||||
<div className="divide-y divide-gray-200 lg:col-span-9">
|
||||
|
||||
|
||||
<div className="lg:col-span-9">
|
||||
<div className="py-6 px-4 sm:p-6 lg:pb-8">
|
||||
{/*{addClientIsOpen &&*/}
|
||||
{/*<AddNewClientForm isOpen={addClientIsOpen} toggle={toggleAddClient}/>*/}
|
||||
{/*}*/}
|
||||
<div className="-ml-4 -mt-4 flex justify-between items-center flex-wrap sm:flex-nowrap">
|
||||
<div className="ml-4 mt-4">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Actions</h3>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Manage actions.
|
||||
Manage actions.
|
||||
</p>
|
||||
</div>
|
||||
<div className="ml-4 mt-4 flex-shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
||||
// onClick={toggleAddClient}
|
||||
>
|
||||
Add new
|
||||
Add new
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -36,25 +30,25 @@ function ActionSettings() {
|
|||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Name
|
||||
Name
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Type
|
||||
Type
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Port
|
||||
Port
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Enabled
|
||||
Enabled
|
||||
</th>
|
||||
<th scope="col" className="relative px-6 py-3">
|
||||
<span className="sr-only">Edit</span>
|
||||
|
|
|
@ -23,7 +23,7 @@ function ApplicationSettings() {
|
|||
<div>
|
||||
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Application</h2>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Application settings. Change in config.toml and restart to take effect.
|
||||
Application settings. Change in config.toml and restart to take effect.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -31,7 +31,7 @@ function ApplicationSettings() {
|
|||
<div className="mt-6 grid grid-cols-12 gap-6">
|
||||
<div className="col-span-6 sm:col-span-4">
|
||||
<label htmlFor="host" className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
Host
|
||||
Host
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
|
@ -39,13 +39,13 @@ function ApplicationSettings() {
|
|||
id="host"
|
||||
value={data.host}
|
||||
disabled={true}
|
||||
className="mt-2 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:text-gray-100 sm:text-sm"
|
||||
className="mt-2 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:text-gray-100 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-6 sm:col-span-4">
|
||||
<label htmlFor="port" className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
Port
|
||||
Port
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
|
@ -53,13 +53,13 @@ function ApplicationSettings() {
|
|||
id="port"
|
||||
value={data.port}
|
||||
disabled={true}
|
||||
className="mt-2 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:text-gray-100 sm:text-sm"
|
||||
className="mt-2 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:text-gray-100 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-6 sm:col-span-4">
|
||||
<label htmlFor="base_url" className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
|
||||
Base url
|
||||
Base url
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
|
@ -67,7 +67,7 @@ function ApplicationSettings() {
|
|||
id="base_url"
|
||||
value={data.base_url}
|
||||
disabled={true}
|
||||
className="mt-2 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:text-gray-100 sm:text-sm"
|
||||
className="mt-2 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:text-gray-100 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -80,19 +80,21 @@ function ApplicationSettings() {
|
|||
{data?.version ? (
|
||||
<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">Version:</dt>
|
||||
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2">{data?.version}</dd>
|
||||
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2 break-all">
|
||||
{data?.version}
|
||||
</dd>
|
||||
</div>
|
||||
) : null}
|
||||
{data?.commit ? (
|
||||
<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">Commit:</dt>
|
||||
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2">{data.commit}</dd>
|
||||
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2 break-all">{data.commit}</dd>
|
||||
</div>
|
||||
) : null}
|
||||
{data?.date ? (
|
||||
<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">Date:</dt>
|
||||
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2">{data?.date}</dd>
|
||||
<dd className="mt-1 text-gray-900 dark:text-white sm:mt-0 sm:col-span-2 break-all">{data?.date}</dd>
|
||||
</div>
|
||||
) : null}
|
||||
</dl>
|
||||
|
@ -123,7 +125,6 @@ function ApplicationSettings() {
|
|||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ function DownloadClientSettings() {
|
|||
return (<p>An error has occurred: </p>);
|
||||
|
||||
return (
|
||||
<div className="divide-y divide-gray-200 lg:col-span-9">
|
||||
<div className="lg:col-span-9">
|
||||
|
||||
<DownloadClientAddForm isOpen={addClientIsOpen} toggle={toggleAddClient} />
|
||||
|
||||
|
@ -121,25 +121,25 @@ function DownloadClientSettings() {
|
|||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"
|
||||
>
|
||||
Enabled
|
||||
Enabled
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"
|
||||
>
|
||||
Name
|
||||
Name
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"
|
||||
>
|
||||
Host
|
||||
Host
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"
|
||||
>
|
||||
Type
|
||||
Type
|
||||
</th>
|
||||
<th scope="col" className="relative px-6 py-3">
|
||||
<span className="sr-only">Edit</span>
|
||||
|
|
|
@ -27,13 +27,13 @@ function FeedSettings() {
|
|||
);
|
||||
|
||||
return (
|
||||
<div className="divide-y divide-gray-200 lg:col-span-9">
|
||||
<div className="lg:col-span-9">
|
||||
<div className="py-6 px-4 sm:p-6 lg:pb-8">
|
||||
<div className="-ml-4 -mt-4 flex justify-between items-center flex-wrap sm:flex-nowrap">
|
||||
<div className="ml-4 mt-4">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Feeds</h3>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Manage Torznab feeds.
|
||||
Manage Torznab feeds.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -82,7 +82,7 @@ function IndexerSettings() {
|
|||
return (<p>An error has occurred</p>);
|
||||
|
||||
return (
|
||||
<div className="divide-y divide-gray-200 lg:col-span-9">
|
||||
<div className="lg:col-span-9">
|
||||
|
||||
<IndexerAddForm isOpen={addIndexerIsOpen} toggle={toggleAddIndexer} />
|
||||
|
||||
|
|
|
@ -1,41 +1,35 @@
|
|||
import { useQuery } from "react-query";
|
||||
|
||||
import {
|
||||
simplifyDate,
|
||||
IsEmptyDate
|
||||
} from "../../utils";
|
||||
import {
|
||||
IrcNetworkAddForm,
|
||||
IrcNetworkUpdateForm
|
||||
} from "../../forms";
|
||||
import { simplifyDate, IsEmptyDate, classNames } from "../../utils";
|
||||
import { IrcNetworkAddForm, IrcNetworkUpdateForm } from "../../forms";
|
||||
import { useToggle } from "../../hooks/hooks";
|
||||
import { APIClient } from "../../api/APIClient";
|
||||
import { EmptySimple } from "../../components/emptystates";
|
||||
import {ExclamationCircleIcon} from "@heroicons/react/outline";
|
||||
import { ExclamationCircleIcon } from "@heroicons/react/outline";
|
||||
import { LockClosedIcon, LockOpenIcon } from "@heroicons/react/solid";
|
||||
|
||||
export const IrcSettings = () => {
|
||||
const [addNetworkIsOpen, toggleAddNetwork] = useToggle(false);
|
||||
|
||||
const { data } = useQuery(
|
||||
"networks",
|
||||
() => APIClient.irc.getNetworks(),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
// Refetch every 3 seconds
|
||||
refetchInterval: 3000
|
||||
}
|
||||
);
|
||||
const { data } = useQuery("networks", () => APIClient.irc.getNetworks(), {
|
||||
refetchOnWindowFocus: false,
|
||||
// Refetch every 3 seconds
|
||||
refetchInterval: 3000
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="divide-y divide-gray-200 lg:col-span-9">
|
||||
<div className="lg:col-span-9">
|
||||
<IrcNetworkAddForm isOpen={addNetworkIsOpen} toggle={toggleAddNetwork} />
|
||||
|
||||
<div className="py-6 px-4 sm:p-6 lg:pb-8">
|
||||
<div className="-ml-4 -mt-4 flex justify-between items-center flex-wrap sm:flex-nowrap">
|
||||
<div className="ml-4 mt-4">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">IRC</h3>
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">
|
||||
IRC
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
IRC networks and channels. Click on a network to view channel status.
|
||||
IRC networks and channels. Click on a network to view channel
|
||||
status.
|
||||
</p>
|
||||
</div>
|
||||
<div className="ml-4 mt-4 flex-shrink-0">
|
||||
|
@ -44,35 +38,46 @@ export const IrcSettings = () => {
|
|||
onClick={toggleAddNetwork}
|
||||
className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 dark:bg-blue-600 hover:bg-indigo-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
||||
>
|
||||
Add new
|
||||
Add new
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{data && data.length > 0 ? (
|
||||
<section className="mt-6 light:bg-white dark:bg-gray-800 light:shadow sm:rounded-md">
|
||||
<ol className="min-w-full">
|
||||
<li className="grid grid-cols-12 gap-4 border-b border-gray-200 dark:border-gray-700">
|
||||
{/* <div className="col-span-1 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Enabled</div> */}
|
||||
<div className="col-span-3 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Network</div>
|
||||
<div className="col-span-4 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Server</div>
|
||||
<div className="col-span-4 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Nick</div>
|
||||
<div className="col-span-3 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Network
|
||||
</div>
|
||||
<div className="col-span-5 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Server
|
||||
</div>
|
||||
<div className="col-span-3 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Nick
|
||||
</div>
|
||||
</li>
|
||||
|
||||
{data && data.map((network, idx) => (
|
||||
<ListItem key={idx} idx={idx} network={network} />
|
||||
))}
|
||||
{data &&
|
||||
data.map((network, idx) => (
|
||||
<ListItem key={idx} idx={idx} network={network} />
|
||||
))}
|
||||
</ol>
|
||||
</section>
|
||||
) : <EmptySimple title="No networks" subtitle="Add a new network" buttonText="New network" buttonAction={toggleAddNetwork} />}
|
||||
) : (
|
||||
<EmptySimple
|
||||
title="No networks"
|
||||
subtitle="Add a new network"
|
||||
buttonText="New network"
|
||||
buttonAction={toggleAddNetwork}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface ListItemProps {
|
||||
idx: number;
|
||||
network: IrcNetworkWithHealth;
|
||||
idx: number;
|
||||
network: IrcNetworkWithHealth;
|
||||
}
|
||||
|
||||
const ListItem = ({ idx, network }: ListItemProps) => {
|
||||
|
@ -80,33 +85,91 @@ const ListItem = ({ idx, network }: ListItemProps) => {
|
|||
const [edit, toggleEdit] = useToggle(false);
|
||||
|
||||
return (
|
||||
<li key={idx}>
|
||||
<div className="grid grid-cols-12 gap-2 lg:gap-4 items-center hover:bg-gray-50 dark:hover:bg-gray-700 py-4">
|
||||
<IrcNetworkUpdateForm
|
||||
isOpen={updateIsOpen}
|
||||
toggle={toggleUpdate}
|
||||
network={network}
|
||||
/>
|
||||
|
||||
<li key={idx} >
|
||||
<div className="grid grid-cols-12 gap-4 items-center hover:bg-gray-50 dark:hover:bg-gray-700 py-4">
|
||||
<IrcNetworkUpdateForm isOpen={updateIsOpen} toggle={toggleUpdate} network={network} />
|
||||
|
||||
<div className="col-span-3 items-center sm:px-6 text-sm font-medium text-gray-900 dark:text-white cursor-pointer" onClick={toggleEdit}>
|
||||
<span className="relative inline-flex items-center">
|
||||
{
|
||||
network.enabled ? (
|
||||
networkHealthy(network) ? (
|
||||
<span className="mr-3 flex h-3 w-3 relative" title={`Connected since: ${simplifyDate(network.connected_since)}`}>
|
||||
<span className="animate-ping inline-flex h-full w-full rounded-full bg-green-400 opacity-75"/>
|
||||
<span className="inline-flex absolute rounded-full h-3 w-3 bg-green-500"/>
|
||||
<div
|
||||
className="col-span-3 items-center sm:px-6 text-sm font-medium text-gray-900 dark:text-white cursor-pointer"
|
||||
onClick={toggleEdit}
|
||||
>
|
||||
<div className="flex">
|
||||
<span className="relative inline-flex items-center ml-1">
|
||||
{network.enabled ? (
|
||||
IsNetworkHealthy(network) ? (
|
||||
<span
|
||||
className="mr-3 flex h-3 w-3 relative"
|
||||
title={`Connected since: ${simplifyDate(network.connected_since)}`}
|
||||
>
|
||||
<span className="animate-ping inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
|
||||
<span className="inline-flex absolute rounded-full h-3 w-3 bg-green-500" />
|
||||
</span>
|
||||
) : <span className="mr-3 flex items-center" title={network.connection_errors.toString()}><ExclamationCircleIcon className="h-4 w-4 text-red-400 hover:text-red-600" /></span>
|
||||
) : <span className="mr-3 flex h-3 w-3 rounded-full opacity-75 bg-gray-500" />
|
||||
}
|
||||
{network.name}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
className="mr-3 flex items-center"
|
||||
title={network.connection_errors.toString()}
|
||||
>
|
||||
<ExclamationCircleIcon className="h-4 w-4 text-red-400 hover:text-red-600" />
|
||||
</span>
|
||||
)
|
||||
) : (
|
||||
<span className="mr-3 flex h-3 w-3 rounded-full opacity-75 bg-gray-500" />
|
||||
)}
|
||||
</span>
|
||||
<div className="overflow-x-auto flex">
|
||||
{network.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="col-span-5 sm:px-6 text-sm text-gray-500 dark:text-gray-400 cursor-pointer"
|
||||
onClick={toggleEdit}
|
||||
>
|
||||
<div
|
||||
className="overflow-x-auto flex items-center"
|
||||
title={network.tls ? "Secured using TLS" : "Insecure, not using TLS"}
|
||||
>
|
||||
<div className="min-h-2 min-w-2">
|
||||
{network.tls ? (
|
||||
<LockClosedIcon
|
||||
className={classNames(
|
||||
"mr-2 h-4 w-4",
|
||||
network.enabled ? "text-green-600" : "text-gray-500"
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<LockOpenIcon className={classNames(
|
||||
"mr-2 h-4 w-4",
|
||||
network.enabled ? "text-red-500" : "text-yellow-500"
|
||||
)} />
|
||||
)}
|
||||
</div>
|
||||
<p className="break-all">
|
||||
{network.server}:{network.port}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-span-4 flex justify-between items-center sm:px-6 text-sm text-gray-500 dark:text-gray-400 cursor-pointer" onClick={toggleEdit}>{network.server}:{network.port} {network.tls && <span className="ml-2 inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 dark:bg-green-300 text-green-800 dark:text-green-900">TLS</span>}</div>
|
||||
{network.nickserv && network.nickserv.account ? (
|
||||
<div className="col-span-4 items-center sm:px-6 text-sm text-gray-500 dark:text-gray-400 cursor-pointer" onClick={toggleEdit}>{network.nickserv.account}</div>
|
||||
) : <div className="col-span-4" />}
|
||||
<div
|
||||
className="col-span-3 items-center sm:px-6 text-sm text-gray-500 dark:text-gray-400 cursor-pointer"
|
||||
onClick={toggleEdit}
|
||||
>
|
||||
<div className="overflow-x-auto flex">
|
||||
{network.nickserv.account}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="col-span-3" />
|
||||
)}
|
||||
<div className="col-span-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
<span className="text-indigo-600 dark:text-gray-300 hover:text-indigo-900 cursor-pointer" onClick={toggleUpdate}>
|
||||
<span
|
||||
className="text-indigo-600 dark:text-gray-300 hover:text-indigo-900 cursor-pointer"
|
||||
onClick={toggleUpdate}
|
||||
>
|
||||
Edit
|
||||
</span>
|
||||
</div>
|
||||
|
@ -117,39 +180,58 @@ const ListItem = ({ idx, network }: ListItemProps) => {
|
|||
{network.channels.length > 0 ? (
|
||||
<ol>
|
||||
<li className="grid grid-cols-12 gap-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="col-span-4 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Channel</div>
|
||||
<div className="col-span-4 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Monitoring since</div>
|
||||
<div className="col-span-4 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Last announce</div>
|
||||
<div className="col-span-4 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Channel
|
||||
</div>
|
||||
<div className="col-span-4 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Monitoring since
|
||||
</div>
|
||||
<div className="col-span-4 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Last announce
|
||||
</div>
|
||||
</li>
|
||||
{network.channels.map(c => (
|
||||
{network.channels.map((c) => (
|
||||
<li key={c.id} className="text-gray-500 dark:text-gray-400">
|
||||
<div className="grid grid-cols-12 gap-4 items-center py-4">
|
||||
<div className="col-span-4 flex items-center sm:px-6 ">
|
||||
<span className="relative inline-flex items-center">
|
||||
{
|
||||
network.enabled ? (
|
||||
c.monitoring ? (
|
||||
<span className="mr-3 flex h-3 w-3 relative" title="monitoring">
|
||||
<span className="animate-ping inline-flex h-full w-full rounded-full bg-green-400 opacity-75"/>
|
||||
<span className="inline-flex absolute rounded-full h-3 w-3 bg-green-500"/>
|
||||
</span>
|
||||
) : <span className="mr-3 flex h-3 w-3 rounded-full opacity-75 bg-red-400" />
|
||||
) : <span className="mr-3 flex h-3 w-3 rounded-full opacity-75 bg-gray-500" />
|
||||
}
|
||||
{network.enabled ? (
|
||||
c.monitoring ? (
|
||||
<span
|
||||
className="mr-3 flex h-3 w-3 relative"
|
||||
title="monitoring"
|
||||
>
|
||||
<span className="animate-ping inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
|
||||
<span className="inline-flex absolute rounded-full h-3 w-3 bg-green-500" />
|
||||
</span>
|
||||
) : (
|
||||
<span className="mr-3 flex h-3 w-3 rounded-full opacity-75 bg-red-400" />
|
||||
)
|
||||
) : (
|
||||
<span className="mr-3 flex h-3 w-3 rounded-full opacity-75 bg-gray-500" />
|
||||
)}
|
||||
{c.name}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-span-4 flex items-center sm:px-6 ">
|
||||
<span className="" title={simplifyDate(c.monitoring_since)}>{IsEmptyDate(c.monitoring_since)}</span>
|
||||
<span title={simplifyDate(c.monitoring_since)}>
|
||||
{IsEmptyDate(c.monitoring_since)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-span-4 flex items-center sm:px-6 ">
|
||||
<span className="" title={simplifyDate(c.last_announce)}>{IsEmptyDate(c.last_announce)}</span>
|
||||
<span title={simplifyDate(c.last_announce)}>
|
||||
{IsEmptyDate(c.last_announce)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
) : <div className="flex text-center justify-center py-4 dark:text-gray-500"><p>No channels!</p></div>}
|
||||
) : (
|
||||
<div className="flex text-center justify-center py-4 dark:text-gray-500">
|
||||
<p>No channels!</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -157,10 +239,5 @@ const ListItem = ({ idx, network }: ListItemProps) => {
|
|||
);
|
||||
};
|
||||
|
||||
function networkHealthy(network: IrcNetworkWithHealth): boolean {
|
||||
if (network.connection_errors.length > 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
const IsNetworkHealthy = (network: IrcNetworkWithHealth) =>
|
||||
network.connection_errors.length <= 0;
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useQuery } from "react-query";
|
|||
import { APIClient } from "../../api/APIClient";
|
||||
import { EmptySimple } from "../../components/emptystates";
|
||||
import { useToggle } from "../../hooks/hooks";
|
||||
import { NotificationAddForm, NotificationUpdateForm } from "../../forms/settings/NotifiactionForms";
|
||||
import { NotificationAddForm, NotificationUpdateForm } from "../../forms/settings/NotificationForms";
|
||||
import { Switch } from "@headlessui/react";
|
||||
import { classNames } from "../../utils";
|
||||
import { componentMapType } from "../../forms/settings/DownloadClientForms";
|
||||
|
|
|
@ -28,7 +28,11 @@ function ReleaseSettings() {
|
|||
const cancelModalButtonRef = useRef(null);
|
||||
|
||||
return (
|
||||
<form className="divide-y divide-gray-200 dark:divide-gray-700 lg:col-span-9" action="#" method="POST">
|
||||
<form
|
||||
className="lg:col-span-9"
|
||||
action="#"
|
||||
method="POST"
|
||||
>
|
||||
<DeleteModal
|
||||
isOpen={deleteModalIsOpen}
|
||||
toggle={toggleDeleteModal}
|
||||
|
@ -40,9 +44,11 @@ function ReleaseSettings() {
|
|||
|
||||
<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">Releases</h2>
|
||||
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">
|
||||
Releases
|
||||
</h2>
|
||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Release settings. Reset state.
|
||||
Release settings. Reset state.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -50,25 +56,21 @@ function ReleaseSettings() {
|
|||
<div className="pb-6 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<div className="px-4 py-5 sm:p-0">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
|
||||
<div>
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Danger Zone</h3>
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">
|
||||
Danger Zone
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<ul className="p-4 mt-6 divide-y divide-gray-200 dark:divide-gray-700 border-red-500 border rounded-lg">
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Delete all releases
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleDeleteModal}
|
||||
className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-red-700 dark:text-red-100 bg-red-100 dark:bg-red-500 hover:bg-red-200 dark:hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:text-sm"
|
||||
>
|
||||
Delete all releases
|
||||
</button>
|
||||
</div>
|
||||
</ul>
|
||||
<div className="flex justify-between items-center p-4 mt-6 max-w-sm m-auto">
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleDeleteModal}
|
||||
className="w-full inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-red-700 hover:text-red-900 dark:text-white bg-red-100 dark:bg-red-800 hover:bg-red-200 dark:hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:text-sm"
|
||||
>
|
||||
Delete all releases
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue