fix(ui): uniform-size checkboxes, vertically-aligned form labels, and fixes for other UI imperfections/inconsistencies (#737)
This commit is contained in:
@@ -77,7 +77,7 @@ const Alert: React.FC<AlertProps> = ({ title, children, type }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`rounded-md p-4 mb-8 ${design.bgColor}`}>
|
<div className={`rounded-md p-4 mb-5 ${design.bgColor}`}>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className={`flex-shrink-0 ${design.titleColor}`}>{design.svg}</div>
|
<div className={`flex-shrink-0 ${design.titleColor}`}>{design.svg}</div>
|
||||||
<div className="ml-3">
|
<div className="ml-3">
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ const Header: React.FC<HeaderProps> = ({
|
|||||||
subtext,
|
subtext,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="md:flex md:items-center md:justify-between mt-8 mb-8">
|
<div className="mt-8 md:flex md:items-center md:justify-between">
|
||||||
<div className={`flex-1 min-w-0 mx-${extraMargin}`}>
|
<div className={`flex-1 min-w-0 mx-${extraMargin}`}>
|
||||||
<h2 className="text-2xl font-bold leading-7 text-gray-100 sm:text-4xl sm:leading-9 truncate sm:overflow-visible">
|
<h2 className="mb-4 text-2xl font-bold leading-7 text-gray-100 truncate sm:text-4xl sm:leading-9 sm:overflow-visible md:mb-0">
|
||||||
<span className="bg-clip-text text-transparent bg-gradient-to-br from-indigo-400 to-purple-400">
|
<span className="text-transparent bg-clip-text bg-gradient-to-br from-indigo-400 to-purple-400">
|
||||||
{children}
|
{children}
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
{subtext && <div className="text-gray-400 mt-2">{subtext}</div>}
|
{subtext && <div className="mt-2 text-gray-400">{subtext}</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ interface ListItemProps {
|
|||||||
const ListItem: React.FC<ListItemProps> = ({ title, children }) => {
|
const ListItem: React.FC<ListItemProps> = ({ title, children }) => {
|
||||||
return (
|
return (
|
||||||
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4">
|
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4">
|
||||||
<dt className="text-sm font-medium text-gray-200">{title}</dt>
|
<dt className="block text-sm font-medium text-gray-400">{title}</dt>
|
||||||
<dd className="mt-1 flex text-sm text-gray-400 sm:mt-0 sm:col-span-2">
|
<dd className="flex text-sm text-white sm:mt-0 sm:col-span-2">
|
||||||
<span className="flex-grow">{children}</span>
|
<span className="flex-grow">{children}</span>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,12 +25,10 @@ const List: React.FC<ListProps> = ({ title, subTitle, children }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-lg leading-6 font-medium text-gray-100">{title}</h3>
|
<h3 className="heading">{title}</h3>
|
||||||
{subTitle && (
|
{subTitle && <p className="description">{subTitle}</p>}
|
||||||
<p className="mt-1 max-w-2xl text-sm text-gray-300">{subTitle}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5 border-t border-gray-800">
|
<div className="border-t border-gray-800 section">
|
||||||
<dl className="divide-y divide-gray-800">{children}</dl>
|
<dl className="divide-y divide-gray-800">{children}</dl>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ const Modal: React.FC<ModalProps> = ({
|
|||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className={`mt-3 text-center sm:mt-0 sm:text-left ${
|
className={`mt-3 text-center sm:mt-0 sm:text-left ${
|
||||||
iconSvg ? 'sm:ml-4' : 'mb-6'
|
iconSvg ? 'sm:ml-4' : 'sm:mb-4'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{title && (
|
{title && (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { withProperties } from '../../../utils/typeHelpers';
|
|||||||
|
|
||||||
const TBody: React.FC = ({ children }) => {
|
const TBody: React.FC = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<tbody className="bg-gray-600 divide-y divide-gray-700">{children}</tbody>
|
<tbody className="bg-gray-800 divide-y divide-gray-700">{children}</tbody>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -68,9 +68,11 @@ const DiscoverMovies: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header>
|
<div className="mt-1 mb-5">
|
||||||
<FormattedMessage {...messages.discovermovies} />
|
<Header>
|
||||||
</Header>
|
<FormattedMessage {...messages.discovermovies} />
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ListView
|
<ListView
|
||||||
items={titles}
|
items={titles}
|
||||||
isEmpty={isEmpty}
|
isEmpty={isEmpty}
|
||||||
|
|||||||
@@ -67,9 +67,11 @@ const DiscoverTv: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header>
|
<div className="mt-1 mb-5">
|
||||||
<FormattedMessage {...messages.discovertv} />
|
<Header>
|
||||||
</Header>
|
<FormattedMessage {...messages.discovertv} />
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ListView
|
<ListView
|
||||||
items={titles}
|
items={titles}
|
||||||
isEmpty={isEmpty}
|
isEmpty={isEmpty}
|
||||||
|
|||||||
@@ -74,9 +74,11 @@ const Trending: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header>
|
<div className="mt-1 mb-5">
|
||||||
<FormattedMessage {...messages.trending} />
|
<Header>
|
||||||
</Header>
|
<FormattedMessage {...messages.trending} />
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ListView
|
<ListView
|
||||||
items={titles}
|
items={titles}
|
||||||
isEmpty={isEmpty}
|
isEmpty={isEmpty}
|
||||||
|
|||||||
@@ -69,9 +69,11 @@ const UpcomingMovies: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header>
|
<div className="mt-1 mb-5">
|
||||||
<FormattedMessage {...messages.upcomingmovies} />
|
<Header>
|
||||||
</Header>
|
<FormattedMessage {...messages.upcomingmovies} />
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ListView
|
<ListView
|
||||||
items={titles}
|
items={titles}
|
||||||
isEmpty={isEmpty}
|
isEmpty={isEmpty}
|
||||||
|
|||||||
@@ -117,10 +117,10 @@ const LanguagePicker: React.FC = () => {
|
|||||||
leaveTo="transform opacity-0 scale-95"
|
leaveTo="transform opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="absolute right-0 w-48 mt-2 origin-top-right rounded-md shadow-lg"
|
className="absolute right-0 w-56 mt-2 origin-top-right rounded-md shadow-lg"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
>
|
>
|
||||||
<div className="px-2 py-2 bg-gray-700 rounded-md ring-1 ring-black ring-opacity-5">
|
<div className="px-3 py-2 bg-gray-700 rounded-md ring-1 ring-black ring-opacity-5">
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
htmlFor="language"
|
htmlFor="language"
|
||||||
@@ -130,7 +130,7 @@ const LanguagePicker: React.FC = () => {
|
|||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id="language"
|
id="language"
|
||||||
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 text-white bg-gray-700 border-gray-600 form-select focus:outline-none focus:ring-indigo focus:border-blue-800 sm:text-sm sm:leading-5"
|
className="rounded-md"
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setLocale && setLocale(e.target.value as AvailableLocales)
|
setLocale && setLocale(e.target.value as AvailableLocales)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ const Sidebar: React.FC<SidebarProps> = ({ open, setClosed }) => {
|
|||||||
}}
|
}}
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
className={`group flex items-center px-2 py-2 text-base leading-6 font-medium rounded-md text-white focus:outline-none focus:bg-gray-700 transition ease-in-out duration-150
|
className={`flex items-center px-2 py-2 text-base leading-6 font-medium rounded-md text-white focus:outline-none focus:bg-gray-700 transition ease-in-out duration-150
|
||||||
${
|
${
|
||||||
router.pathname.match(
|
router.pathname.match(
|
||||||
sidebarLink.activeRegExp
|
sidebarLink.activeRegExp
|
||||||
@@ -255,7 +255,7 @@ const Sidebar: React.FC<SidebarProps> = ({ open, setClosed }) => {
|
|||||||
as={sidebarLink.as}
|
as={sidebarLink.as}
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
className={`group flex items-center px-2 py-2 text-base leading-6 font-medium rounded-md text-white focus:outline-none focus:bg-gray-700 transition ease-in-out duration-150
|
className={`flex items-center px-2 py-2 text-base leading-6 font-medium rounded-md text-white focus:outline-none focus:bg-gray-700 transition ease-in-out duration-150
|
||||||
${
|
${
|
||||||
router.pathname.match(
|
router.pathname.match(
|
||||||
sidebarLink.activeRegExp
|
sidebarLink.activeRegExp
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ const Layout: React.FC = ({ children }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<main className="relative z-0 top-16 focus:outline-none" tabIndex={0}>
|
<main className="relative z-0 top-16 focus:outline-none" tabIndex={0}>
|
||||||
<div className="pt-2 pb-6">
|
<div className="pt-2 mb-6">
|
||||||
<div className="px-4 mx-auto max-w-8xl">
|
<div className="px-4 mx-auto max-w-8xl">
|
||||||
{router.pathname === '/' && hasPermission(Permission.ADMIN) && (
|
{router.pathname === '/' && hasPermission(Permission.ADMIN) && (
|
||||||
<div className="p-4 mt-2 bg-indigo-700 rounded-md">
|
<div className="p-4 mt-6 bg-indigo-700 rounded-md">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<svg
|
<svg
|
||||||
|
|||||||
@@ -57,10 +57,7 @@ const LocalLogin: React.FC<LocalLoginProps> = ({ revalidate }) => {
|
|||||||
<>
|
<>
|
||||||
<Form>
|
<Form>
|
||||||
<div className="sm:border-t sm:border-gray-800">
|
<div className="sm:border-t sm:border-gray-800">
|
||||||
<label
|
<label htmlFor="email" className="text-label">
|
||||||
htmlFor="email"
|
|
||||||
className="block my-1 text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.email)}
|
{intl.formatMessage(messages.email)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 mb-2 sm:mt-0 sm:col-span-2">
|
<div className="mt-1 mb-2 sm:mt-0 sm:col-span-2">
|
||||||
@@ -70,17 +67,13 @@ const LocalLogin: React.FC<LocalLoginProps> = ({ revalidate }) => {
|
|||||||
name="email"
|
name="email"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="name@example.com"
|
placeholder="name@example.com"
|
||||||
className="text-white flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.email && touched.email && (
|
{errors.email && touched.email && (
|
||||||
<div className="mt-2 text-red-500">{errors.email}</div>
|
<div className="error">{errors.email}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<label
|
<label htmlFor="password" className="text-label">
|
||||||
htmlFor="password"
|
|
||||||
className="block my-1 text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.password)}
|
{intl.formatMessage(messages.password)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 mb-2 sm:mt-0 sm:col-span-2">
|
<div className="mt-1 mb-2 sm:mt-0 sm:col-span-2">
|
||||||
@@ -90,20 +83,19 @@ const LocalLogin: React.FC<LocalLoginProps> = ({ revalidate }) => {
|
|||||||
name="password"
|
name="password"
|
||||||
type="password"
|
type="password"
|
||||||
placeholder={intl.formatMessage(messages.password)}
|
placeholder={intl.formatMessage(messages.password)}
|
||||||
className="text-white flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.password && touched.password && (
|
{errors.password && touched.password && (
|
||||||
<div className="mt-2 text-red-500">{errors.password}</div>
|
<div className="error">{errors.password}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{loginError && (
|
{loginError && (
|
||||||
<div className="mt-1 mb-2 sm:mt-0 sm:col-span-2">
|
<div className="mt-1 mb-2 sm:mt-0 sm:col-span-2">
|
||||||
<div className="mt-2 text-red-500">{loginError}</div>
|
<div className="error">{loginError}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -32,15 +32,17 @@ const MovieCast: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header
|
<div className="mt-1 mb-5">
|
||||||
subtext={
|
<Header
|
||||||
<Link href={`/movie/${data.id}`}>
|
subtext={
|
||||||
<a className="hover:underline">{data.title}</a>
|
<Link href={`/movie/${data.id}`}>
|
||||||
</Link>
|
<a className="hover:underline">{data.title}</a>
|
||||||
}
|
</Link>
|
||||||
>
|
}
|
||||||
{intl.formatMessage(messages.fullcast)}
|
>
|
||||||
</Header>
|
{intl.formatMessage(messages.fullcast)}
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ul className="grid grid-cols-2 gap-6 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-7 2xl:grid-cols-8">
|
<ul className="grid grid-cols-2 gap-6 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-7 2xl:grid-cols-8">
|
||||||
{data?.credits.cast.map((person, index) => {
|
{data?.credits.cast.map((person, index) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -32,15 +32,17 @@ const MovieCrew: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header
|
<div className="mt-1 mb-5">
|
||||||
subtext={
|
<Header
|
||||||
<Link href={`/movie/${data.id}`}>
|
subtext={
|
||||||
<a className="hover:underline">{data.title}</a>
|
<Link href={`/movie/${data.id}`}>
|
||||||
</Link>
|
<a className="hover:underline">{data.title}</a>
|
||||||
}
|
</Link>
|
||||||
>
|
}
|
||||||
{intl.formatMessage(messages.fullcrew)}
|
>
|
||||||
</Header>
|
{intl.formatMessage(messages.fullcrew)}
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ul className="grid grid-cols-2 gap-6 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-7 2xl:grid-cols-8">
|
<ul className="grid grid-cols-2 gap-6 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-7 2xl:grid-cols-8">
|
||||||
{data?.credits.crew.map((person, index) => {
|
{data?.credits.crew.map((person, index) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -77,17 +77,19 @@ const MovieRecommendations: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header
|
<div className="mt-1 mb-5">
|
||||||
subtext={
|
<Header
|
||||||
movieData && !movieError
|
subtext={
|
||||||
? intl.formatMessage(messages.recommendationssubtext, {
|
movieData && !movieError
|
||||||
title: movieData.title,
|
? intl.formatMessage(messages.recommendationssubtext, {
|
||||||
})
|
title: movieData.title,
|
||||||
: ''
|
})
|
||||||
}
|
: ''
|
||||||
>
|
}
|
||||||
<FormattedMessage {...messages.recommendations} />
|
>
|
||||||
</Header>
|
<FormattedMessage {...messages.recommendations} />
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ListView
|
<ListView
|
||||||
items={titles}
|
items={titles}
|
||||||
isEmpty={isEmpty}
|
isEmpty={isEmpty}
|
||||||
|
|||||||
@@ -77,17 +77,19 @@ const MovieSimilar: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header
|
<div className="mt-1 mb-5">
|
||||||
subtext={
|
<Header
|
||||||
movieData && !movieError
|
subtext={
|
||||||
? intl.formatMessage(messages.similarsubtext, {
|
movieData && !movieError
|
||||||
title: movieData.title,
|
? intl.formatMessage(messages.similarsubtext, {
|
||||||
})
|
title: movieData.title,
|
||||||
: undefined
|
})
|
||||||
}
|
: undefined
|
||||||
>
|
}
|
||||||
<FormattedMessage {...messages.similar} />
|
>
|
||||||
</Header>
|
<FormattedMessage {...messages.similar} />
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ListView
|
<ListView
|
||||||
items={titles}
|
items={titles}
|
||||||
isEmpty={isEmpty}
|
isEmpty={isEmpty}
|
||||||
|
|||||||
@@ -178,50 +178,56 @@ const MovieDetails: React.FC<MovieDetailsProps> = ({ movie }) => {
|
|||||||
{data?.mediaInfo &&
|
{data?.mediaInfo &&
|
||||||
(data.mediaInfo.status !== MediaStatus.AVAILABLE ||
|
(data.mediaInfo.status !== MediaStatus.AVAILABLE ||
|
||||||
data.mediaInfo.status4k !== MediaStatus.AVAILABLE) && (
|
data.mediaInfo.status4k !== MediaStatus.AVAILABLE) && (
|
||||||
<div className="flex flex-col mb-6 sm:flex-row flex-nowrap">
|
<div className="mb-6">
|
||||||
{data?.mediaInfo &&
|
{data?.mediaInfo &&
|
||||||
data?.mediaInfo.status !== MediaStatus.AVAILABLE && (
|
data?.mediaInfo.status !== MediaStatus.AVAILABLE && (
|
||||||
<Button
|
<div className="flex flex-col sm:flex-row flex-nowrap mb-2">
|
||||||
onClick={() => markAvailable()}
|
<Button
|
||||||
className="w-full mb-2 sm:mb-0 sm:mr-1 last:mr-0"
|
onClick={() => markAvailable()}
|
||||||
buttonType="success"
|
className="w-full sm:mb-0"
|
||||||
>
|
buttonType="success"
|
||||||
<svg
|
|
||||||
className="w-5 h-5 mr-1"
|
|
||||||
fill="currentColor"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
>
|
||||||
<path
|
<svg
|
||||||
fillRule="evenodd"
|
className="w-5 h-5 mr-1"
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z"
|
fill="currentColor"
|
||||||
clipRule="evenodd"
|
viewBox="0 0 20 20"
|
||||||
/>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</svg>
|
>
|
||||||
<span>{intl.formatMessage(messages.markavailable)}</span>
|
<path
|
||||||
</Button>
|
fillRule="evenodd"
|
||||||
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>{intl.formatMessage(messages.markavailable)}</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{data?.mediaInfo &&
|
{data?.mediaInfo &&
|
||||||
data?.mediaInfo.status4k !== MediaStatus.AVAILABLE && (
|
data?.mediaInfo.status4k !== MediaStatus.AVAILABLE && (
|
||||||
<Button
|
<div className="flex flex-col sm:flex-row flex-nowrap mb-2">
|
||||||
onClick={() => markAvailable(true)}
|
<Button
|
||||||
className="w-full sm:ml-1 first:ml-0"
|
onClick={() => markAvailable(true)}
|
||||||
buttonType="success"
|
className="w-full sm:mb-0"
|
||||||
>
|
buttonType="success"
|
||||||
<svg
|
|
||||||
className="w-5 h-5 mr-1"
|
|
||||||
fill="currentColor"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
>
|
||||||
<path
|
<svg
|
||||||
fillRule="evenodd"
|
className="w-5 h-5 mr-1"
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z"
|
fill="currentColor"
|
||||||
clipRule="evenodd"
|
viewBox="0 0 20 20"
|
||||||
/>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</svg>
|
>
|
||||||
<span>{intl.formatMessage(messages.mark4kavailable)}</span>
|
<path
|
||||||
</Button>
|
fillRule="evenodd"
|
||||||
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span>
|
||||||
|
{intl.formatMessage(messages.mark4kavailable)}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -23,12 +23,11 @@ const NotificationType: React.FC<NotificationTypeProps> = ({
|
|||||||
: ''
|
: ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center h-5">
|
<div className="flex items-center h-6">
|
||||||
<input
|
<input
|
||||||
id={option.id}
|
id={option.id}
|
||||||
name="permissions"
|
name="permissions"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="w-4 h-4 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
disabled={
|
disabled={
|
||||||
!!parent?.value && hasNotificationType(parent.value, currentTypes)
|
!!parent?.value && hasNotificationType(parent.value, currentTypes)
|
||||||
}
|
}
|
||||||
@@ -46,7 +45,7 @@ const NotificationType: React.FC<NotificationTypeProps> = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3 text-sm leading-5">
|
<div className="ml-3 text-sm leading-6">
|
||||||
<label htmlFor={option.id} className="font-medium">
|
<label htmlFor={option.id} className="font-medium">
|
||||||
{option.name}
|
{option.name}
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -41,12 +41,11 @@ const PermissionOption: React.FC<PermissionOptionProps> = ({
|
|||||||
: ''
|
: ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center h-5">
|
<div className="flex items-center h-6">
|
||||||
<input
|
<input
|
||||||
id={option.id}
|
id={option.id}
|
||||||
name="permissions"
|
name="permissions"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="w-4 h-4 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
disabled={
|
disabled={
|
||||||
(option.permission !== Permission.ADMIN &&
|
(option.permission !== Permission.ADMIN &&
|
||||||
hasPermission(Permission.ADMIN, currentPermission)) ||
|
hasPermission(Permission.ADMIN, currentPermission)) ||
|
||||||
@@ -73,15 +72,17 @@ const PermissionOption: React.FC<PermissionOptionProps> = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3 text-sm leading-5">
|
<div className="ml-3 text-sm leading-6">
|
||||||
<label htmlFor={option.id} className="font-medium">
|
<label htmlFor={option.id} className="block font-medium">
|
||||||
{option.name}
|
<div className="flex flex-col">
|
||||||
|
<span>{option.name}</span>
|
||||||
|
<span className="text-gray-500">{option.description}</span>
|
||||||
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<p className="text-gray-500">{option.description}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{(option.children ?? []).map((child) => (
|
{(option.children ?? []).map((child) => (
|
||||||
<div key={`permission-child-${child.id}`} className="pl-6 mt-4">
|
<div key={`permission-child-${child.id}`} className="pl-10 mt-4">
|
||||||
<PermissionOption
|
<PermissionOption
|
||||||
option={child}
|
option={child}
|
||||||
currentPermission={currentPermission}
|
currentPermission={currentPermission}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ const RequestItem: React.FC<RequestItemProps> = ({
|
|||||||
|
|
||||||
if (!title && !error) {
|
if (!title && !error) {
|
||||||
return (
|
return (
|
||||||
<tr className="w-full h-24 bg-gray-800 animate-pulse" ref={ref}>
|
<tr className="w-full h-24 animate-pulse" ref={ref}>
|
||||||
<td colSpan={6}></td>
|
<td colSpan={6}></td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
@@ -110,14 +110,14 @@ const RequestItem: React.FC<RequestItemProps> = ({
|
|||||||
|
|
||||||
if (!title || !requestData) {
|
if (!title || !requestData) {
|
||||||
return (
|
return (
|
||||||
<tr className="w-full h-24 bg-gray-800 animate-pulse">
|
<tr className="w-full h-24 animate-pulse">
|
||||||
<td colSpan={6}></td>
|
<td colSpan={6}></td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className="relative w-full h-24 p-2 text-white bg-gray-800">
|
<tr className="relative w-full h-24 p-2">
|
||||||
<RequestModal
|
<RequestModal
|
||||||
show={showEditModal}
|
show={showEditModal}
|
||||||
tmdbId={request.media.tmdbId}
|
tmdbId={request.media.tmdbId}
|
||||||
@@ -216,16 +216,18 @@ const RequestItem: React.FC<RequestItemProps> = ({
|
|||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{requestData.modifiedBy ? (
|
{requestData.modifiedBy ? (
|
||||||
<span className="text-sm text-gray-300">
|
<span className="text-sm text-gray-300">
|
||||||
{requestData.modifiedBy.displayName}
|
<span className="mr-1">{requestData.modifiedBy.displayName}</span>
|
||||||
(
|
<span>
|
||||||
<FormattedRelativeTime
|
(
|
||||||
value={Math.floor(
|
<FormattedRelativeTime
|
||||||
(new Date(requestData.updatedAt).getTime() - Date.now()) /
|
value={Math.floor(
|
||||||
1000
|
(new Date(requestData.updatedAt).getTime() - Date.now()) /
|
||||||
)}
|
1000
|
||||||
updateIntervalInSeconds={1}
|
)}
|
||||||
/>
|
updateIntervalInSeconds={1}
|
||||||
)
|
/>
|
||||||
|
)
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-sm text-gray-300">N/A</span>
|
<span className="text-sm text-gray-300">N/A</span>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ const RequestList: React.FC = () => {
|
|||||||
<>
|
<>
|
||||||
<div className="flex flex-col justify-between md:items-end md:flex-row">
|
<div className="flex flex-col justify-between md:items-end md:flex-row">
|
||||||
<Header>{intl.formatMessage(messages.requests)}</Header>
|
<Header>{intl.formatMessage(messages.requests)}</Header>
|
||||||
<div className="flex flex-col md:flex-row">
|
<div className="flex flex-col mt-2 md:flex-row">
|
||||||
<div className="flex mb-2 md:mb-0 md:mr-2">
|
<div className="flex mb-2 md:mb-0 md:mr-2">
|
||||||
<span className="inline-flex items-center px-3 text-gray-100 bg-gray-800 border border-r-0 border-gray-500 cursor-default rounded-l-md sm:text-sm">
|
<span className="inline-flex items-center px-3 text-gray-100 bg-gray-800 border border-r-0 border-gray-500 cursor-default rounded-l-md sm:text-sm">
|
||||||
<svg
|
<svg
|
||||||
@@ -84,7 +84,7 @@ const RequestList: React.FC = () => {
|
|||||||
setCurrentFilter(e.target.value as Filter);
|
setCurrentFilter(e.target.value as Filter);
|
||||||
}}
|
}}
|
||||||
value={currentFilter}
|
value={currentFilter}
|
||||||
className="flex-1 block w-full py-2 pl-3 pr-10 text-base leading-6 text-white bg-gray-700 border-gray-500 rounded-r-md form-select focus:outline-none focus:ring-blue focus:border-gray-500 sm:text-sm sm:leading-5 disabled:opacity-50"
|
className="rounded-r-only"
|
||||||
>
|
>
|
||||||
<option value="all">
|
<option value="all">
|
||||||
{intl.formatMessage(messages.filterAll)}
|
{intl.formatMessage(messages.filterAll)}
|
||||||
@@ -120,7 +120,7 @@ const RequestList: React.FC = () => {
|
|||||||
setCurrentSort(e.target.value as Sort);
|
setCurrentSort(e.target.value as Sort);
|
||||||
}}
|
}}
|
||||||
value={currentSort}
|
value={currentSort}
|
||||||
className="flex-1 block w-full py-2 pl-3 pr-10 text-base leading-6 text-white bg-gray-700 border-gray-500 rounded-r-md form-select focus:outline-none focus:ring-blue focus:border-gray-500 sm:text-sm sm:leading-5 disabled:opacity-50"
|
className="rounded-r-only"
|
||||||
>
|
>
|
||||||
<option value="added">
|
<option value="added">
|
||||||
{intl.formatMessage(messages.sortAdded)}
|
{intl.formatMessage(messages.sortAdded)}
|
||||||
@@ -152,10 +152,12 @@ const RequestList: React.FC = () => {
|
|||||||
})}
|
})}
|
||||||
|
|
||||||
{data.results.length === 0 && (
|
{data.results.length === 0 && (
|
||||||
<tr className="relative w-full h-24 p-2 text-white bg-gray-800">
|
<tr className="relative w-full h-24 p-2 text-white">
|
||||||
<Table.TD colSpan={6} noPadding>
|
<Table.TD colSpan={6} noPadding>
|
||||||
<div className="flex flex-col items-center justify-center p-4">
|
<div className="flex flex-col items-center justify-center p-6">
|
||||||
<span>{intl.formatMessage(messages.noresults)}</span>
|
<span className="text-base">
|
||||||
|
{intl.formatMessage(messages.noresults)}
|
||||||
|
</span>
|
||||||
{currentFilter !== 'all' && (
|
{currentFilter !== 'all' && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button
|
<Button
|
||||||
@@ -171,10 +173,10 @@ const RequestList: React.FC = () => {
|
|||||||
</Table.TD>
|
</Table.TD>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
<tr>
|
<tr className="bg-gray-700">
|
||||||
<Table.TD colSpan={6} noPadding>
|
<Table.TD colSpan={6} noPadding>
|
||||||
<nav
|
<nav
|
||||||
className="flex items-center justify-between px-4 py-3 text-white bg-gray-700"
|
className="flex items-center justify-between px-6 py-3"
|
||||||
aria-label="Pagination"
|
aria-label="Pagination"
|
||||||
>
|
>
|
||||||
<div className="hidden sm:block">
|
<div className="hidden sm:block">
|
||||||
|
|||||||
@@ -200,15 +200,15 @@ const AdvancedRequester: React.FC<AdvancedRequesterProps> = ({
|
|||||||
<div className="p-4 bg-gray-600 rounded-md shadow">
|
<div className="p-4 bg-gray-600 rounded-md shadow">
|
||||||
<div className="flex flex-col items-center justify-between md:flex-row">
|
<div className="flex flex-col items-center justify-between md:flex-row">
|
||||||
<div className="flex-grow flex-shrink-0 w-full mb-2 md:w-1/3 md:pr-4 md:mb-0">
|
<div className="flex-grow flex-shrink-0 w-full mb-2 md:w-1/3 md:pr-4 md:mb-0">
|
||||||
<label htmlFor="server" className="block text-sm font-medium">
|
<label htmlFor="server" className="text-label">
|
||||||
{intl.formatMessage(messages.destinationserver)}
|
{intl.formatMessage(messages.destinationserver)}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id="server"
|
id="server"
|
||||||
name="server"
|
name="server"
|
||||||
|
value={selectedServer}
|
||||||
onChange={(e) => setSelectedServer(Number(e.target.value))}
|
onChange={(e) => setSelectedServer(Number(e.target.value))}
|
||||||
onBlur={(e) => setSelectedServer(Number(e.target.value))}
|
onBlur={(e) => setSelectedServer(Number(e.target.value))}
|
||||||
value={selectedServer}
|
|
||||||
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 text-white transition duration-150 ease-in-out bg-gray-800 border-gray-700 rounded-md form-select focus:outline-none focus:ring-blue focus:border-blue-300 sm:text-sm sm:leading-5"
|
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 text-white transition duration-150 ease-in-out bg-gray-800 border-gray-700 rounded-md form-select focus:outline-none focus:ring-blue focus:border-blue-300 sm:text-sm sm:leading-5"
|
||||||
>
|
>
|
||||||
{data.map((server) => (
|
{data.map((server) => (
|
||||||
@@ -222,7 +222,7 @@ const AdvancedRequester: React.FC<AdvancedRequesterProps> = ({
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-grow flex-shrink-0 w-full mb-2 md:w-1/3 md:pr-4 md:mb-0">
|
<div className="flex-grow flex-shrink-0 w-full mb-2 md:w-1/3 md:pr-4 md:mb-0">
|
||||||
<label htmlFor="server" className="block text-sm font-medium">
|
<label htmlFor="server" className="text-label">
|
||||||
{intl.formatMessage(messages.qualityprofile)}
|
{intl.formatMessage(messages.qualityprofile)}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@@ -255,7 +255,7 @@ const AdvancedRequester: React.FC<AdvancedRequesterProps> = ({
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-grow flex-shrink-0 w-full mb-2 md:w-1/3 md:mb-0">
|
<div className="flex-grow flex-shrink-0 w-full mb-2 md:w-1/3 md:mb-0">
|
||||||
<label htmlFor="server" className="block text-sm font-medium">
|
<label htmlFor="server" className="text-label">
|
||||||
{intl.formatMessage(messages.rootfolder)}
|
{intl.formatMessage(messages.rootfolder)}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
|||||||
@@ -391,7 +391,7 @@ const TvRequestModal: React.FC<RequestModalProps> = ({
|
|||||||
toggleAllSeasons();
|
toggleAllSeasons();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="relative inline-flex items-center justify-center flex-shrink-0 w-10 h-5 cursor-pointer group focus:outline-none"
|
className="relative inline-flex items-center justify-center flex-shrink-0 w-10 h-5 cursor-pointer pt-2 focus:outline-none"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@@ -451,7 +451,7 @@ const TvRequestModal: React.FC<RequestModalProps> = ({
|
|||||||
toggleSeason(season.seasonNumber);
|
toggleSeason(season.seasonNumber);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className={`group relative inline-flex items-center justify-center flex-shrink-0 h-5 w-10 cursor-pointer focus:outline-none ${
|
className={`pt-2 relative inline-flex items-center justify-center flex-shrink-0 h-5 w-10 cursor-pointer focus:outline-none ${
|
||||||
mediaSeason ||
|
mediaSeason ||
|
||||||
(!!seasonRequest &&
|
(!!seasonRequest &&
|
||||||
!editingSeasons.includes(season.seasonNumber))
|
!editingSeasons.includes(season.seasonNumber))
|
||||||
|
|||||||
@@ -65,7 +65,9 @@ const Search: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header>{intl.formatMessage(messages.searchresults)}</Header>
|
<div className="mt-1 mb-5">
|
||||||
|
<Header>{intl.formatMessage(messages.searchresults)}</Header>
|
||||||
|
</div>
|
||||||
<ListView
|
<ListView
|
||||||
items={titles}
|
items={titles}
|
||||||
isEmpty={isEmpty}
|
isEmpty={isEmpty}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const CopyButton: React.FC<{ textToCopy: string }> = ({ textToCopy }) => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setCopied();
|
setCopied();
|
||||||
}}
|
}}
|
||||||
className="-ml-px relative inline-flex items-center px-4 py-2 border border-gray-500 text-sm leading-5 font-medium text-white bg-indigo-500 hover:bg-indigo-400 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150"
|
className="-ml-px relative inline-flex items-center px-4 py-2 border border-gray-500 text-sm leading-5 font-medium text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
className="w-5 h-5 text-white"
|
className="w-5 h-5 text-white"
|
||||||
|
|||||||
@@ -88,31 +88,20 @@ const NotificationsDiscord: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form className="section">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="enabled" className="checkbox-label">
|
||||||
htmlFor="enabled"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.agentenabled)}
|
{intl.formatMessage(messages.agentenabled)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field type="checkbox" id="enabled" name="enabled" />
|
||||||
type="checkbox"
|
|
||||||
id="enabled"
|
|
||||||
name="enabled"
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="name" className="text-label">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.webhookUrl)}
|
{intl.formatMessage(messages.webhookUrl)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="webhookUrl"
|
id="webhookUrl"
|
||||||
@@ -121,39 +110,29 @@ const NotificationsDiscord: React.FC = () => {
|
|||||||
placeholder={intl.formatMessage(
|
placeholder={intl.formatMessage(
|
||||||
messages.webhookUrlPlaceholder
|
messages.webhookUrlPlaceholder
|
||||||
)}
|
)}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.webhookUrl && touched.webhookUrl && (
|
{errors.webhookUrl && touched.webhookUrl && (
|
||||||
<div className="mt-2 text-red-500">{errors.webhookUrl}</div>
|
<div className="error">{errors.webhookUrl}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div role="group" aria-labelledby="group-label" className="group">
|
||||||
<div role="group" aria-labelledby="label-permissions">
|
<div className="form-row">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
<span id="group-label" className="group-label">
|
||||||
<div>
|
{intl.formatMessage(messages.notificationtypes)}
|
||||||
<div
|
</span>
|
||||||
className="text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
|
<div className="form-input">
|
||||||
id="label-types"
|
<div className="max-w-lg">
|
||||||
>
|
<NotificationTypeSelector
|
||||||
{intl.formatMessage(messages.notificationtypes)}
|
currentTypes={values.types}
|
||||||
</div>
|
onUpdate={(newTypes) => setFieldValue('types', newTypes)}
|
||||||
</div>
|
/>
|
||||||
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
|
||||||
<div className="max-w-lg">
|
|
||||||
<NotificationTypeSelector
|
|
||||||
currentTypes={values.types}
|
|
||||||
onUpdate={(newTypes) =>
|
|
||||||
setFieldValue('types', newTypes)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -124,113 +124,86 @@ const NotificationsEmail: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form className="section">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="enabled" className="checkbox-label">
|
||||||
htmlFor="enabled"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.agentenabled)}
|
{intl.formatMessage(messages.agentenabled)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field type="checkbox" id="enabled" name="enabled" />
|
||||||
type="checkbox"
|
|
||||||
id="enabled"
|
|
||||||
name="enabled"
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="emailFrom" className="text-label">
|
||||||
htmlFor="emailFrom"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.emailsender)}
|
{intl.formatMessage(messages.emailsender)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="emailFrom"
|
id="emailFrom"
|
||||||
name="emailFrom"
|
name="emailFrom"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="no-reply@example.com"
|
placeholder="no-reply@example.com"
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.emailFrom && touched.emailFrom && (
|
{errors.emailFrom && touched.emailFrom && (
|
||||||
<div className="mt-2 text-red-500">{errors.emailFrom}</div>
|
<div className="error">{errors.emailFrom}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="senderName" className="text-label">
|
||||||
htmlFor="senderName"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.senderName)}
|
{intl.formatMessage(messages.senderName)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="senderName"
|
id="senderName"
|
||||||
name="senderName"
|
name="senderName"
|
||||||
placeholder="Overseerr"
|
placeholder="Overseerr"
|
||||||
type="text"
|
type="text"
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="smtpHost" className="text-label">
|
||||||
htmlFor="smtpHost"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.smtpHost)}
|
{intl.formatMessage(messages.smtpHost)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="smtpHost"
|
id="smtpHost"
|
||||||
name="smtpHost"
|
name="smtpHost"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="localhost"
|
placeholder="localhost"
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.smtpHost && touched.smtpHost && (
|
{errors.smtpHost && touched.smtpHost && (
|
||||||
<div className="mt-2 text-red-500">{errors.smtpHost}</div>
|
<div className="error">{errors.smtpHost}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="smtpPort" className="text-label">
|
||||||
htmlFor="smtpPort"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.smtpPort)}
|
{intl.formatMessage(messages.smtpPort)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="smtpPort"
|
id="smtpPort"
|
||||||
name="smtpPort"
|
name="smtpPort"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="465"
|
placeholder="465"
|
||||||
className="block w-24 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.smtpPort && touched.smtpPort && (
|
{errors.smtpPort && touched.smtpPort && (
|
||||||
<div className="mt-2 text-red-500">{errors.smtpPort}</div>
|
<div className="error">{errors.smtpPort}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="secure" className="checkbox-label">
|
||||||
htmlFor="secure"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span>{intl.formatMessage(messages.enableSsl)}</span>
|
<span>{intl.formatMessage(messages.enableSsl)}</span>
|
||||||
<span className="text-gray-500">
|
<span className="text-gray-500">
|
||||||
@@ -238,93 +211,63 @@ const NotificationsEmail: React.FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field type="checkbox" id="secure" name="secure" />
|
||||||
type="checkbox"
|
|
||||||
id="secure"
|
|
||||||
name="secure"
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="allowSelfSigned" className="checkbox-label">
|
||||||
htmlFor="allowSelfSigned"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.allowselfsigned)}
|
{intl.formatMessage(messages.allowselfsigned)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="allowSelfSigned"
|
id="allowSelfSigned"
|
||||||
name="allowSelfSigned"
|
name="allowSelfSigned"
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="authUser" className="text-label">
|
||||||
htmlFor="authUser"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.authUser)}
|
{intl.formatMessage(messages.authUser)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field id="authUser" name="authUser" type="text" />
|
||||||
id="authUser"
|
|
||||||
name="authUser"
|
|
||||||
type="text"
|
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="authPass" className="text-label">
|
||||||
htmlFor="authPass"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.authPass)}
|
{intl.formatMessage(messages.authPass)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="authPass"
|
id="authPass"
|
||||||
name="authPass"
|
name="authPass"
|
||||||
type="password"
|
type="password"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div role="group" aria-labelledby="group-label" className="group">
|
||||||
<div role="group" aria-labelledby="label-permissions">
|
<div className="form-row">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
<span id="group-label" className="group-label">
|
||||||
<div>
|
{intl.formatMessage(messages.notificationtypes)}
|
||||||
<div
|
</span>
|
||||||
className="text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
|
<div className="form-input">
|
||||||
id="label-types"
|
<div className="max-w-lg">
|
||||||
>
|
<NotificationTypeSelector
|
||||||
{intl.formatMessage(messages.notificationtypes)}
|
currentTypes={values.types}
|
||||||
</div>
|
onUpdate={(newTypes) => setFieldValue('types', newTypes)}
|
||||||
</div>
|
/>
|
||||||
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
|
||||||
<div className="max-w-lg">
|
|
||||||
<NotificationTypeSelector
|
|
||||||
currentTypes={values.types}
|
|
||||||
onUpdate={(newTypes) =>
|
|
||||||
setFieldValue('types', newTypes)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -133,92 +133,69 @@ const NotificationsPushover: React.FC = () => {
|
|||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
</Alert>
|
</Alert>
|
||||||
<Form>
|
<Form className="section">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="enabled" className="checkbox-label">
|
||||||
htmlFor="enabled"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.agentenabled)}
|
{intl.formatMessage(messages.agentenabled)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field type="checkbox" id="enabled" name="enabled" />
|
||||||
type="checkbox"
|
|
||||||
id="enabled"
|
|
||||||
name="enabled"
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="accessToken" className="text-label">
|
||||||
htmlFor="accessToken"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.accessToken)}
|
{intl.formatMessage(messages.accessToken)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="accessToken"
|
id="accessToken"
|
||||||
name="accessToken"
|
name="accessToken"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={intl.formatMessage(messages.accessToken)}
|
placeholder={intl.formatMessage(messages.accessToken)}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.accessToken && touched.accessToken && (
|
{errors.accessToken && touched.accessToken && (
|
||||||
<div className="mt-2 text-red-500">
|
<div className="error">{errors.accessToken}</div>
|
||||||
{errors.accessToken}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<label
|
</div>
|
||||||
htmlFor="userToken"
|
<div className="form-row">
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
<label htmlFor="userToken" className="text-label">
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.userToken)}
|
{intl.formatMessage(messages.userToken)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="userToken"
|
id="userToken"
|
||||||
name="userToken"
|
name="userToken"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={intl.formatMessage(messages.userToken)}
|
placeholder={intl.formatMessage(messages.userToken)}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.userToken && touched.userToken && (
|
{errors.userToken && touched.userToken && (
|
||||||
<div className="mt-2 text-red-500">{errors.userToken}</div>
|
<div className="error">{errors.userToken}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div role="group" aria-labelledby="group-label" className="group">
|
||||||
<div role="group" aria-labelledby="label-permissions">
|
<div className="form-row">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
<span id="group-label" className="group-label">
|
||||||
<div>
|
{intl.formatMessage(messages.notificationtypes)}
|
||||||
<div
|
</span>
|
||||||
className="text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
|
<div className="form-input">
|
||||||
id="label-types"
|
<div className="max-w-lg">
|
||||||
>
|
<NotificationTypeSelector
|
||||||
{intl.formatMessage(messages.notificationtypes)}
|
currentTypes={values.types}
|
||||||
</div>
|
onUpdate={(newTypes) =>
|
||||||
</div>
|
setFieldValue('types', newTypes)
|
||||||
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
}
|
||||||
<div className="max-w-lg">
|
/>
|
||||||
<NotificationTypeSelector
|
|
||||||
currentTypes={values.types}
|
|
||||||
onUpdate={(newTypes) =>
|
|
||||||
setFieldValue('types', newTypes)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -116,31 +116,20 @@ const NotificationsSlack: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form className="section">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="isDefault" className="checkbox-label">
|
||||||
htmlFor="isDefault"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.agentenabled)}
|
{intl.formatMessage(messages.agentenabled)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field type="checkbox" id="enabled" name="enabled" />
|
||||||
type="checkbox"
|
|
||||||
id="enabled"
|
|
||||||
name="enabled"
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="name" className="text-label">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.webhookUrl)}
|
{intl.formatMessage(messages.webhookUrl)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="webhookUrl"
|
id="webhookUrl"
|
||||||
@@ -149,39 +138,31 @@ const NotificationsSlack: React.FC = () => {
|
|||||||
placeholder={intl.formatMessage(
|
placeholder={intl.formatMessage(
|
||||||
messages.webhookUrlPlaceholder
|
messages.webhookUrlPlaceholder
|
||||||
)}
|
)}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.webhookUrl && touched.webhookUrl && (
|
{errors.webhookUrl && touched.webhookUrl && (
|
||||||
<div className="mt-2 text-red-500">{errors.webhookUrl}</div>
|
<div className="error">{errors.webhookUrl}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div role="group" aria-labelledby="group-label" className="group">
|
||||||
<div role="group" aria-labelledby="label-permissions">
|
<div className="form-row">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
<span id="group-label" className="group-label">
|
||||||
<div>
|
{intl.formatMessage(messages.notificationtypes)}
|
||||||
<div
|
</span>
|
||||||
className="text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
|
<div className="form-input">
|
||||||
id="label-types"
|
<div className="max-w-lg">
|
||||||
>
|
<NotificationTypeSelector
|
||||||
{intl.formatMessage(messages.notificationtypes)}
|
currentTypes={values.types}
|
||||||
</div>
|
onUpdate={(newTypes) =>
|
||||||
</div>
|
setFieldValue('types', newTypes)
|
||||||
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
}
|
||||||
<div className="max-w-lg">
|
/>
|
||||||
<NotificationTypeSelector
|
|
||||||
currentTypes={values.types}
|
|
||||||
onUpdate={(newTypes) =>
|
|
||||||
setFieldValue('types', newTypes)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -133,90 +133,69 @@ const NotificationsTelegram: React.FC = () => {
|
|||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
</Alert>
|
</Alert>
|
||||||
<Form>
|
<Form className="section">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="enabled" className="checkbox-label">
|
||||||
htmlFor="enabled"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.agentenabled)}
|
{intl.formatMessage(messages.agentenabled)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field type="checkbox" id="enabled" name="enabled" />
|
||||||
type="checkbox"
|
|
||||||
id="enabled"
|
|
||||||
name="enabled"
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="botAPI" className="text-label">
|
||||||
htmlFor="botAPI"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.botAPI)}
|
{intl.formatMessage(messages.botAPI)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="botAPI"
|
id="botAPI"
|
||||||
name="botAPI"
|
name="botAPI"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={intl.formatMessage(messages.botAPI)}
|
placeholder={intl.formatMessage(messages.botAPI)}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.botAPI && touched.botAPI && (
|
{errors.botAPI && touched.botAPI && (
|
||||||
<div className="mt-2 text-red-500">{errors.botAPI}</div>
|
<div className="error">{errors.botAPI}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<label
|
</div>
|
||||||
htmlFor="chatId"
|
<div className="form-row">
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
<label htmlFor="chatId" className="text-label">
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.chatId)}
|
{intl.formatMessage(messages.chatId)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="chatId"
|
id="chatId"
|
||||||
name="chatId"
|
name="chatId"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={intl.formatMessage(messages.chatId)}
|
placeholder={intl.formatMessage(messages.chatId)}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.chatId && touched.chatId && (
|
{errors.chatId && touched.chatId && (
|
||||||
<div className="mt-2 text-red-500">{errors.chatId}</div>
|
<div className="error">{errors.chatId}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div role="group" aria-labelledby="group-label" className="group">
|
||||||
<div role="group" aria-labelledby="label-permissions">
|
<div className="form-row">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
<span id="group-label" className="group-label">
|
||||||
<div>
|
{intl.formatMessage(messages.notificationtypes)}
|
||||||
<div
|
</span>
|
||||||
className="text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
|
<div className="form-input">
|
||||||
id="label-types"
|
<div className="max-w-lg">
|
||||||
>
|
<NotificationTypeSelector
|
||||||
{intl.formatMessage(messages.notificationtypes)}
|
currentTypes={values.types}
|
||||||
</div>
|
onUpdate={(newTypes) =>
|
||||||
</div>
|
setFieldValue('types', newTypes)
|
||||||
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
}
|
||||||
<div className="max-w-lg">
|
/>
|
||||||
<NotificationTypeSelector
|
|
||||||
currentTypes={values.types}
|
|
||||||
onUpdate={(newTypes) =>
|
|
||||||
setFieldValue('types', newTypes)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -151,31 +151,20 @@ const NotificationsWebhook: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form className="section">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="enabled" className="checkbox-label">
|
||||||
htmlFor="enabled"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.agentenabled)}
|
{intl.formatMessage(messages.agentenabled)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field type="checkbox" id="enabled" name="enabled" />
|
||||||
type="checkbox"
|
|
||||||
id="enabled"
|
|
||||||
name="enabled"
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="name" className="text-label">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.webhookUrl)}
|
{intl.formatMessage(messages.webhookUrl)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="webhookUrl"
|
id="webhookUrl"
|
||||||
@@ -184,40 +173,28 @@ const NotificationsWebhook: React.FC = () => {
|
|||||||
placeholder={intl.formatMessage(
|
placeholder={intl.formatMessage(
|
||||||
messages.webhookUrlPlaceholder
|
messages.webhookUrlPlaceholder
|
||||||
)}
|
)}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.webhookUrl && touched.webhookUrl && (
|
{errors.webhookUrl && touched.webhookUrl && (
|
||||||
<div className="mt-2 text-red-500">{errors.webhookUrl}</div>
|
<div className="error">{errors.webhookUrl}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="name" className="text-label">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.authheader)}
|
{intl.formatMessage(messages.authheader)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field id="authHeader" name="authHeader" type="text" />
|
||||||
id="authHeader"
|
|
||||||
name="authHeader"
|
|
||||||
type="text"
|
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="name" className="text-label">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.customJson)}
|
{intl.formatMessage(messages.customJson)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<JSONEditor
|
<JSONEditor
|
||||||
name="webhook-json-payload"
|
name="webhook-json-payload"
|
||||||
@@ -227,7 +204,7 @@ const NotificationsWebhook: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.jsonPayload && touched.jsonPayload && (
|
{errors.jsonPayload && touched.jsonPayload && (
|
||||||
<div className="mt-2 text-red-500">{errors.jsonPayload}</div>
|
<div className="error">{errors.jsonPayload}</div>
|
||||||
)}
|
)}
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<Button
|
<Button
|
||||||
@@ -275,18 +252,15 @@ const NotificationsWebhook: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div className="mt-8">
|
||||||
<div role="group" aria-labelledby="label-permissions">
|
<div role="group" aria-labelledby="group-label" className="group">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
<div className="sm:grid sm:grid-cols-4 sm:gap-4">
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div id="group-label" className="group-label">
|
||||||
className="text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
|
|
||||||
id="label-types"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.notificationtypes)}
|
{intl.formatMessage(messages.notificationtypes)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="max-w-lg">
|
<div className="max-w-lg">
|
||||||
<NotificationTypeSelector
|
<NotificationTypeSelector
|
||||||
currentTypes={values.types}
|
currentTypes={values.types}
|
||||||
@@ -299,7 +273,7 @@ const NotificationsWebhook: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -93,7 +93,9 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
port: Yup.number().required(
|
port: Yup.number().required(
|
||||||
intl.formatMessage(messages.validationPortRequired)
|
intl.formatMessage(messages.validationPortRequired)
|
||||||
),
|
),
|
||||||
apiKey: Yup.string().required(intl.formatMessage(messages.apiKey)),
|
apiKey: Yup.string().required(
|
||||||
|
intl.formatMessage(messages.validationApiKeyRequired)
|
||||||
|
),
|
||||||
rootFolder: Yup.string().required(
|
rootFolder: Yup.string().required(
|
||||||
intl.formatMessage(messages.validationRootFolderRequired)
|
intl.formatMessage(messages.validationRootFolderRequired)
|
||||||
),
|
),
|
||||||
@@ -284,47 +286,28 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="isDefault" className="checkbox-label">
|
||||||
htmlFor="isDefault"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.defaultserver)}
|
{intl.formatMessage(messages.defaultserver)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field type="checkbox" id="isDefault" name="isDefault" />
|
||||||
type="checkbox"
|
|
||||||
id="isDefault"
|
|
||||||
name="isDefault"
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="is4k" className="checkbox-label">
|
||||||
htmlFor="is4k"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.server4k)}
|
{intl.formatMessage(messages.server4k)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field type="checkbox" id="is4k" name="is4k" />
|
||||||
type="checkbox"
|
|
||||||
id="is4k"
|
|
||||||
name="is4k"
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="name" className="text-label">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.servername)}
|
{intl.formatMessage(messages.servername)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="name"
|
id="name"
|
||||||
@@ -337,25 +320,21 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('name', e.target.value);
|
setFieldValue('name', e.target.value);
|
||||||
}}
|
}}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.name && touched.name && (
|
{errors.name && touched.name && (
|
||||||
<div className="mt-2 text-red-500">{errors.name}</div>
|
<div className="error">{errors.name}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="hostname" className="text-label">
|
||||||
htmlFor="hostname"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.hostname)}
|
{intl.formatMessage(messages.hostname)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<span className="inline-flex items-center px-3 text-gray-100 bg-gray-600 border border-r-0 border-gray-500 cursor-default rounded-l-md sm:text-sm">
|
<span className="protocol">
|
||||||
{values.ssl ? 'https://' : 'http://'}
|
{values.ssl ? 'https://' : 'http://'}
|
||||||
</span>
|
</span>
|
||||||
<Field
|
<Field
|
||||||
@@ -367,23 +346,20 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('hostname', e.target.value);
|
setFieldValue('hostname', e.target.value);
|
||||||
}}
|
}}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 form-input rounded-r-md sm:text-sm sm:leading-5"
|
className="rounded-r-only"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.hostname && touched.hostname && (
|
{errors.hostname && touched.hostname && (
|
||||||
<div className="mt-2 text-red-500">{errors.hostname}</div>
|
<div className="error">{errors.hostname}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="port" className="text-label">
|
||||||
htmlFor="port"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.port)}
|
{intl.formatMessage(messages.port)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
id="port"
|
id="port"
|
||||||
name="port"
|
name="port"
|
||||||
@@ -393,21 +369,17 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('port', e.target.value);
|
setFieldValue('port', e.target.value);
|
||||||
}}
|
}}
|
||||||
className="block w-24 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md shadow-sm form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
{errors.port && touched.port && (
|
{errors.port && touched.port && (
|
||||||
<div className="mt-2 text-red-500">{errors.port}</div>
|
<div className="error">{errors.port}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="ssl" className="checkbox-label">
|
||||||
htmlFor="ssl"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.ssl)}
|
{intl.formatMessage(messages.ssl)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="ssl"
|
id="ssl"
|
||||||
@@ -416,19 +388,15 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('ssl', !values.ssl);
|
setFieldValue('ssl', !values.ssl);
|
||||||
}}
|
}}
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="apiKey" className="text-label">
|
||||||
htmlFor="apiKey"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.apiKey)}
|
{intl.formatMessage(messages.apiKey)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="apiKey"
|
id="apiKey"
|
||||||
@@ -441,22 +409,18 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('apiKey', e.target.value);
|
setFieldValue('apiKey', e.target.value);
|
||||||
}}
|
}}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.apiKey && touched.apiKey && (
|
{errors.apiKey && touched.apiKey && (
|
||||||
<div className="mt-2 text-red-500">{errors.apiKey}</div>
|
<div className="error">{errors.apiKey}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="baseUrl" className="text-label">
|
||||||
htmlFor="baseUrl"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.baseUrl)}
|
{intl.formatMessage(messages.baseUrl)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="baseUrl"
|
id="baseUrl"
|
||||||
@@ -469,30 +433,25 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('baseUrl', e.target.value);
|
setFieldValue('baseUrl', e.target.value);
|
||||||
}}
|
}}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.baseUrl && touched.baseUrl && (
|
{errors.baseUrl && touched.baseUrl && (
|
||||||
<div className="mt-2 text-red-500">{errors.baseUrl}</div>
|
<div className="error">{errors.baseUrl}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="activeProfileId" className="text-label">
|
||||||
htmlFor="activeProfileId"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.qualityprofile)}
|
{intl.formatMessage(messages.qualityprofile)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
as="select"
|
as="select"
|
||||||
id="activeProfileId"
|
id="activeProfileId"
|
||||||
name="activeProfileId"
|
name="activeProfileId"
|
||||||
disabled={!isValidated || isTesting}
|
disabled={!isValidated || isTesting}
|
||||||
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 bg-gray-700 border-gray-500 rounded-md form-select focus:outline-none focus:ring-blue focus:border-gray-500 sm:text-sm sm:leading-5 disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">
|
<option value="">
|
||||||
{isTesting
|
{isTesting
|
||||||
@@ -515,28 +474,22 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
{errors.activeProfileId && touched.activeProfileId && (
|
{errors.activeProfileId && touched.activeProfileId && (
|
||||||
<div className="mt-2 text-red-500">
|
<div className="error">{errors.activeProfileId}</div>
|
||||||
{errors.activeProfileId}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-8005">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="rootFolder" className="text-label">
|
||||||
htmlFor="rootFolder"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.rootfolder)}
|
{intl.formatMessage(messages.rootfolder)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
as="select"
|
as="select"
|
||||||
id="rootFolder"
|
id="rootFolder"
|
||||||
name="rootFolder"
|
name="rootFolder"
|
||||||
disabled={!isValidated || isTesting}
|
disabled={!isValidated || isTesting}
|
||||||
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 bg-gray-700 border-gray-500 rounded-md form-select focus:outline-none focus:ring-blue focus:border-gray-500 sm:text-sm sm:leading-5 disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">
|
<option value="">
|
||||||
{isTesting
|
{isTesting
|
||||||
@@ -557,27 +510,21 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
{errors.rootFolder && touched.rootFolder && (
|
{errors.rootFolder && touched.rootFolder && (
|
||||||
<div className="mt-2 text-red-500">
|
<div className="error">{errors.rootFolder}</div>
|
||||||
{errors.rootFolder}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="minimumAvailability" className="text-label">
|
||||||
htmlFor="minimumAvailability"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.minimumAvailability)}
|
{intl.formatMessage(messages.minimumAvailability)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
as="select"
|
as="select"
|
||||||
id="minimumAvailability"
|
id="minimumAvailability"
|
||||||
name="minimumAvailability"
|
name="minimumAvailability"
|
||||||
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 bg-gray-700 border-gray-500 rounded-md form-select focus:outline-none focus:ring-blue focus:border-gray-500 sm:text-sm sm:leading-5"
|
|
||||||
>
|
>
|
||||||
<option value="announced">Announced</option>
|
<option value="announced">Announced</option>
|
||||||
<option value="inCinemas">In Cinemas</option>
|
<option value="inCinemas">In Cinemas</option>
|
||||||
@@ -587,20 +534,17 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
{errors.minimumAvailability &&
|
{errors.minimumAvailability &&
|
||||||
touched.minimumAvailability && (
|
touched.minimumAvailability && (
|
||||||
<div className="mt-2 text-red-500">
|
<div className="error">
|
||||||
{errors.minimumAvailability}
|
{errors.minimumAvailability}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="externalUrl" className="text-label">
|
||||||
htmlFor="externalUrl"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.externalUrl)}
|
{intl.formatMessage(messages.externalUrl)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="externalUrl"
|
id="externalUrl"
|
||||||
@@ -609,45 +553,34 @@ const RadarrModal: React.FC<RadarrModalProps> = ({
|
|||||||
placeholder={intl.formatMessage(
|
placeholder={intl.formatMessage(
|
||||||
messages.externalUrlPlaceholder
|
messages.externalUrlPlaceholder
|
||||||
)}
|
)}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.externalUrl && touched.externalUrl && (
|
{errors.externalUrl && touched.externalUrl && (
|
||||||
<div className="mt-2 text-red-500">
|
<div className="error">{errors.externalUrl}</div>
|
||||||
{errors.externalUrl}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="syncEnabled" className="checkbox-label">
|
||||||
htmlFor="syncEnabled"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.syncEnabled)}
|
{intl.formatMessage(messages.syncEnabled)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="syncEnabled"
|
id="syncEnabled"
|
||||||
name="syncEnabled"
|
name="syncEnabled"
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="preventSearch" className="checkbox-label">
|
||||||
htmlFor="preventSearch"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.preventSearch)}
|
{intl.formatMessage(messages.preventSearch)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="preventSearch"
|
id="preventSearch"
|
||||||
name="preventSearch"
|
name="preventSearch"
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ const Release: React.FC<ReleaseProps> = ({
|
|||||||
</Modal>
|
</Modal>
|
||||||
</Transition>
|
</Transition>
|
||||||
<div className="flex items-center justify-center mb-4 sm:mb-0 sm:justify-start">
|
<div className="flex items-center justify-center mb-4 sm:mb-0 sm:justify-start">
|
||||||
<span className="mr-2 text-sm">
|
<span className="mt-1 mr-2 text-xs">
|
||||||
<FormattedRelativeTime
|
<FormattedRelativeTime
|
||||||
value={Math.floor(
|
value={Math.floor(
|
||||||
(new Date(release.created_at).getTime() - Date.now()) / 1000
|
(new Date(release.created_at).getTime() - Date.now()) / 1000
|
||||||
@@ -109,16 +109,16 @@ const Release: React.FC<ReleaseProps> = ({
|
|||||||
numeric="always"
|
numeric="always"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xl">{release.name}</span>
|
<span className="text-lg">{release.name}</span>
|
||||||
{isLatest && (
|
{isLatest && (
|
||||||
<span className="ml-2">
|
<span className="ml-2 -mt-1">
|
||||||
<Badge badgeType="primary">
|
<Badge badgeType="primary">
|
||||||
{intl.formatMessage(messages.latestversion)}
|
{intl.formatMessage(messages.latestversion)}
|
||||||
</Badge>
|
</Badge>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{release.name.includes(currentVersion) && (
|
{release.name.includes(currentVersion) && (
|
||||||
<span className="ml-2">
|
<span className="ml-2 -mt-1">
|
||||||
<Badge badgeType="success">
|
<Badge badgeType="success">
|
||||||
{intl.formatMessage(messages.currentversion)}
|
{intl.formatMessage(messages.currentversion)}
|
||||||
</Badge>
|
</Badge>
|
||||||
@@ -156,38 +156,38 @@ const Releases: React.FC<ReleasesProps> = ({ currentVersion }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="pb-4 mb-4 text-xl border-b border-gray-800">
|
<h3 className="heading">{intl.formatMessage(messages.releases)}</h3>
|
||||||
{intl.formatMessage(messages.releases)}
|
<div className="section">
|
||||||
|
{currentVersion.startsWith('develop-') && (
|
||||||
|
<Alert title={intl.formatMessage(messages.runningDevelop)}>
|
||||||
|
{intl.formatMessage(messages.runningDevelopMessage, {
|
||||||
|
GithubLink: function GithubLink(msg) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href="https://github.com/sct/overseerr"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-yellow-100 underline transition duration-300 hover:text-white"
|
||||||
|
>
|
||||||
|
{msg}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
{data?.map((release, index) => {
|
||||||
|
return (
|
||||||
|
<div key={`release-${release.id}`} className="mb-2">
|
||||||
|
<Release
|
||||||
|
release={release}
|
||||||
|
currentVersion={currentVersion}
|
||||||
|
isLatest={index === 0}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
{currentVersion.startsWith('develop-') && (
|
|
||||||
<Alert title={intl.formatMessage(messages.runningDevelop)}>
|
|
||||||
{intl.formatMessage(messages.runningDevelopMessage, {
|
|
||||||
GithubLink: function GithubLink(msg) {
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
href="https://github.com/sct/overseerr"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="text-yellow-100 underline transition duration-300 hover:text-white"
|
|
||||||
>
|
|
||||||
{msg}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
{data?.map((release, index) => {
|
|
||||||
return (
|
|
||||||
<div key={`release-${release.id}`} className="mb-2">
|
|
||||||
<Release
|
|
||||||
release={release}
|
|
||||||
currentVersion={currentVersion}
|
|
||||||
isLatest={index === 0}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const SettingsAbout: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-8">
|
<div className="section">
|
||||||
<List title={intl.formatMessage(messages.overseerrinformation)}>
|
<List title={intl.formatMessage(messages.overseerrinformation)}>
|
||||||
<List.Item title={intl.formatMessage(messages.version)}>
|
<List.Item title={intl.formatMessage(messages.version)}>
|
||||||
{data.version}
|
{data.version}
|
||||||
@@ -55,7 +55,7 @@ const SettingsAbout: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-8">
|
<div className="section">
|
||||||
<List title={intl.formatMessage(messages.gettingsupport)}>
|
<List title={intl.formatMessage(messages.gettingsupport)}>
|
||||||
<List.Item title={intl.formatMessage(messages.documentation)}>
|
<List.Item title={intl.formatMessage(messages.documentation)}>
|
||||||
<a
|
<a
|
||||||
@@ -89,7 +89,7 @@ const SettingsAbout: React.FC = () => {
|
|||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-8">
|
<div className="section">
|
||||||
<List title={intl.formatMessage(messages.supportoverseerr)}>
|
<List title={intl.formatMessage(messages.supportoverseerr)}>
|
||||||
<List.Item
|
<List.Item
|
||||||
title={`${intl.formatMessage(messages.helppaycoffee)} ☕️`}
|
title={`${intl.formatMessage(messages.helppaycoffee)} ☕️`}
|
||||||
@@ -105,7 +105,7 @@ const SettingsAbout: React.FC = () => {
|
|||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-8">
|
<div className="section">
|
||||||
<Releases currentVersion={data.version} />
|
<Releases currentVersion={data.version} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ const messages = defineMessages({
|
|||||||
canceljob: 'Cancel Job',
|
canceljob: 'Cancel Job',
|
||||||
jobstarted: '{jobname} started.',
|
jobstarted: '{jobname} started.',
|
||||||
jobcancelled: '{jobname} cancelled.',
|
jobcancelled: '{jobname} cancelled.',
|
||||||
|
process: 'Process',
|
||||||
|
command: 'Command',
|
||||||
cache: 'Cache',
|
cache: 'Cache',
|
||||||
cacheDescription:
|
cacheDescription:
|
||||||
'Overseerr caches requests to external API endpoints to optimize performance and avoid making unnecessary API calls.',
|
'Overseerr caches requests to external API endpoints to optimize performance and avoid making unnecessary API calls.',
|
||||||
@@ -97,100 +99,103 @@ const SettingsJobs: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-4">
|
<div className="mb-6">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-200">
|
<h3 className="heading">{intl.formatMessage(messages.jobs)}</h3>
|
||||||
{intl.formatMessage(messages.jobs)}
|
<p className="description">
|
||||||
</h3>
|
|
||||||
<p className="max-w-2xl mt-1 text-sm leading-5 text-gray-500">
|
|
||||||
{intl.formatMessage(messages.jobsDescription)}
|
{intl.formatMessage(messages.jobsDescription)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Table>
|
<div className="section">
|
||||||
<thead>
|
<Table>
|
||||||
<Table.TH>{intl.formatMessage(messages.jobname)}</Table.TH>
|
<thead>
|
||||||
<Table.TH>{intl.formatMessage(messages.jobtype)}</Table.TH>
|
<Table.TH>{intl.formatMessage(messages.jobname)}</Table.TH>
|
||||||
<Table.TH>{intl.formatMessage(messages.nextexecution)}</Table.TH>
|
<Table.TH>{intl.formatMessage(messages.jobtype)}</Table.TH>
|
||||||
<Table.TH></Table.TH>
|
<Table.TH>{intl.formatMessage(messages.nextexecution)}</Table.TH>
|
||||||
</thead>
|
<Table.TH></Table.TH>
|
||||||
<Table.TBody>
|
</thead>
|
||||||
{data?.map((job) => (
|
<Table.TBody>
|
||||||
<tr key={`job-list-${job.id}`}>
|
{data?.map((job) => (
|
||||||
<Table.TD>
|
<tr key={`job-list-${job.id}`}>
|
||||||
<div className="flex items-center text-sm leading-5 text-white">
|
<Table.TD>
|
||||||
{job.running && <Spinner className="w-5 h-5 mr-2" />}
|
<div className="flex items-center text-sm leading-5 text-white">
|
||||||
<span>{job.name}</span>
|
{job.running && <Spinner className="w-5 h-5 mr-2" />}
|
||||||
</div>
|
<span>{job.name}</span>
|
||||||
</Table.TD>
|
</div>
|
||||||
<Table.TD>
|
</Table.TD>
|
||||||
<Badge
|
<Table.TD>
|
||||||
badgeType={job.type === 'process' ? 'primary' : 'warning'}
|
<Badge
|
||||||
className="uppercase"
|
badgeType={job.type === 'process' ? 'primary' : 'warning'}
|
||||||
>
|
className="uppercase"
|
||||||
{job.type}
|
>
|
||||||
</Badge>
|
{job.type === 'process'
|
||||||
</Table.TD>
|
? intl.formatMessage(messages.process)
|
||||||
<Table.TD>
|
: intl.formatMessage(messages.command)}
|
||||||
<div className="text-sm leading-5 text-white">
|
</Badge>
|
||||||
<FormattedRelativeTime
|
</Table.TD>
|
||||||
value={Math.floor(
|
<Table.TD>
|
||||||
(new Date(job.nextExecutionTime).getTime() - Date.now()) /
|
<div className="text-sm leading-5 text-white">
|
||||||
1000
|
<FormattedRelativeTime
|
||||||
)}
|
value={Math.floor(
|
||||||
updateIntervalInSeconds={1}
|
(new Date(job.nextExecutionTime).getTime() -
|
||||||
/>
|
Date.now()) /
|
||||||
</div>
|
1000
|
||||||
</Table.TD>
|
)}
|
||||||
<Table.TD alignText="right">
|
updateIntervalInSeconds={1}
|
||||||
{job.running ? (
|
/>
|
||||||
<Button buttonType="danger" onClick={() => cancelJob(job)}>
|
</div>
|
||||||
{intl.formatMessage(messages.canceljob)}
|
</Table.TD>
|
||||||
</Button>
|
<Table.TD alignText="right">
|
||||||
) : (
|
{job.running ? (
|
||||||
<Button buttonType="primary" onClick={() => runJob(job)}>
|
<Button buttonType="danger" onClick={() => cancelJob(job)}>
|
||||||
{intl.formatMessage(messages.runnow)}
|
{intl.formatMessage(messages.canceljob)}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
) : (
|
||||||
</Table.TD>
|
<Button buttonType="primary" onClick={() => runJob(job)}>
|
||||||
</tr>
|
{intl.formatMessage(messages.runnow)}
|
||||||
))}
|
</Button>
|
||||||
</Table.TBody>
|
)}
|
||||||
</Table>
|
</Table.TD>
|
||||||
<div className="my-4">
|
</tr>
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-200">
|
))}
|
||||||
{intl.formatMessage(messages.cache)}
|
</Table.TBody>
|
||||||
</h3>
|
</Table>
|
||||||
<p className="max-w-2xl mt-1 text-sm leading-5 text-gray-500">
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="heading">{intl.formatMessage(messages.cache)}</h3>
|
||||||
|
<p className="description">
|
||||||
{intl.formatMessage(messages.cacheDescription)}
|
{intl.formatMessage(messages.cacheDescription)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Table>
|
<div className="section">
|
||||||
<thead>
|
<Table>
|
||||||
<Table.TH>{intl.formatMessage(messages.cachename)}</Table.TH>
|
<thead>
|
||||||
<Table.TH>{intl.formatMessage(messages.cachehits)}</Table.TH>
|
<Table.TH>{intl.formatMessage(messages.cachename)}</Table.TH>
|
||||||
<Table.TH>{intl.formatMessage(messages.cachemisses)}</Table.TH>
|
<Table.TH>{intl.formatMessage(messages.cachehits)}</Table.TH>
|
||||||
<Table.TH>{intl.formatMessage(messages.cachekeys)}</Table.TH>
|
<Table.TH>{intl.formatMessage(messages.cachemisses)}</Table.TH>
|
||||||
<Table.TH>{intl.formatMessage(messages.cacheksize)}</Table.TH>
|
<Table.TH>{intl.formatMessage(messages.cachekeys)}</Table.TH>
|
||||||
<Table.TH>{intl.formatMessage(messages.cachevsize)}</Table.TH>
|
<Table.TH>{intl.formatMessage(messages.cacheksize)}</Table.TH>
|
||||||
<Table.TH></Table.TH>
|
<Table.TH>{intl.formatMessage(messages.cachevsize)}</Table.TH>
|
||||||
</thead>
|
<Table.TH></Table.TH>
|
||||||
<Table.TBody>
|
</thead>
|
||||||
{cacheData?.map((cache) => (
|
<Table.TBody>
|
||||||
<tr key={`cache-list-${cache.id}`}>
|
{cacheData?.map((cache) => (
|
||||||
<Table.TD>{cache.name}</Table.TD>
|
<tr key={`cache-list-${cache.id}`}>
|
||||||
<Table.TD>{intl.formatNumber(cache.stats.hits)}</Table.TD>
|
<Table.TD>{cache.name}</Table.TD>
|
||||||
<Table.TD>{intl.formatNumber(cache.stats.misses)}</Table.TD>
|
<Table.TD>{intl.formatNumber(cache.stats.hits)}</Table.TD>
|
||||||
<Table.TD>{intl.formatNumber(cache.stats.keys)}</Table.TD>
|
<Table.TD>{intl.formatNumber(cache.stats.misses)}</Table.TD>
|
||||||
<Table.TD>{formatBytes(cache.stats.ksize)}</Table.TD>
|
<Table.TD>{intl.formatNumber(cache.stats.keys)}</Table.TD>
|
||||||
<Table.TD>{formatBytes(cache.stats.vsize)}</Table.TD>
|
<Table.TD>{formatBytes(cache.stats.ksize)}</Table.TD>
|
||||||
<Table.TD alignText="right">
|
<Table.TD>{formatBytes(cache.stats.vsize)}</Table.TD>
|
||||||
<Button buttonType="danger" onClick={() => flushCache(cache)}>
|
<Table.TD alignText="right">
|
||||||
{intl.formatMessage(messages.flushcache)}
|
<Button buttonType="danger" onClick={() => flushCache(cache)}>
|
||||||
</Button>
|
{intl.formatMessage(messages.flushcache)}
|
||||||
</Table.TD>
|
</Button>
|
||||||
</tr>
|
</Table.TD>
|
||||||
))}
|
</tr>
|
||||||
</Table.TBody>
|
))}
|
||||||
</Table>
|
</Table.TBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -106,7 +106,6 @@ const SettingsLayout: React.FC = ({ children }) => {
|
|||||||
)?.route
|
)?.route
|
||||||
}
|
}
|
||||||
aria-label="Selected tab"
|
aria-label="Selected tab"
|
||||||
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 text-white transition duration-150 ease-in-out bg-gray-800 border-gray-700 rounded-md form-select focus:outline-none focus:ring-blue focus:border-blue-300 sm:text-sm sm:leading-5"
|
|
||||||
>
|
>
|
||||||
{settingsRoutes.map((route, index) => (
|
{settingsRoutes.map((route, index) => (
|
||||||
<SettingsLink
|
<SettingsLink
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ const messages = defineMessages({
|
|||||||
hideAvailable: 'Hide Available Media',
|
hideAvailable: 'Hide Available Media',
|
||||||
csrfProtection: 'Enable CSRF Protection',
|
csrfProtection: 'Enable CSRF Protection',
|
||||||
csrfProtectionTip:
|
csrfProtectionTip:
|
||||||
'Sets external API access to read-only (Overseerr must be reloaded for changes to take effect)',
|
'Sets external API access to read-only (requires HTTPS and Overseerr must be reloaded for changes to take effect)',
|
||||||
|
csrfProtectionHoverTip:
|
||||||
|
'Do NOT enable this unless you understand what you are doing!',
|
||||||
trustProxy: 'Enable Proxy Support',
|
trustProxy: 'Enable Proxy Support',
|
||||||
trustProxyTip:
|
trustProxyTip:
|
||||||
'Allows Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be reloaded for changes to take effect)',
|
'Allows Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be reloaded for changes to take effect)',
|
||||||
@@ -66,15 +68,15 @@ const SettingsMain: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div className="mb-6">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-200">
|
<h3 className="heading">
|
||||||
{intl.formatMessage(messages.generalsettings)}
|
{intl.formatMessage(messages.generalsettings)}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="max-w-2xl mt-1 text-sm leading-5 text-gray-500">
|
<p className="description">
|
||||||
{intl.formatMessage(messages.generalsettingsDescription)}
|
{intl.formatMessage(messages.generalsettingsDescription)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5">
|
<div className="section">
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
applicationUrl: data?.applicationUrl,
|
applicationUrl: data?.applicationUrl,
|
||||||
@@ -110,21 +112,18 @@ const SettingsMain: React.FC = () => {
|
|||||||
>
|
>
|
||||||
{({ isSubmitting, values, setFieldValue }) => {
|
{({ isSubmitting, values, setFieldValue }) => {
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form className="section">
|
||||||
{userHasPermission(Permission.ADMIN) && (
|
{userHasPermission(Permission.ADMIN) && (
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="apiKey" className="text-label">
|
||||||
htmlFor="username"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.apikey)}
|
{intl.formatMessage(messages.apikey)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="apiKey"
|
id="apiKey"
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-none form-input rounded-l-md sm:text-sm sm:leading-5"
|
className="rounded-l-only"
|
||||||
value={data?.apiKey}
|
value={data?.apiKey}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
@@ -137,7 +136,7 @@ const SettingsMain: React.FC = () => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
regenerate();
|
regenerate();
|
||||||
}}
|
}}
|
||||||
className="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium leading-5 text-white transition duration-150 ease-in-out bg-indigo-500 border border-gray-500 rounded-r-md hover:bg-indigo-400 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700"
|
className="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium leading-5 text-white transition duration-150 ease-in-out bg-indigo-600 border border-gray-500 rounded-r-md hover:bg-indigo-500 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
className="w-5 h-5"
|
className="w-5 h-5"
|
||||||
@@ -156,40 +155,31 @@ const SettingsMain: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="applicationUrl" className="text-label">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.applicationurl)}
|
{intl.formatMessage(messages.applicationurl)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="applicationUrl"
|
id="applicationUrl"
|
||||||
name="applicationUrl"
|
name="applicationUrl"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="https://os.example.com"
|
placeholder="https://os.example.com"
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="trustProxy" className="checkbox-label">
|
||||||
htmlFor="trustProxy"
|
<span className="mr-2">
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
{intl.formatMessage(messages.trustProxy)}
|
||||||
>
|
</span>
|
||||||
<div className="flex flex-col">
|
<span className="label-tip">
|
||||||
<span className="mr-2">
|
{intl.formatMessage(messages.trustProxyTip)}
|
||||||
{intl.formatMessage(messages.trustProxy)}
|
</span>
|
||||||
</span>
|
|
||||||
<span className="text-gray-500">
|
|
||||||
{intl.formatMessage(messages.trustProxyTip)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="trustProxy"
|
id="trustProxy"
|
||||||
@@ -197,41 +187,37 @@ const SettingsMain: React.FC = () => {
|
|||||||
onChange={() => {
|
onChange={() => {
|
||||||
setFieldValue('trustProxy', !values.trustProxy);
|
setFieldValue('trustProxy', !values.trustProxy);
|
||||||
}}
|
}}
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="csrfProtection" className="checkbox-label">
|
||||||
htmlFor="csrfProtection"
|
<span className="mr-2">
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
{intl.formatMessage(messages.csrfProtection)}
|
||||||
>
|
</span>
|
||||||
<div className="flex flex-col">
|
<Badge badgeType="danger">
|
||||||
<span className="mr-2">
|
{intl.formatMessage(globalMessages.advanced)}
|
||||||
{intl.formatMessage(messages.csrfProtection)}
|
</Badge>
|
||||||
</span>
|
<span className="label-tip">
|
||||||
<span className="text-gray-500">
|
{intl.formatMessage(messages.csrfProtectionTip)}
|
||||||
{intl.formatMessage(messages.csrfProtectionTip)}
|
</span>
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="csrfProtection"
|
id="csrfProtection"
|
||||||
name="csrfProtection"
|
name="csrfProtection"
|
||||||
|
title={intl.formatMessage(
|
||||||
|
messages.csrfProtectionHoverTip
|
||||||
|
)}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setFieldValue('csrfProtection', !values.csrfProtection);
|
setFieldValue('csrfProtection', !values.csrfProtection);
|
||||||
}}
|
}}
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="hideAvailable" className="checkbox-label">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
<span className="mr-2">
|
<span className="mr-2">
|
||||||
{intl.formatMessage(messages.hideAvailable)}
|
{intl.formatMessage(messages.hideAvailable)}
|
||||||
</span>
|
</span>
|
||||||
@@ -239,7 +225,7 @@ const SettingsMain: React.FC = () => {
|
|||||||
{intl.formatMessage(globalMessages.experimental)}
|
{intl.formatMessage(globalMessages.experimental)}
|
||||||
</Badge>
|
</Badge>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="hideAvailable"
|
id="hideAvailable"
|
||||||
@@ -247,38 +233,31 @@ const SettingsMain: React.FC = () => {
|
|||||||
onChange={() => {
|
onChange={() => {
|
||||||
setFieldValue('hideAvailable', !values.hideAvailable);
|
setFieldValue('hideAvailable', !values.hideAvailable);
|
||||||
}}
|
}}
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
<div
|
||||||
<div role="group" aria-labelledby="label-permissions">
|
role="group"
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
aria-labelledby="group-label"
|
||||||
<div>
|
className="group"
|
||||||
<div
|
>
|
||||||
className="text-base font-medium leading-6 text-gray-400 sm:text-sm sm:leading-5"
|
<div className="form-row">
|
||||||
id="label-permissions"
|
<span id="group-label" className="group-label">
|
||||||
>
|
{intl.formatMessage(messages.defaultPermissions)}
|
||||||
{intl.formatMessage(messages.defaultPermissions)}
|
</span>
|
||||||
</div>
|
<div className="form-input">
|
||||||
</div>
|
<div className="max-w-lg">
|
||||||
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
<PermissionEdit
|
||||||
<div className="max-w-lg">
|
currentPermission={values.defaultPermissions}
|
||||||
<PermissionEdit
|
onUpdate={(newPermissions) =>
|
||||||
currentPermission={values.defaultPermissions}
|
setFieldValue('defaultPermissions', newPermissions)
|
||||||
onUpdate={(newPermissions) =>
|
}
|
||||||
setFieldValue(
|
/>
|
||||||
'defaultPermissions',
|
|
||||||
newPermissions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const messages = defineMessages({
|
|||||||
notificationsettingssaved: 'Notification settings saved!',
|
notificationsettingssaved: 'Notification settings saved!',
|
||||||
notificationsettingsfailed: 'Notification settings failed to save.',
|
notificationsettingsfailed: 'Notification settings failed to save.',
|
||||||
enablenotifications: 'Enable Notifications',
|
enablenotifications: 'Enable Notifications',
|
||||||
autoapprovedrequests: 'Send Notifications for Auto-Approved Requests',
|
autoapprovedrequests: 'Enable Notifications for Auto-Approved Requests',
|
||||||
});
|
});
|
||||||
|
|
||||||
interface SettingsRoute {
|
interface SettingsRoute {
|
||||||
@@ -163,14 +163,14 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-200">
|
<h3 className="heading">
|
||||||
{intl.formatMessage(messages.notificationsettings)}
|
{intl.formatMessage(messages.notificationsettings)}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="max-w-2xl mt-1 text-sm leading-5 text-gray-500">
|
<p className="description">
|
||||||
{intl.formatMessage(messages.notificationsettingsDescription)}
|
{intl.formatMessage(messages.notificationsettingsDescription)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5">
|
<div className="section">
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
enabled: data.enabled,
|
enabled: data.enabled,
|
||||||
@@ -202,17 +202,14 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
|||||||
>
|
>
|
||||||
{({ isSubmitting, values, setFieldValue }) => {
|
{({ isSubmitting, values, setFieldValue }) => {
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form className="section">
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="name" className="checkbox-label">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
<span className="mr-2">
|
<span className="mr-2">
|
||||||
{intl.formatMessage(messages.enablenotifications)}
|
{intl.formatMessage(messages.enablenotifications)}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="enabled"
|
id="enabled"
|
||||||
@@ -220,20 +217,16 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
|||||||
onChange={() => {
|
onChange={() => {
|
||||||
setFieldValue('enabled', !values.enabled);
|
setFieldValue('enabled', !values.enabled);
|
||||||
}}
|
}}
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="name" className="checkbox-label">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
<span className="mr-2">
|
<span className="mr-2">
|
||||||
{intl.formatMessage(messages.autoapprovedrequests)}
|
{intl.formatMessage(messages.autoapprovedrequests)}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="autoapprovalEnabled"
|
id="autoapprovalEnabled"
|
||||||
@@ -244,11 +237,10 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
|||||||
!values.autoapprovalEnabled
|
!values.autoapprovalEnabled
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
@@ -269,10 +261,10 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
|||||||
</Formik>
|
</Formik>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-10 mb-6">
|
<div className="mt-10 mb-6">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-200">
|
<h3 className="heading">
|
||||||
{intl.formatMessage(messages.notificationAgentsSettings)}
|
{intl.formatMessage(messages.notificationAgentsSettings)}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="max-w-2xl mt-1 text-sm leading-5 text-gray-500">
|
<p className="description">
|
||||||
{intl.formatMessage(messages.notificationAgentSettingsDescription)}
|
{intl.formatMessage(messages.notificationAgentSettingsDescription)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -294,7 +286,6 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
|||||||
)?.route
|
)?.route
|
||||||
}
|
}
|
||||||
aria-label="Selected tab"
|
aria-label="Selected tab"
|
||||||
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 text-white transition duration-150 ease-in-out bg-gray-800 border-gray-700 rounded-md form-select focus:outline-none focus:ring-blue focus:border-blue-300 sm:text-sm sm:leading-5"
|
|
||||||
>
|
>
|
||||||
{settingsRoutes.map((route, index) => (
|
{settingsRoutes.map((route, index) => (
|
||||||
<SettingsLink
|
<SettingsLink
|
||||||
@@ -322,7 +313,7 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-10">{children}</div>
|
<div className="section">{children}</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const messages = defineMessages({
|
|||||||
serverLocal: 'local',
|
serverLocal: 'local',
|
||||||
serverRemote: 'remote',
|
serverRemote: 'remote',
|
||||||
serverConnected: 'connected',
|
serverConnected: 'connected',
|
||||||
serverpresetManualMessage: 'Manually configure',
|
serverpresetManualMessage: 'Manual configuration',
|
||||||
serverpresetRefreshing: 'Retrieving servers…',
|
serverpresetRefreshing: 'Retrieving servers…',
|
||||||
serverpresetLoad: 'Press the button to load available servers',
|
serverpresetLoad: 'Press the button to load available servers',
|
||||||
toastPlexRefresh: 'Retrieving server list from Plex',
|
toastPlexRefresh: 'Retrieving server list from Plex',
|
||||||
@@ -259,14 +259,14 @@ const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div className="mb-6">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-200">
|
<h3 className="heading">
|
||||||
<FormattedMessage {...messages.plexsettings} />
|
<FormattedMessage {...messages.plexsettings} />
|
||||||
</h3>
|
</h3>
|
||||||
<p className="max-w-2xl mt-1 text-sm leading-5 text-gray-500">
|
<p className="description">
|
||||||
<FormattedMessage {...messages.plexsettingsDescription} />
|
<FormattedMessage {...messages.plexsettingsDescription} />
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-6 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="section">
|
||||||
<Alert title={intl.formatMessage(messages.settingUpPlex)} type="info">
|
<Alert title={intl.formatMessage(messages.settingUpPlex)} type="info">
|
||||||
{intl.formatMessage(messages.settingUpPlexDescription, {
|
{intl.formatMessage(messages.settingUpPlexDescription, {
|
||||||
RegisterPlexTVLink: function RegisterPlexTVLink(msg) {
|
RegisterPlexTVLink: function RegisterPlexTVLink(msg) {
|
||||||
@@ -346,200 +346,172 @@ const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
|
|||||||
isSubmitting,
|
isSubmitting,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit}>
|
<form className="section" onSubmit={handleSubmit}>
|
||||||
<div className="mt-6 sm:mt-5">
|
<div className="form-row">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<label htmlFor="name" className="text-label">
|
||||||
<label
|
<div className="flex flex-col">
|
||||||
htmlFor="name"
|
<span className="mr-2">
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
<FormattedMessage {...messages.servername} />
|
||||||
>
|
</span>
|
||||||
<div className="flex flex-col">
|
<span className="text-gray-500">
|
||||||
<span className="mr-2">
|
<FormattedMessage {...messages.servernameTip} />
|
||||||
<FormattedMessage {...messages.servername} />
|
</span>
|
||||||
</span>
|
|
||||||
<span className="text-gray-500">
|
|
||||||
<FormattedMessage {...messages.servernameTip} />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="name"
|
|
||||||
name="name"
|
|
||||||
placeholder={intl.formatMessage(
|
|
||||||
messages.servernamePlaceholder
|
|
||||||
)}
|
|
||||||
value={data?.name}
|
|
||||||
readOnly
|
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</label>
|
||||||
<div className="mt-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-input">
|
||||||
<label
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
htmlFor="preset"
|
<input
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
type="text"
|
||||||
>
|
id="name"
|
||||||
<FormattedMessage {...messages.serverpreset} />
|
name="name"
|
||||||
</label>
|
placeholder={intl.formatMessage(
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
messages.servernamePlaceholder
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm input-group">
|
)}
|
||||||
<select
|
value={data?.name}
|
||||||
id="preset"
|
readOnly
|
||||||
name="preset"
|
/>
|
||||||
placeholder={intl.formatMessage(
|
|
||||||
messages.serverpresetPlaceholder
|
|
||||||
)}
|
|
||||||
value={values.selectedPreset}
|
|
||||||
disabled={!availableServers || isRefreshingPresets}
|
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-none rounded-l-md form-input sm:text-sm sm:leading-5"
|
|
||||||
onChange={async (e) => {
|
|
||||||
const targPreset =
|
|
||||||
availablePresets[Number(e.target.value)];
|
|
||||||
if (targPreset) {
|
|
||||||
setFieldValue('hostname', targPreset.host);
|
|
||||||
setFieldValue('port', targPreset.port);
|
|
||||||
setFieldValue('useSsl', targPreset.ssl);
|
|
||||||
}
|
|
||||||
setFieldTouched('hostname');
|
|
||||||
setFieldTouched('port');
|
|
||||||
setFieldTouched('useSsl');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<option value="manual">
|
|
||||||
{availableServers || isRefreshingPresets
|
|
||||||
? isRefreshingPresets
|
|
||||||
? intl.formatMessage(
|
|
||||||
messages.serverpresetRefreshing
|
|
||||||
)
|
|
||||||
: intl.formatMessage(
|
|
||||||
messages.serverpresetManualMessage
|
|
||||||
)
|
|
||||||
: intl.formatMessage(messages.serverpresetLoad)}
|
|
||||||
</option>
|
|
||||||
{availablePresets.map((server, index) => (
|
|
||||||
<option
|
|
||||||
key={`preset-server-${index}`}
|
|
||||||
value={index}
|
|
||||||
disabled={!server.status}
|
|
||||||
>
|
|
||||||
{`
|
|
||||||
${server.name} (${server.address})
|
|
||||||
[${
|
|
||||||
server.local
|
|
||||||
? intl.formatMessage(messages.serverLocal)
|
|
||||||
: intl.formatMessage(messages.serverRemote)
|
|
||||||
}]
|
|
||||||
${server.status ? '' : '(' + server.message + ')'}
|
|
||||||
`}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<button
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
refreshPresetServers();
|
|
||||||
}}
|
|
||||||
className="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium leading-5 text-white transition duration-150 ease-in-out bg-indigo-500 border border-gray-500 rounded-r-md hover:bg-indigo-400 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className={`w-5 h-5 ${
|
|
||||||
isRefreshingPresets ? 'animate-spin' : ''
|
|
||||||
}`}
|
|
||||||
fill="currentColor"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
|
|
||||||
clipRule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<div className="mt-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
|
||||||
<label
|
|
||||||
htmlFor="hostname"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
<FormattedMessage {...messages.hostname} />
|
|
||||||
</label>
|
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
|
||||||
<span className="inline-flex items-center px-3 text-gray-100 bg-gray-800 border border-r-0 border-gray-500 cursor-default rounded-l-md sm:text-sm">
|
|
||||||
{values.useSsl ? 'https://' : 'http://'}
|
|
||||||
</span>
|
|
||||||
<Field
|
|
||||||
type="text"
|
|
||||||
id="hostname"
|
|
||||||
name="hostname"
|
|
||||||
placeholder="127.0.0.1"
|
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 form-input rounded-r-md sm:text-sm sm:leading-5"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{errors.hostname && touched.hostname && (
|
|
||||||
<div className="mt-2 text-red-500">
|
|
||||||
{errors.hostname}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
|
||||||
<label
|
|
||||||
htmlFor="port"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
<FormattedMessage {...messages.port} />
|
|
||||||
</label>
|
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
|
||||||
<div className="max-w-lg rounded-md shadow-sm sm:max-w-xs">
|
|
||||||
<Field
|
|
||||||
type="text"
|
|
||||||
id="port"
|
|
||||||
name="port"
|
|
||||||
placeholder="32400"
|
|
||||||
className="block w-24 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{errors.port && touched.port && (
|
|
||||||
<div className="mt-2 text-red-500">{errors.port}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
|
||||||
<label
|
|
||||||
htmlFor="ssl"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.ssl)}
|
|
||||||
</label>
|
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
|
||||||
<Field
|
|
||||||
type="checkbox"
|
|
||||||
id="useSsl"
|
|
||||||
name="useSsl"
|
|
||||||
onChange={() => {
|
|
||||||
setFieldValue('useSsl', !values.useSsl);
|
|
||||||
}}
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label htmlFor="preset" className="text-label">
|
||||||
|
<FormattedMessage {...messages.serverpreset} />
|
||||||
|
</label>
|
||||||
|
<div className="form-input">
|
||||||
|
<div className="flex max-w-lg rounded-md shadow-sm input-group">
|
||||||
|
<select
|
||||||
|
id="preset"
|
||||||
|
name="preset"
|
||||||
|
placeholder={intl.formatMessage(
|
||||||
|
messages.serverpresetPlaceholder
|
||||||
|
)}
|
||||||
|
value={values.selectedPreset}
|
||||||
|
disabled={!availableServers || isRefreshingPresets}
|
||||||
|
className="rounded-l-only"
|
||||||
|
onChange={async (e) => {
|
||||||
|
const targPreset =
|
||||||
|
availablePresets[Number(e.target.value)];
|
||||||
|
if (targPreset) {
|
||||||
|
setFieldValue('hostname', targPreset.host);
|
||||||
|
setFieldValue('port', targPreset.port);
|
||||||
|
setFieldValue('useSsl', targPreset.ssl);
|
||||||
|
}
|
||||||
|
setFieldTouched('hostname');
|
||||||
|
setFieldTouched('port');
|
||||||
|
setFieldTouched('useSsl');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="manual">
|
||||||
|
{availableServers || isRefreshingPresets
|
||||||
|
? isRefreshingPresets
|
||||||
|
? intl.formatMessage(
|
||||||
|
messages.serverpresetRefreshing
|
||||||
|
)
|
||||||
|
: intl.formatMessage(
|
||||||
|
messages.serverpresetManualMessage
|
||||||
|
)
|
||||||
|
: intl.formatMessage(messages.serverpresetLoad)}
|
||||||
|
</option>
|
||||||
|
{availablePresets.map((server, index) => (
|
||||||
|
<option
|
||||||
|
key={`preset-server-${index}`}
|
||||||
|
value={index}
|
||||||
|
disabled={!server.status}
|
||||||
|
>
|
||||||
|
{`
|
||||||
|
${server.name} (${server.address})
|
||||||
|
[${
|
||||||
|
server.local
|
||||||
|
? intl.formatMessage(messages.serverLocal)
|
||||||
|
: intl.formatMessage(messages.serverRemote)
|
||||||
|
}]
|
||||||
|
${server.status ? '' : '(' + server.message + ')'}
|
||||||
|
`}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
refreshPresetServers();
|
||||||
|
}}
|
||||||
|
className="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium leading-5 text-white transition duration-150 ease-in-out bg-indigo-600 border border-gray-500 rounded-r-md hover:bg-indigo-500 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className={`w-5 h-5 ${
|
||||||
|
isRefreshingPresets ? 'animate-spin' : ''
|
||||||
|
}`}
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label htmlFor="hostname" className="text-label">
|
||||||
|
<FormattedMessage {...messages.hostname} />
|
||||||
|
</label>
|
||||||
|
<div className="form-input">
|
||||||
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
|
<span className="inline-flex items-center px-3 text-gray-100 bg-gray-800 border border-r-0 border-gray-500 cursor-default rounded-l-md sm:text-sm">
|
||||||
|
{values.useSsl ? 'https://' : 'http://'}
|
||||||
|
</span>
|
||||||
|
<Field
|
||||||
|
type="text"
|
||||||
|
id="hostname"
|
||||||
|
name="hostname"
|
||||||
|
placeholder="127.0.0.1"
|
||||||
|
className="rounded-r-only"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{errors.hostname && touched.hostname && (
|
||||||
|
<div className="error">{errors.hostname}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label htmlFor="port" className="text-label">
|
||||||
|
<FormattedMessage {...messages.port} />
|
||||||
|
</label>
|
||||||
|
<div className="form-input">
|
||||||
|
<div className="max-w-lg rounded-md shadow-sm sm:max-w-xs">
|
||||||
|
<Field
|
||||||
|
type="text"
|
||||||
|
id="port"
|
||||||
|
name="port"
|
||||||
|
placeholder="32400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{errors.port && touched.port && (
|
||||||
|
<div className="error">{errors.port}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label htmlFor="ssl" className="checkbox-label">
|
||||||
|
{intl.formatMessage(messages.ssl)}
|
||||||
|
</label>
|
||||||
|
<div className="form-input">
|
||||||
|
<Field
|
||||||
|
type="checkbox"
|
||||||
|
id="useSsl"
|
||||||
|
name="useSsl"
|
||||||
|
onChange={() => {
|
||||||
|
setFieldValue('useSsl', !values.useSsl);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{submitError && (
|
{submitError && (
|
||||||
<div className="mt-6 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="mt-6 sm:gap-4 sm:items-start">
|
||||||
<Alert
|
<Alert
|
||||||
title={intl.formatMessage(
|
title={intl.formatMessage(
|
||||||
messages.toastPlexConnectingFailure
|
messages.toastPlexConnectingFailure
|
||||||
@@ -550,7 +522,7 @@ const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
|
|||||||
</Alert>
|
</Alert>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
@@ -569,32 +541,32 @@ const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Formik>
|
</Formik>
|
||||||
<div className="mt-10">
|
<div className="mt-10 mb-6">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-200">
|
<h3 className="heading">
|
||||||
<FormattedMessage {...messages.plexlibraries} />
|
<FormattedMessage {...messages.plexlibraries} />
|
||||||
</h3>
|
</h3>
|
||||||
<p className="max-w-2xl mt-1 text-sm leading-5 text-gray-500">
|
<p className="description">
|
||||||
<FormattedMessage {...messages.plexlibrariesDescription} />
|
<FormattedMessage {...messages.plexlibrariesDescription} />
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-6">
|
</div>
|
||||||
<Button onClick={() => syncLibraries()} disabled={isSyncing}>
|
<div className="section">
|
||||||
<svg
|
<Button onClick={() => syncLibraries()} disabled={isSyncing}>
|
||||||
className={`${isSyncing ? 'animate-spin' : ''} w-5 h-5 mr-1`}
|
<svg
|
||||||
fill="currentColor"
|
className={`${isSyncing ? 'animate-spin' : ''} w-5 h-5 mr-1`}
|
||||||
viewBox="0 0 20 20"
|
fill="currentColor"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
viewBox="0 0 20 20"
|
||||||
>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path
|
>
|
||||||
fillRule="evenodd"
|
<path
|
||||||
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
|
||||||
/>
|
clipRule="evenodd"
|
||||||
</svg>
|
/>
|
||||||
{isSyncing
|
</svg>
|
||||||
? intl.formatMessage(messages.syncing)
|
{isSyncing
|
||||||
: intl.formatMessage(messages.sync)}
|
? intl.formatMessage(messages.syncing)
|
||||||
</Button>
|
: intl.formatMessage(messages.sync)}
|
||||||
</div>
|
</Button>
|
||||||
<ul className="grid grid-cols-1 gap-5 mt-6 sm:gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
<ul className="grid grid-cols-1 gap-5 mt-6 sm:gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
||||||
{data?.libraries.map((library) => (
|
{data?.libraries.map((library) => (
|
||||||
<LibraryItem
|
<LibraryItem
|
||||||
@@ -606,107 +578,107 @@ const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-10">
|
<div className="mt-10 mb-6">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-200">
|
<h3 className="heading">
|
||||||
<FormattedMessage {...messages.manualscan} />
|
<FormattedMessage {...messages.manualscan} />
|
||||||
</h3>
|
</h3>
|
||||||
<p className="max-w-2xl mt-1 text-sm leading-5 text-gray-500">
|
<p className="description">
|
||||||
<FormattedMessage {...messages.manualscanDescription} />
|
<FormattedMessage {...messages.manualscanDescription} />
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-6">
|
</div>
|
||||||
<div className="p-4 bg-gray-800 rounded-md">
|
<div className="section">
|
||||||
<div className="relative w-full h-8 mb-6 overflow-hidden bg-gray-600 rounded-full">
|
<div className="p-4 bg-gray-800 rounded-md">
|
||||||
{dataSync?.running && (
|
<div className="relative w-full h-8 mb-6 overflow-hidden bg-gray-600 rounded-full">
|
||||||
<div
|
{dataSync?.running && (
|
||||||
className="h-8 transition-all duration-200 ease-in-out bg-indigo-600"
|
<div
|
||||||
style={{
|
className="h-8 transition-all duration-200 ease-in-out bg-indigo-600"
|
||||||
width: `${Math.round(
|
style={{
|
||||||
(dataSync.progress / dataSync.total) * 100
|
width: `${Math.round(
|
||||||
)}%`,
|
(dataSync.progress / dataSync.total) * 100
|
||||||
}}
|
)}%`,
|
||||||
/>
|
}}
|
||||||
)}
|
/>
|
||||||
<div className="absolute inset-0 flex items-center justify-center w-full h-8 text-sm">
|
)}
|
||||||
<span>
|
<div className="absolute inset-0 flex items-center justify-center w-full h-8 text-sm">
|
||||||
{dataSync?.running
|
<span>
|
||||||
? `${dataSync.progress} of ${dataSync.total}`
|
{dataSync?.running
|
||||||
: 'Not running'}
|
? `${dataSync.progress} of ${dataSync.total}`
|
||||||
</span>
|
: 'Not running'}
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col w-full sm:flex-row">
|
</div>
|
||||||
{dataSync?.running && (
|
<div className="flex flex-col w-full sm:flex-row">
|
||||||
<>
|
{dataSync?.running && (
|
||||||
{dataSync.currentLibrary && (
|
<>
|
||||||
<div className="flex items-center mb-2 mr-0 sm:mb-0 sm:mr-2">
|
{dataSync.currentLibrary && (
|
||||||
<Badge>
|
<div className="flex items-center mb-2 mr-0 sm:mb-0 sm:mr-2">
|
||||||
<FormattedMessage
|
<Badge>
|
||||||
{...messages.currentlibrary}
|
|
||||||
values={{ name: dataSync.currentLibrary.name }}
|
|
||||||
/>
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Badge badgeType="warning">
|
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
{...messages.librariesRemaining}
|
{...messages.currentlibrary}
|
||||||
values={{
|
values={{ name: dataSync.currentLibrary.name }}
|
||||||
count: dataSync.currentLibrary
|
|
||||||
? dataSync.libraries.slice(
|
|
||||||
dataSync.libraries.findIndex(
|
|
||||||
(library) =>
|
|
||||||
library.id === dataSync.currentLibrary?.id
|
|
||||||
) + 1
|
|
||||||
).length
|
|
||||||
: 0,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</>
|
)}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Badge badgeType="warning">
|
||||||
|
<FormattedMessage
|
||||||
|
{...messages.librariesRemaining}
|
||||||
|
values={{
|
||||||
|
count: dataSync.currentLibrary
|
||||||
|
? dataSync.libraries.slice(
|
||||||
|
dataSync.libraries.findIndex(
|
||||||
|
(library) =>
|
||||||
|
library.id === dataSync.currentLibrary?.id
|
||||||
|
) + 1
|
||||||
|
).length
|
||||||
|
: 0,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<div className="flex-1 text-right">
|
||||||
|
{!dataSync?.running && (
|
||||||
|
<Button buttonType="warning" onClick={() => startScan()}>
|
||||||
|
<svg
|
||||||
|
className="w-5 h-5 mr-1"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<FormattedMessage {...messages.startscan} />
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
<div className="flex-1 text-right">
|
|
||||||
{!dataSync?.running && (
|
|
||||||
<Button buttonType="warning" onClick={() => startScan()}>
|
|
||||||
<svg
|
|
||||||
className="w-5 h-5 mr-1"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<FormattedMessage {...messages.startscan} />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{dataSync?.running && (
|
{dataSync?.running && (
|
||||||
<Button buttonType="danger" onClick={() => cancelScan()}>
|
<Button buttonType="danger" onClick={() => cancelScan()}>
|
||||||
<svg
|
<svg
|
||||||
className="w-5 h-5 mr-1"
|
className="w-5 h-5 mr-1"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
strokeWidth={2}
|
strokeWidth={2}
|
||||||
d="M6 18L18 6M6 6l12 12"
|
d="M6 18L18 6M6 6l12 12"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<FormattedMessage {...messages.cancelscan} />
|
<FormattedMessage {...messages.cancelscan} />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ import Alert from '../Common/Alert';
|
|||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
radarrsettings: 'Radarr Settings',
|
radarrsettings: 'Radarr Settings',
|
||||||
radarrSettingsDescription:
|
radarrSettingsDescription:
|
||||||
'Configure your Radarr connection below. You can have multiple Radarr configurations but only two can be active as defaults at any time (one for standard HD and one for 4K). Administrators can override the server will be used when a new request is made.',
|
'Configure your Radarr connection below. You can have multiple Radarr configurations, but only two can be active as defaults at any time (one for standard HD and one for 4K). Administrators can override the server which is used for new requests.',
|
||||||
sonarrsettings: 'Sonarr Settings',
|
sonarrsettings: 'Sonarr Settings',
|
||||||
sonarrSettingsDescription:
|
sonarrSettingsDescription:
|
||||||
'Configure your Sonarr connection below. You can have multiple Sonarr configurations but only two can be active as defaults at any time (one for standard HD and one for 4K). Administrators can override the server will be used when a new request is made.',
|
'Configure your Sonarr connection below. You can have multiple Sonarr configurations, but only two can be active as defaults at any time (one for standard HD and one for 4K). Administrators can override the server which is used for new requests.',
|
||||||
deleteserverconfirm: 'Are you sure you want to delete this server?',
|
deleteserverconfirm: 'Are you sure you want to delete this server?',
|
||||||
edit: 'Edit',
|
edit: 'Edit',
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
@@ -65,7 +65,7 @@ const ServerInstance: React.FC<ServerInstanceProps> = ({
|
|||||||
<div className="flex items-center justify-between w-full p-6 space-x-6">
|
<div className="flex items-center justify-between w-full p-6 space-x-6">
|
||||||
<div className="flex-1 truncate">
|
<div className="flex-1 truncate">
|
||||||
<div className="flex items-center mb-2 space-x-3">
|
<div className="flex items-center mb-2 space-x-3">
|
||||||
<h3 className="text-sm font-medium leading-5 text-white truncate">
|
<h3 className="font-medium leading-5 text-white truncate">
|
||||||
{name}
|
{name}
|
||||||
</h3>
|
</h3>
|
||||||
{isDefault && (
|
{isDefault && (
|
||||||
@@ -198,11 +198,11 @@ const SettingsServices: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div className="mb-6">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-200">
|
<h3 className="heading">
|
||||||
<FormattedMessage {...messages.radarrsettings} />
|
<FormattedMessage {...messages.radarrsettings} />
|
||||||
</h3>
|
</h3>
|
||||||
<p className="max-w-2xl mt-1 text-sm leading-5 text-gray-500">
|
<p className="description">
|
||||||
<FormattedMessage {...messages.radarrSettingsDescription} />
|
<FormattedMessage {...messages.radarrSettingsDescription} />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -251,7 +251,7 @@ const SettingsServices: React.FC = () => {
|
|||||||
<FormattedMessage {...messages.deleteserverconfirm} />
|
<FormattedMessage {...messages.deleteserverconfirm} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</Transition>
|
</Transition>
|
||||||
<div className="mt-6 sm:mt-5">
|
<div className="section">
|
||||||
{!radarrData && !radarrError && <LoadingSpinner />}
|
{!radarrData && !radarrError && <LoadingSpinner />}
|
||||||
{radarrData && !radarrError && (
|
{radarrData && !radarrError && (
|
||||||
<>
|
<>
|
||||||
@@ -283,10 +283,11 @@ const SettingsServices: React.FC = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<li className="h-32 col-span-1 border-2 border-gray-400 border-dashed rounded-lg shadow sm:h-32">
|
<li className="h-32 col-span-1 border-2 border-gray-400 border-dashed rounded-lg shadow sm:h-44">
|
||||||
<div className="flex items-center justify-center w-full h-full">
|
<div className="flex items-center justify-center w-full h-full">
|
||||||
<Button
|
<Button
|
||||||
buttonType="ghost"
|
buttonType="ghost"
|
||||||
|
className="mt-3 mb-3"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setEditRadarrModal({ open: true, radarr: null })
|
setEditRadarrModal({ open: true, radarr: null })
|
||||||
}
|
}
|
||||||
@@ -311,15 +312,15 @@ const SettingsServices: React.FC = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-10">
|
<div className="mt-10 mb-6">
|
||||||
<h3 className="text-lg font-medium leading-6 text-gray-200">
|
<h3 className="heading">
|
||||||
<FormattedMessage {...messages.sonarrsettings} />
|
<FormattedMessage {...messages.sonarrsettings} />
|
||||||
</h3>
|
</h3>
|
||||||
<p className="max-w-2xl mt-1 text-sm leading-5 text-gray-500">
|
<p className="description">
|
||||||
<FormattedMessage {...messages.sonarrSettingsDescription} />
|
<FormattedMessage {...messages.sonarrSettingsDescription} />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5">
|
<div className="section">
|
||||||
{!sonarrData && !sonarrError && <LoadingSpinner />}
|
{!sonarrData && !sonarrError && <LoadingSpinner />}
|
||||||
{sonarrData && !sonarrError && (
|
{sonarrData && !sonarrError && (
|
||||||
<>
|
<>
|
||||||
@@ -352,7 +353,7 @@ const SettingsServices: React.FC = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<li className="h-32 col-span-1 border-2 border-gray-400 border-dashed rounded-lg shadow sm:h-32">
|
<li className="h-32 col-span-1 border-2 border-gray-400 border-dashed rounded-lg shadow sm:h-44">
|
||||||
<div className="flex items-center justify-center w-full h-full">
|
<div className="flex items-center justify-center w-full h-full">
|
||||||
<Button
|
<Button
|
||||||
buttonType="ghost"
|
buttonType="ghost"
|
||||||
|
|||||||
@@ -293,47 +293,28 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="isDefault" className="checkbox-label">
|
||||||
htmlFor="isDefault"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.defaultserver)}
|
{intl.formatMessage(messages.defaultserver)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field type="checkbox" id="isDefault" name="isDefault" />
|
||||||
type="checkbox"
|
|
||||||
id="isDefault"
|
|
||||||
name="isDefault"
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="is4k" className="checkbox-label">
|
||||||
htmlFor="is4k"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.server4k)}
|
{intl.formatMessage(messages.server4k)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field type="checkbox" id="is4k" name="is4k" />
|
||||||
type="checkbox"
|
|
||||||
id="is4k"
|
|
||||||
name="is4k"
|
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="name" className="text-label">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.servername)}
|
{intl.formatMessage(messages.servername)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="name"
|
id="name"
|
||||||
@@ -346,25 +327,21 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('name', e.target.value);
|
setFieldValue('name', e.target.value);
|
||||||
}}
|
}}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.name && touched.name && (
|
{errors.name && touched.name && (
|
||||||
<div className="mt-2 text-red-500">{errors.name}</div>
|
<div className="error">{errors.name}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="hostname" className="text-label">
|
||||||
htmlFor="hostname"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.hostname)}
|
{intl.formatMessage(messages.hostname)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<span className="inline-flex items-center px-3 text-gray-100 bg-gray-600 border border-r-0 border-gray-500 cursor-default rounded-l-md sm:text-sm">
|
<span className="protocol">
|
||||||
{values.ssl ? 'https://' : 'http://'}
|
{values.ssl ? 'https://' : 'http://'}
|
||||||
</span>
|
</span>
|
||||||
<Field
|
<Field
|
||||||
@@ -376,23 +353,20 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('hostname', e.target.value);
|
setFieldValue('hostname', e.target.value);
|
||||||
}}
|
}}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 form-input rounded-r-md sm:text-sm sm:leading-5"
|
className="rounded-r-only"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.hostname && touched.hostname && (
|
{errors.hostname && touched.hostname && (
|
||||||
<div className="mt-2 text-red-500">{errors.hostname}</div>
|
<div className="error">{errors.hostname}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="port" className="text-label">
|
||||||
htmlFor="port"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.port)}
|
{intl.formatMessage(messages.port)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
id="port"
|
id="port"
|
||||||
name="port"
|
name="port"
|
||||||
@@ -402,21 +376,17 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('port', e.target.value);
|
setFieldValue('port', e.target.value);
|
||||||
}}
|
}}
|
||||||
className="block w-24 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md shadow-sm form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
{errors.port && touched.port && (
|
{errors.port && touched.port && (
|
||||||
<div className="mt-2 text-red-500">{errors.port}</div>
|
<div className="error">{errors.port}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="ssl" className="checkbox-label">
|
||||||
htmlFor="ssl"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.ssl)}
|
{intl.formatMessage(messages.ssl)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="ssl"
|
id="ssl"
|
||||||
@@ -425,19 +395,15 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('ssl', !values.ssl);
|
setFieldValue('ssl', !values.ssl);
|
||||||
}}
|
}}
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="apiKey" className="text-label">
|
||||||
htmlFor="apiKey"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.apiKey)}
|
{intl.formatMessage(messages.apiKey)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="apiKey"
|
id="apiKey"
|
||||||
@@ -450,22 +416,18 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('apiKey', e.target.value);
|
setFieldValue('apiKey', e.target.value);
|
||||||
}}
|
}}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.apiKey && touched.apiKey && (
|
{errors.apiKey && touched.apiKey && (
|
||||||
<div className="mt-2 text-red-500">{errors.apiKey}</div>
|
<div className="error">{errors.apiKey}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="baseUrl" className="text-label">
|
||||||
htmlFor="baseUrl"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.baseUrl)}
|
{intl.formatMessage(messages.baseUrl)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="baseUrl"
|
id="baseUrl"
|
||||||
@@ -478,30 +440,25 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
setIsValidated(false);
|
setIsValidated(false);
|
||||||
setFieldValue('baseUrl', e.target.value);
|
setFieldValue('baseUrl', e.target.value);
|
||||||
}}
|
}}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.baseUrl && touched.baseUrl && (
|
{errors.baseUrl && touched.baseUrl && (
|
||||||
<div className="mt-2 text-red-500">{errors.baseUrl}</div>
|
<div className="error">{errors.baseUrl}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="activeProfileId" className="text-label">
|
||||||
htmlFor="activeProfileId"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.qualityprofile)}
|
{intl.formatMessage(messages.qualityprofile)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
as="select"
|
as="select"
|
||||||
id="activeProfileId"
|
id="activeProfileId"
|
||||||
name="activeProfileId"
|
name="activeProfileId"
|
||||||
disabled={!isValidated || isTesting}
|
disabled={!isValidated || isTesting}
|
||||||
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 bg-gray-700 border-gray-500 rounded-md form-select focus:outline-none focus:ring-blue focus:border-gray-500 sm:text-sm sm:leading-5 disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">
|
<option value="">
|
||||||
{isTesting
|
{isTesting
|
||||||
@@ -524,28 +481,22 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
{errors.activeProfileId && touched.activeProfileId && (
|
{errors.activeProfileId && touched.activeProfileId && (
|
||||||
<div className="mt-2 text-red-500">
|
<div className="error">{errors.activeProfileId}</div>
|
||||||
{errors.activeProfileId}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="rootFolder" className="text-label">
|
||||||
htmlFor="rootFolder"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.rootfolder)}
|
{intl.formatMessage(messages.rootfolder)}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
as="select"
|
as="select"
|
||||||
id="rootFolder"
|
id="rootFolder"
|
||||||
name="rootFolder"
|
name="rootFolder"
|
||||||
disabled={!isValidated || isTesting}
|
disabled={!isValidated || isTesting}
|
||||||
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 bg-gray-700 border-gray-500 rounded-md form-select focus:outline-none focus:ring-blue focus:border-gray-500 sm:text-sm sm:leading-5 disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">
|
<option value="">
|
||||||
{isTesting
|
{isTesting
|
||||||
@@ -566,27 +517,21 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
{errors.rootFolder && touched.rootFolder && (
|
{errors.rootFolder && touched.rootFolder && (
|
||||||
<div className="mt-2 text-red-500">
|
<div className="error">{errors.rootFolder}</div>
|
||||||
{errors.rootFolder}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="activeAnimeProfileId" className="text-label">
|
||||||
htmlFor="activeAnimeProfileId"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.animequalityprofile)}
|
{intl.formatMessage(messages.animequalityprofile)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
as="select"
|
as="select"
|
||||||
id="activeAnimeProfileId"
|
id="activeAnimeProfileId"
|
||||||
name="activeAnimeProfileId"
|
name="activeAnimeProfileId"
|
||||||
disabled={!isValidated || isTesting}
|
disabled={!isValidated || isTesting}
|
||||||
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 bg-gray-700 border-gray-500 rounded-md form-select focus:outline-none focus:ring-blue focus:border-gray-500 sm:text-sm sm:leading-5 disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">
|
<option value="">
|
||||||
{isTesting
|
{isTesting
|
||||||
@@ -610,27 +555,23 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
{errors.activeAnimeProfileId &&
|
{errors.activeAnimeProfileId &&
|
||||||
touched.activeAnimeProfileId && (
|
touched.activeAnimeProfileId && (
|
||||||
<div className="mt-2 text-red-500">
|
<div className="error">
|
||||||
{errors.activeAnimeProfileId}
|
{errors.activeAnimeProfileId}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="activeAnimeRootFolder" className="text-label">
|
||||||
htmlFor="activeAnimeRootFolder"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.animerootfolder)}
|
{intl.formatMessage(messages.animerootfolder)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
as="select"
|
as="select"
|
||||||
id="activeAnimeRootFolder"
|
id="activeAnimeRootFolder"
|
||||||
name="activeAnimeRootFolder"
|
name="activeAnimeRootFolder"
|
||||||
disabled={!isValidated || isTesting}
|
disabled={!isValidated || isTesting}
|
||||||
className="block w-full py-2 pl-3 pr-10 mt-1 text-base leading-6 bg-gray-700 border-gray-500 rounded-md form-select focus:outline-none focus:ring-blue focus:border-gray-500 sm:text-sm sm:leading-5 disabled:opacity-50"
|
|
||||||
>
|
>
|
||||||
<option value="">
|
<option value="">
|
||||||
{isTesting
|
{isTesting
|
||||||
@@ -652,36 +593,30 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
{errors.activeAnimeRootFolder &&
|
{errors.activeAnimeRootFolder &&
|
||||||
touched.activeAnimeRootFolder && (
|
touched.activeAnimeRootFolder && (
|
||||||
<div className="mt-2 text-red-500">
|
<div className="error">{errors.rootFolder}</div>
|
||||||
{errors.rootFolder}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label
|
||||||
htmlFor="enableSeasonFolders"
|
htmlFor="enableSeasonFolders"
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
className="checkbox-label"
|
||||||
>
|
>
|
||||||
{intl.formatMessage(messages.seasonfolders)}
|
{intl.formatMessage(messages.seasonfolders)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="enableSeasonFolders"
|
id="enableSeasonFolders"
|
||||||
name="enableSeasonFolders"
|
name="enableSeasonFolders"
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="externalUrl" className="text-label">
|
||||||
htmlFor="externalUrl"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.externalUrl)}
|
{intl.formatMessage(messages.externalUrl)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="externalUrl"
|
id="externalUrl"
|
||||||
@@ -690,37 +625,27 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
placeholder={intl.formatMessage(
|
placeholder={intl.formatMessage(
|
||||||
messages.externalUrlPlaceholder
|
messages.externalUrlPlaceholder
|
||||||
)}
|
)}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.externalUrl && touched.externalUrl && (
|
{errors.externalUrl && touched.externalUrl && (
|
||||||
<div className="mt-2 text-red-500">
|
<div className="error">{errors.externalUrl}</div>
|
||||||
{errors.externalUrl}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="syncEnabled" className="checkbox-label">
|
||||||
htmlFor="syncEnabled"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.syncEnabled)}
|
{intl.formatMessage(messages.syncEnabled)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="syncEnabled"
|
id="syncEnabled"
|
||||||
name="syncEnabled"
|
name="syncEnabled"
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="preventSearch" className="checkbox-label">
|
||||||
htmlFor="preventSearch"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.preventSearch)}
|
{intl.formatMessage(messages.preventSearch)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||||
@@ -728,7 +653,6 @@ const SonarrModal: React.FC<SonarrModalProps> = ({
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="preventSearch"
|
id="preventSearch"
|
||||||
name="preventSearch"
|
name="preventSearch"
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ const LoginWithPlex: React.FC<LoginWithPlexProps> = ({ onComplete }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form>
|
<form>
|
||||||
<div className="flex justify-center font-bold text-xl mb-2">
|
<div className="flex justify-center mb-2 text-xl font-bold">
|
||||||
<FormattedMessage {...messages.welcome} />
|
<FormattedMessage {...messages.welcome} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-center text-sm pb-6 mb-2">
|
<div className="flex justify-center pb-6 mb-2 text-sm">
|
||||||
<FormattedMessage {...messages.signinMessage} />
|
<FormattedMessage {...messages.signinMessage} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ const Setup: React.FC = () => {
|
|||||||
</span>
|
</span>
|
||||||
{intl.formatMessage(messages.syncingbackground)}
|
{intl.formatMessage(messages.syncingbackground)}
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
@@ -119,7 +119,7 @@ const Setup: React.FC = () => {
|
|||||||
{currentStep === 3 && (
|
{currentStep === 3 && (
|
||||||
<div>
|
<div>
|
||||||
<SettingsServices />
|
<SettingsServices />
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -32,21 +32,23 @@ const TvCast: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header
|
<div className="mt-1 mb-5">
|
||||||
subtext={
|
<Header
|
||||||
<Link href={`/tv/${data.id}`}>
|
subtext={
|
||||||
<a className="hover:underline">{data.name}</a>
|
<Link href={`/tv/${data.id}`}>
|
||||||
</Link>
|
<a className="hover:underline">{data.name}</a>
|
||||||
}
|
</Link>
|
||||||
>
|
}
|
||||||
{intl.formatMessage(messages.fullseriescast)}
|
>
|
||||||
</Header>
|
{intl.formatMessage(messages.fullseriescast)}
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ul className="grid grid-cols-2 gap-6 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-7 2xl:grid-cols-8">
|
<ul className="grid grid-cols-2 gap-6 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-7 2xl:grid-cols-8">
|
||||||
{data?.credits.cast.map((person) => {
|
{data?.credits.cast.map((person) => {
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
key={person.id}
|
key={person.id}
|
||||||
className="col-span-1 flex flex-col text-center items-center"
|
className="flex flex-col items-center col-span-1 text-center"
|
||||||
>
|
>
|
||||||
<PersonCard
|
<PersonCard
|
||||||
name={person.name}
|
name={person.name}
|
||||||
|
|||||||
@@ -32,15 +32,17 @@ const TvCrew: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header
|
<div className="mt-1 mb-5">
|
||||||
subtext={
|
<Header
|
||||||
<Link href={`/tv/${data.id}`}>
|
subtext={
|
||||||
<a className="hover:underline">{data.name}</a>
|
<Link href={`/tv/${data.id}`}>
|
||||||
</Link>
|
<a className="hover:underline">{data.name}</a>
|
||||||
}
|
</Link>
|
||||||
>
|
}
|
||||||
{intl.formatMessage(messages.fullseriescrew)}
|
>
|
||||||
</Header>
|
{intl.formatMessage(messages.fullseriescrew)}
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ul className="grid grid-cols-2 gap-6 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-7 2xl:grid-cols-8">
|
<ul className="grid grid-cols-2 gap-6 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-7 2xl:grid-cols-8">
|
||||||
{data?.credits.crew.map((person, index) => {
|
{data?.credits.crew.map((person, index) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -77,17 +77,19 @@ const TvRecommendations: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header
|
<div className="mt-1 mb-5">
|
||||||
subtext={
|
<Header
|
||||||
tvData && !tvError
|
subtext={
|
||||||
? intl.formatMessage(messages.recommendationssubtext, {
|
tvData && !tvError
|
||||||
title: tvData.name,
|
? intl.formatMessage(messages.recommendationssubtext, {
|
||||||
})
|
title: tvData.name,
|
||||||
: ''
|
})
|
||||||
}
|
: ''
|
||||||
>
|
}
|
||||||
<FormattedMessage {...messages.recommendations} />
|
>
|
||||||
</Header>
|
<FormattedMessage {...messages.recommendations} />
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ListView
|
<ListView
|
||||||
items={titles}
|
items={titles}
|
||||||
isEmpty={isEmpty}
|
isEmpty={isEmpty}
|
||||||
|
|||||||
@@ -77,17 +77,19 @@ const TvSimilar: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header
|
<div className="mt-1 mb-5">
|
||||||
subtext={
|
<Header
|
||||||
tvData && !tvError
|
subtext={
|
||||||
? intl.formatMessage(messages.similarsubtext, {
|
tvData && !tvError
|
||||||
title: tvData.name,
|
? intl.formatMessage(messages.similarsubtext, {
|
||||||
})
|
title: tvData.name,
|
||||||
: undefined
|
})
|
||||||
}
|
: undefined
|
||||||
>
|
}
|
||||||
<FormattedMessage {...messages.similar} />
|
>
|
||||||
</Header>
|
<FormattedMessage {...messages.similar} />
|
||||||
|
</Header>
|
||||||
|
</div>
|
||||||
<ListView
|
<ListView
|
||||||
items={titles}
|
items={titles}
|
||||||
isEmpty={isEmpty}
|
isEmpty={isEmpty}
|
||||||
|
|||||||
@@ -207,12 +207,12 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
(data.mediaInfo.status !== MediaStatus.AVAILABLE ||
|
(data.mediaInfo.status !== MediaStatus.AVAILABLE ||
|
||||||
data.mediaInfo.status4k !== MediaStatus.AVAILABLE) && (
|
data.mediaInfo.status4k !== MediaStatus.AVAILABLE) && (
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="flex flex-col sm:flex-row flex-nowrap">
|
{data?.mediaInfo &&
|
||||||
{data?.mediaInfo &&
|
data?.mediaInfo.status !== MediaStatus.AVAILABLE && (
|
||||||
data?.mediaInfo.status !== MediaStatus.AVAILABLE && (
|
<div className="flex flex-col sm:flex-row flex-nowrap mb-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => markAvailable()}
|
onClick={() => markAvailable()}
|
||||||
className="w-full mb-2 sm:mb-0 sm:mr-1 last:mr-0"
|
className="w-full sm:mb-0"
|
||||||
buttonType="success"
|
buttonType="success"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -229,12 +229,14 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
</svg>
|
</svg>
|
||||||
<span>{intl.formatMessage(messages.markavailable)}</span>
|
<span>{intl.formatMessage(messages.markavailable)}</span>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</div>
|
||||||
{data?.mediaInfo &&
|
)}
|
||||||
data?.mediaInfo.status4k !== MediaStatus.AVAILABLE && (
|
{data?.mediaInfo &&
|
||||||
|
data?.mediaInfo.status4k !== MediaStatus.AVAILABLE && (
|
||||||
|
<div className="flex flex-col sm:flex-row flex-nowrap mb-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => markAvailable(true)}
|
onClick={() => markAvailable(true)}
|
||||||
className="w-full sm:ml-1 first:ml-0"
|
className="w-full sm:mb-0"
|
||||||
buttonType="success"
|
buttonType="success"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -253,8 +255,8 @@ const TvDetails: React.FC<TvDetailsProps> = ({ tv }) => {
|
|||||||
{intl.formatMessage(messages.mark4kavailable)}
|
{intl.formatMessage(messages.mark4kavailable)}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
<div className="mt-3 text-xs text-gray-300">
|
<div className="mt-3 text-xs text-gray-300">
|
||||||
{intl.formatMessage(messages.allseasonsmarkedavailable)}
|
{intl.formatMessage(messages.allseasonsmarkedavailable)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ export const messages = defineMessages({
|
|||||||
avatar: 'Avatar',
|
avatar: 'Avatar',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
permissions: 'Permissions',
|
permissions: 'Permissions',
|
||||||
save: 'Save',
|
save: 'Save Changes',
|
||||||
saving: 'Saving…',
|
saving: 'Saving…',
|
||||||
usersaved: 'User saved',
|
usersaved: 'User saved!',
|
||||||
userfail: 'Something went wrong while saving the user.',
|
userfail: 'Something went wrong while saving the user.',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -85,141 +85,98 @@ const UserEdit: React.FC = () => {
|
|||||||
>
|
>
|
||||||
{({ isSubmitting, handleSubmit }) => (
|
{({ isSubmitting, handleSubmit }) => (
|
||||||
<Form>
|
<Form>
|
||||||
<Header>
|
<div>
|
||||||
<FormattedMessage {...messages.edituser} />
|
<div className="flex flex-col justify-between sm:flex-row">
|
||||||
</Header>
|
<Header>
|
||||||
<div className="space-y-6">
|
<FormattedMessage {...messages.edituser} />
|
||||||
<div className="flex flex-col space-y-6 text-white lg:flex-row lg:space-y-0 lg:space-x-6">
|
</Header>
|
||||||
<div className="flex-grow space-y-6">
|
</div>
|
||||||
{user?.userType === UserType.PLEX && (
|
{user?.userType === UserType.PLEX && (
|
||||||
<div className="space-y-1">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="plexUsername" className="text-label">
|
||||||
htmlFor="plexUsername"
|
{intl.formatMessage(messages.plexUsername)}
|
||||||
className="block text-sm font-medium leading-5 text-gray-400"
|
</label>
|
||||||
>
|
<div className="form-input">
|
||||||
{intl.formatMessage(messages.plexUsername)}
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
</label>
|
|
||||||
<div className="flex rounded-md shadow-sm">
|
|
||||||
<Field
|
|
||||||
id="plexUsername"
|
|
||||||
name="plexUsername"
|
|
||||||
type="text"
|
|
||||||
className="flex-grow block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="space-y-1">
|
|
||||||
<label
|
|
||||||
htmlFor="username"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.username)}
|
|
||||||
</label>
|
|
||||||
<div className="flex rounded-md shadow-sm">
|
|
||||||
<Field
|
<Field
|
||||||
id="username"
|
id="plexUsername"
|
||||||
name="username"
|
name="plexUsername"
|
||||||
type="text"
|
type="text"
|
||||||
className="flex-grow block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-1">
|
|
||||||
<label
|
|
||||||
htmlFor="email"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400"
|
|
||||||
>
|
|
||||||
<FormattedMessage {...messages.email} />
|
|
||||||
</label>
|
|
||||||
<div className="flex rounded-md shadow-sm">
|
|
||||||
<Field
|
|
||||||
id="email"
|
|
||||||
name="email"
|
|
||||||
type="text"
|
|
||||||
className="flex-grow block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<div className="flex-grow space-y-1 lg:flex-grow-0 lg:flex-shrink-0">
|
<div className="form-row">
|
||||||
<p
|
<label htmlFor="username" className="text-label">
|
||||||
className="block text-sm font-medium leading-5 text-gray-400"
|
{intl.formatMessage(messages.username)}
|
||||||
aria-hidden="true"
|
</label>
|
||||||
>
|
<div className="form-input">
|
||||||
<FormattedMessage {...messages.avatar} />
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
</p>
|
<Field id="username" name="username" type="text" />
|
||||||
<div className="lg:hidden">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div
|
|
||||||
className="flex-shrink-0 inline-block w-12 h-12 overflow-hidden rounded-full"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
className="w-full h-full rounded-full"
|
|
||||||
src={user?.avatar}
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className="relative hidden overflow-hidden transition duration-150 ease-in-out rounded-full lg:block">
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label htmlFor="email" className="text-label">
|
||||||
|
<FormattedMessage {...messages.email} />
|
||||||
|
</label>
|
||||||
|
<div className="form-input">
|
||||||
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
|
<Field id="email" name="email" type="text" readOnly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<span className="text-label">
|
||||||
|
<FormattedMessage {...messages.avatar} />
|
||||||
|
</span>
|
||||||
|
<div className="form-input">
|
||||||
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<img
|
<img
|
||||||
className="relative w-40 h-40 rounded-full"
|
className="w-40 h-40 rounded-full"
|
||||||
src={user?.avatar}
|
src={user?.avatar}
|
||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-white">
|
</div>
|
||||||
<div className="sm:border-t sm:border-gray-200">
|
<div role="group" aria-labelledby="group-label" className="group">
|
||||||
<div role="group" aria-labelledby="label-permissions">
|
<div className="form-row">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
<span id="group-label" className="group-label">
|
||||||
<div>
|
<FormattedMessage {...messages.permissions} />
|
||||||
<div
|
</span>
|
||||||
className="text-base font-medium leading-6 sm:text-sm sm:leading-5"
|
<div className="form-input">
|
||||||
id="label-permissions"
|
<div className="max-w-lg">
|
||||||
>
|
<PermissionEdit
|
||||||
<FormattedMessage {...messages.permissions} />
|
user={currentUser}
|
||||||
</div>
|
currentPermission={currentPermission}
|
||||||
</div>
|
onUpdate={(newPermission) =>
|
||||||
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
setCurrentPermission(newPermission)
|
||||||
<div className="max-w-lg">
|
}
|
||||||
<PermissionEdit
|
/>
|
||||||
user={currentUser}
|
|
||||||
currentPermission={currentPermission}
|
|
||||||
onUpdate={(newPermission) =>
|
|
||||||
setCurrentPermission(newPermission)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="pt-5 mt-8 border-t border-gray-700">
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
|
||||||
<Button
|
|
||||||
buttonType="primary"
|
|
||||||
type="submit"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
onClick={() => handleSubmit}
|
|
||||||
>
|
|
||||||
{isSubmitting
|
|
||||||
? intl.formatMessage(messages.saving)
|
|
||||||
: intl.formatMessage(messages.save)}
|
|
||||||
</Button>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="actions">
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<span className="inline-flex ml-3 rounded-md shadow-sm">
|
||||||
|
<Button
|
||||||
|
buttonType="primary"
|
||||||
|
type="submit"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
onClick={() => handleSubmit}
|
||||||
|
>
|
||||||
|
{isSubmitting
|
||||||
|
? intl.formatMessage(messages.saving)
|
||||||
|
: intl.formatMessage(messages.save)}
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|||||||
@@ -89,22 +89,25 @@ const BulkEditModal: React.FC<BulkEditProps> = ({
|
|||||||
okText={intl.formatMessage(userEditMessages.save)}
|
okText={intl.formatMessage(userEditMessages.save)}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
>
|
>
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline">
|
<div className="mt-6 mb-6">
|
||||||
<div>
|
<div role="group" aria-labelledby="group-label">
|
||||||
<div
|
<div className="form-row">
|
||||||
className="text-base font-medium leading-6 sm:text-sm sm:leading-5"
|
<div>
|
||||||
id="label-permissions"
|
<div id="group-label" className="group-label">
|
||||||
>
|
<FormattedMessage {...userEditMessages.permissions} />
|
||||||
<FormattedMessage {...userEditMessages.permissions} />
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="form-input">
|
||||||
<div className="mt-4 sm:mt-0 sm:col-span-2">
|
<div className="max-w-lg">
|
||||||
<div className="max-w-lg">
|
<PermissionEdit
|
||||||
<PermissionEdit
|
user={currentUser}
|
||||||
user={currentUser}
|
currentPermission={currentPermission}
|
||||||
currentPermission={currentPermission}
|
onUpdate={(newPermission) =>
|
||||||
onUpdate={(newPermission) => setCurrentPermission(newPermission)}
|
setCurrentPermission(newPermission)
|
||||||
/>
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -282,50 +282,43 @@ const UserList: React.FC = () => {
|
|||||||
<Alert title={intl.formatMessage(messages.passwordinfo)}>
|
<Alert title={intl.formatMessage(messages.passwordinfo)}>
|
||||||
{intl.formatMessage(messages.passwordinfodescription)}
|
{intl.formatMessage(messages.passwordinfodescription)}
|
||||||
</Alert>
|
</Alert>
|
||||||
<Form>
|
<Form className="section">
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800">
|
<div className="form-row">
|
||||||
<label
|
<label htmlFor="email" className="text-label">
|
||||||
htmlFor="email"
|
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.email)}
|
{intl.formatMessage(messages.email)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="name@example.com"
|
placeholder="name@example.com"
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.email && touched.email && (
|
{errors.email && touched.email && (
|
||||||
<div className="mt-2 text-red-500">{errors.email}</div>
|
<div className="error">{errors.email}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<label
|
</div>
|
||||||
htmlFor="genpassword"
|
<div className="form-row">
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
<label htmlFor="genpassword" className="checkbox-label">
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.autogeneratepassword)}
|
{intl.formatMessage(messages.autogeneratepassword)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<Field
|
<Field
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="genpassword"
|
id="genpassword"
|
||||||
name="genpassword"
|
name="genpassword"
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
onClick={() => setFieldValue('password', '')}
|
onClick={() => setFieldValue('password', '')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<label
|
</div>
|
||||||
htmlFor="password"
|
<div className="form-row">
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px"
|
<label htmlFor="password" className="text-label">
|
||||||
>
|
|
||||||
{intl.formatMessage(messages.password)}
|
{intl.formatMessage(messages.password)}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
<div className="form-input">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<Field
|
<Field
|
||||||
id="password"
|
id="password"
|
||||||
@@ -333,13 +326,10 @@ const UserList: React.FC = () => {
|
|||||||
type="password"
|
type="password"
|
||||||
disabled={values.genpassword}
|
disabled={values.genpassword}
|
||||||
placeholder={intl.formatMessage(messages.password)}
|
placeholder={intl.formatMessage(messages.password)}
|
||||||
className="flex-1 block w-full min-w-0 transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errors.password && touched.password && (
|
{errors.password && touched.password && (
|
||||||
<div className="mt-2 text-red-500">
|
<div className="error">{errors.password}</div>
|
||||||
{errors.password}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -370,18 +360,18 @@ const UserList: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<div className="flex flex-col justify-between sm:flex-row">
|
<div className="flex flex-col justify-between md:items-end md:flex-row">
|
||||||
<Header>{intl.formatMessage(messages.userlist)}</Header>
|
<Header>{intl.formatMessage(messages.userlist)}</Header>
|
||||||
<div className="flex">
|
<div className="flex flex-row justify-between mt-2 sm:flex-row md:mb-0">
|
||||||
<Button
|
<Button
|
||||||
className="mx-4 my-8 outline"
|
className="flex-grow mr-2 outline"
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
onClick={() => setCreateModal({ isOpen: true })}
|
onClick={() => setCreateModal({ isOpen: true })}
|
||||||
>
|
>
|
||||||
{intl.formatMessage(messages.createlocaluser)}
|
{intl.formatMessage(messages.createlocaluser)}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="mx-4 my-8"
|
className="flex-grow outline"
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
disabled={isImporting}
|
disabled={isImporting}
|
||||||
onClick={() => importFromPlex()}
|
onClick={() => importFromPlex()}
|
||||||
@@ -390,7 +380,6 @@ const UserList: React.FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Table>
|
<Table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -403,7 +392,6 @@ const UserList: React.FC = () => {
|
|||||||
onChange={() => {
|
onChange={() => {
|
||||||
toggleAllUsers();
|
toggleAllUsers();
|
||||||
}}
|
}}
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
</Table.TH>
|
</Table.TH>
|
||||||
<Table.TH>{intl.formatMessage(messages.username)}</Table.TH>
|
<Table.TH>{intl.formatMessage(messages.username)}</Table.TH>
|
||||||
@@ -414,7 +402,6 @@ const UserList: React.FC = () => {
|
|||||||
<Table.TH>{intl.formatMessage(messages.lastupdated)}</Table.TH>
|
<Table.TH>{intl.formatMessage(messages.lastupdated)}</Table.TH>
|
||||||
<Table.TH className="text-right">
|
<Table.TH className="text-right">
|
||||||
<Button
|
<Button
|
||||||
buttonSize="sm"
|
|
||||||
buttonType="warning"
|
buttonType="warning"
|
||||||
onClick={() => setShowBulkEditModal(true)}
|
onClick={() => setShowBulkEditModal(true)}
|
||||||
disabled={selectedUsers.length === 0}
|
disabled={selectedUsers.length === 0}
|
||||||
@@ -437,7 +424,6 @@ const UserList: React.FC = () => {
|
|||||||
onChange={() => {
|
onChange={() => {
|
||||||
toggleUser(user.id);
|
toggleUser(user.id);
|
||||||
}}
|
}}
|
||||||
className="w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md form-checkbox"
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Table.TD>
|
</Table.TD>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const globalMessages = defineMessages({
|
|||||||
close: 'Close',
|
close: 'Close',
|
||||||
edit: 'Edit',
|
edit: 'Edit',
|
||||||
experimental: 'Experimental',
|
experimental: 'Experimental',
|
||||||
|
advanced: 'Advanced',
|
||||||
});
|
});
|
||||||
|
|
||||||
export default globalMessages;
|
export default globalMessages;
|
||||||
|
|||||||
@@ -411,11 +411,13 @@
|
|||||||
"components.Settings.addsonarr": "Add Sonarr Server",
|
"components.Settings.addsonarr": "Add Sonarr Server",
|
||||||
"components.Settings.apikey": "API Key",
|
"components.Settings.apikey": "API Key",
|
||||||
"components.Settings.applicationurl": "Application URL",
|
"components.Settings.applicationurl": "Application URL",
|
||||||
"components.Settings.autoapprovedrequests": "Send Notifications for Auto-Approved Requests",
|
"components.Settings.autoapprovedrequests": "Enable Notifications for Auto-Approved Requests",
|
||||||
"components.Settings.cancelscan": "Cancel Scan",
|
"components.Settings.cancelscan": "Cancel Scan",
|
||||||
|
"components.Settings.command": "Command",
|
||||||
"components.Settings.copied": "Copied API key to clipboard.",
|
"components.Settings.copied": "Copied API key to clipboard.",
|
||||||
"components.Settings.csrfProtection": "Enable CSRF Protection",
|
"components.Settings.csrfProtection": "Enable CSRF Protection",
|
||||||
"components.Settings.csrfProtectionTip": "Sets external API access to read-only (Overseerr must be reloaded for changes to take effect)",
|
"components.Settings.csrfProtectionHoverTip": "Do NOT enable this unless you understand what you are doing!",
|
||||||
|
"components.Settings.csrfProtectionTip": "Sets external API access to read-only (requires HTTPS and Overseerr must be reloaded for changes to take effect)",
|
||||||
"components.Settings.currentlibrary": "Current Library: {name}",
|
"components.Settings.currentlibrary": "Current Library: {name}",
|
||||||
"components.Settings.default": "Default",
|
"components.Settings.default": "Default",
|
||||||
"components.Settings.default4k": "Default 4K",
|
"components.Settings.default4k": "Default 4K",
|
||||||
@@ -453,7 +455,8 @@
|
|||||||
"components.Settings.plexsettings": "Plex Settings",
|
"components.Settings.plexsettings": "Plex Settings",
|
||||||
"components.Settings.plexsettingsDescription": "Configure the settings for your Plex server. Overseerr scans your Plex libraries to see what content is available.",
|
"components.Settings.plexsettingsDescription": "Configure the settings for your Plex server. Overseerr scans your Plex libraries to see what content is available.",
|
||||||
"components.Settings.port": "Port",
|
"components.Settings.port": "Port",
|
||||||
"components.Settings.radarrSettingsDescription": "Set up your Radarr connection below. You can have multiple, but only two active as defaults at any time (one for standard HD, and one for 4K). Administrators can override the server is used for new requests.",
|
"components.Settings.process": "Process",
|
||||||
|
"components.Settings.radarrSettingsDescription": "Configure your Radarr connection below. You can have multiple Radarr configurations, but only two can be active as defaults at any time (one for standard HD and one for 4K). Administrators can override the server which is used for new requests.",
|
||||||
"components.Settings.radarrsettings": "Radarr Settings",
|
"components.Settings.radarrsettings": "Radarr Settings",
|
||||||
"components.Settings.save": "Save Changes",
|
"components.Settings.save": "Save Changes",
|
||||||
"components.Settings.saving": "Saving…",
|
"components.Settings.saving": "Saving…",
|
||||||
@@ -465,12 +468,12 @@
|
|||||||
"components.Settings.servernameTip": "Automatically retrieved from Plex after saving",
|
"components.Settings.servernameTip": "Automatically retrieved from Plex after saving",
|
||||||
"components.Settings.serverpreset": "Server",
|
"components.Settings.serverpreset": "Server",
|
||||||
"components.Settings.serverpresetLoad": "Press the button to load available servers",
|
"components.Settings.serverpresetLoad": "Press the button to load available servers",
|
||||||
"components.Settings.serverpresetManualMessage": "Manually configure",
|
"components.Settings.serverpresetManualMessage": "Manual configuration",
|
||||||
"components.Settings.serverpresetPlaceholder": "Plex Server",
|
"components.Settings.serverpresetPlaceholder": "Plex Server",
|
||||||
"components.Settings.serverpresetRefreshing": "Retrieving servers…",
|
"components.Settings.serverpresetRefreshing": "Retrieving servers…",
|
||||||
"components.Settings.settingUpPlex": "Setting Up Plex",
|
"components.Settings.settingUpPlex": "Setting Up Plex",
|
||||||
"components.Settings.settingUpPlexDescription": "To set up Plex, you can either enter your details manually or select a server retrieved from <RegisterPlexTVLink>plex.tv</RegisterPlexTVLink>. Press the button to the right of the dropdown to check connectivity and retrieve available servers.",
|
"components.Settings.settingUpPlexDescription": "To set up Plex, you can either enter your details manually or select a server retrieved from <RegisterPlexTVLink>plex.tv</RegisterPlexTVLink>. Press the button to the right of the dropdown to check connectivity and retrieve available servers.",
|
||||||
"components.Settings.sonarrSettingsDescription": "Set up your Sonarr connection below. You can have multiple, but only two active as defaults at any time (one for standard HD and one for 4K). Administrators can override the server is used for new requests.",
|
"components.Settings.sonarrSettingsDescription": "Configure your Sonarr connection below. You can have multiple Sonarr configurations, but only two can be active as defaults at any time (one for standard HD and one for 4K). Administrators can override the server which is used for new requests.",
|
||||||
"components.Settings.sonarrsettings": "Sonarr Settings",
|
"components.Settings.sonarrsettings": "Sonarr Settings",
|
||||||
"components.Settings.ssl": "SSL",
|
"components.Settings.ssl": "SSL",
|
||||||
"components.Settings.startscan": "Start Scan",
|
"components.Settings.startscan": "Start Scan",
|
||||||
@@ -551,7 +554,7 @@
|
|||||||
"components.UserEdit.email": "Email",
|
"components.UserEdit.email": "Email",
|
||||||
"components.UserEdit.permissions": "Permissions",
|
"components.UserEdit.permissions": "Permissions",
|
||||||
"components.UserEdit.plexUsername": "Plex Username",
|
"components.UserEdit.plexUsername": "Plex Username",
|
||||||
"components.UserEdit.save": "Save",
|
"components.UserEdit.save": "Save Changes",
|
||||||
"components.UserEdit.saving": "Saving…",
|
"components.UserEdit.saving": "Saving…",
|
||||||
"components.UserEdit.userfail": "Something went wrong while saving the user.",
|
"components.UserEdit.userfail": "Something went wrong while saving the user.",
|
||||||
"components.UserEdit.username": "Display Name",
|
"components.UserEdit.username": "Display Name",
|
||||||
@@ -591,6 +594,7 @@
|
|||||||
"components.UserList.usertype": "User Type",
|
"components.UserList.usertype": "User Type",
|
||||||
"components.UserList.validationemailrequired": "Must enter a valid email address",
|
"components.UserList.validationemailrequired": "Must enter a valid email address",
|
||||||
"components.UserList.validationpasswordminchars": "Password is too short; should be a minimum of 8 characters",
|
"components.UserList.validationpasswordminchars": "Password is too short; should be a minimum of 8 characters",
|
||||||
|
"i18n.advanced": "Advanced",
|
||||||
"i18n.approve": "Approve",
|
"i18n.approve": "Approve",
|
||||||
"i18n.approved": "Approved",
|
"i18n.approved": "Approved",
|
||||||
"i18n.available": "Available",
|
"i18n.available": "Available",
|
||||||
|
|||||||
@@ -37,6 +37,78 @@ body {
|
|||||||
@apply relative top-0 bottom-0 left-0 right-0 flex flex-col items-center justify-center h-screen text-center text-gray-300;
|
@apply relative top-0 bottom-0 left-0 right-0 flex flex-col items-center justify-center h-screen text-center text-gray-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
@apply text-2xl leading-8 text-gray-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
@apply max-w-2xl mt-1 text-sm leading-5 text-gray-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
@apply mt-6 mb-10 text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
@apply mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
@apply text-white sm:col-span-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-tip {
|
||||||
|
@apply block text-gray-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
@apply pt-5 mt-8 border-t border-gray-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='checkbox'] {
|
||||||
|
@apply w-6 h-6 text-indigo-600 transition duration-150 ease-in-out rounded-md;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
@apply block mb-1 text-sm font-medium leading-5 text-gray-400 sm:mt-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='text'],
|
||||||
|
input[type='password'],
|
||||||
|
select {
|
||||||
|
@apply flex-1 block w-full min-w-0 text-white transition duration-150 ease-in-out bg-gray-700 border border-gray-500 rounded-md form-input sm:text-sm sm:leading-5;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.rounded-l-only,
|
||||||
|
select.rounded-l-only {
|
||||||
|
@apply rounded-r-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.rounded-r-only,
|
||||||
|
select.rounded-r-only {
|
||||||
|
@apply rounded-l-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.protocol {
|
||||||
|
@apply inline-flex items-center px-3 text-gray-100 bg-gray-600 border border-r-0 border-gray-500 cursor-default rounded-l-md sm:text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-label {
|
||||||
|
@apply block mb-1 text-sm font-medium leading-5 text-gray-400 sm:mt-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
@apply mt-2 text-sm text-red-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group {
|
||||||
|
@apply mt-6 text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-label {
|
||||||
|
@apply block mb-1 text-sm font-medium leading-6 text-gray-400;
|
||||||
|
}
|
||||||
|
|
||||||
/* Used for animating height */
|
/* Used for animating height */
|
||||||
.extra-max-height {
|
.extra-max-height {
|
||||||
max-height: 100rem;
|
max-height: 100rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user