refactor: modal redesign and fix for transitions (#2987)
This commit is contained in:
@@ -54,10 +54,7 @@ describe('User List', () => {
|
|||||||
.contains('Delete')
|
.contains('Delete')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get('[data-testid=modal-title]').should(
|
cy.get('[data-testid=modal-title]').should('contain', `Delete User`);
|
||||||
'contain',
|
|
||||||
`Delete ${testUser.displayName}`
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.intercept('/api/v1/user?take=10&skip=0&sort=displayname').as('user');
|
cy.intercept('/api/v1/user?take=10&skip=0&sort=displayname').as('user');
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
"@formatjs/intl-locale": "3.0.3",
|
"@formatjs/intl-locale": "3.0.3",
|
||||||
"@formatjs/intl-pluralrules": "5.0.3",
|
"@formatjs/intl-pluralrules": "5.0.3",
|
||||||
"@formatjs/intl-utils": "3.8.4",
|
"@formatjs/intl-utils": "3.8.4",
|
||||||
"@headlessui/react": "1.6.6",
|
"@headlessui/react": "^0.0.0-insiders.b301f04",
|
||||||
"@heroicons/react": "1.0.6",
|
"@heroicons/react": "1.0.6",
|
||||||
"@supercharge/request-ip": "1.2.0",
|
"@supercharge/request-ip": "1.2.0",
|
||||||
"@svgr/webpack": "6.3.1",
|
"@svgr/webpack": "6.3.1",
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ interface AlertProps {
|
|||||||
|
|
||||||
const Alert = ({ title, children, type }: AlertProps) => {
|
const Alert = ({ title, children, type }: AlertProps) => {
|
||||||
let design = {
|
let design = {
|
||||||
bgColor: 'bg-yellow-600',
|
bgColor:
|
||||||
|
'border border-yellow-500 backdrop-blur bg-yellow-400 bg-opacity-20',
|
||||||
titleColor: 'text-yellow-100',
|
titleColor: 'text-yellow-100',
|
||||||
textColor: 'text-yellow-300',
|
textColor: 'text-yellow-300',
|
||||||
svg: <ExclamationIcon className="h-5 w-5" />,
|
svg: <ExclamationIcon className="h-5 w-5" />,
|
||||||
@@ -21,9 +22,10 @@ const Alert = ({ title, children, type }: AlertProps) => {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case 'info':
|
case 'info':
|
||||||
design = {
|
design = {
|
||||||
bgColor: 'bg-indigo-600',
|
bgColor:
|
||||||
titleColor: 'text-indigo-100',
|
'border border-indigo-500 backdrop-blur bg-indigo-400 bg-opacity-20',
|
||||||
textColor: 'text-indigo-300',
|
titleColor: 'text-gray-100',
|
||||||
|
textColor: 'text-gray-300',
|
||||||
svg: <InformationCircleIcon className="h-5 w-5" />,
|
svg: <InformationCircleIcon className="h-5 w-5" />,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -31,21 +31,27 @@ const Badge = (
|
|||||||
|
|
||||||
switch (badgeType) {
|
switch (badgeType) {
|
||||||
case 'danger':
|
case 'danger':
|
||||||
badgeStyle.push('bg-red-600 !text-red-100');
|
badgeStyle.push(
|
||||||
|
'bg-red-600 bg-opacity-80 border-red-500 border !text-red-100'
|
||||||
|
);
|
||||||
if (href) {
|
if (href) {
|
||||||
badgeStyle.push('hover:bg-red-500');
|
badgeStyle.push('hover:bg-red-500 bg-opacity-100');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'warning':
|
case 'warning':
|
||||||
badgeStyle.push('bg-yellow-500 !text-yellow-100');
|
badgeStyle.push(
|
||||||
|
'bg-yellow-500 bg-opacity-80 border-yellow-500 border !text-yellow-100'
|
||||||
|
);
|
||||||
if (href) {
|
if (href) {
|
||||||
badgeStyle.push('hover:bg-yellow-400');
|
badgeStyle.push('hover:bg-yellow-500 hover:bg-opacity-100');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'success':
|
case 'success':
|
||||||
badgeStyle.push('bg-green-500 !text-green-100');
|
badgeStyle.push(
|
||||||
|
'bg-green-500 bg-opacity-80 border border-green-500 !text-green-100'
|
||||||
|
);
|
||||||
if (href) {
|
if (href) {
|
||||||
badgeStyle.push('hover:bg-green-400');
|
badgeStyle.push('hover:bg-green-500 hover:bg-opacity-100');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'dark':
|
case 'dark':
|
||||||
@@ -61,9 +67,11 @@ const Badge = (
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
badgeStyle.push('bg-indigo-500 !text-indigo-100');
|
badgeStyle.push(
|
||||||
|
'bg-indigo-500 bg-opacity-80 border border-indigo-500 !text-indigo-100'
|
||||||
|
);
|
||||||
if (href) {
|
if (href) {
|
||||||
badgeStyle.push('hover:bg-indigo-400');
|
badgeStyle.push('hover:bg-indigo-500 bg-opacity-100');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,22 +51,22 @@ function Button<P extends ElementTypes = 'button'>(
|
|||||||
switch (buttonType) {
|
switch (buttonType) {
|
||||||
case 'primary':
|
case 'primary':
|
||||||
buttonStyle.push(
|
buttonStyle.push(
|
||||||
'text-white bg-indigo-600 border-indigo-600 hover:bg-indigo-500 hover:border-indigo-500 focus:border-indigo-700 focus:ring-indigo active:bg-indigo-700 active:border-indigo-700'
|
'text-white border border-indigo-500 bg-indigo-600 bg-opacity-80 hover:bg-opacity-100 hover:border-indigo-500 focus:border-indigo-700 focus:ring-indigo active:bg-opacity-100 active:border-indigo-700'
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'danger':
|
case 'danger':
|
||||||
buttonStyle.push(
|
buttonStyle.push(
|
||||||
'text-white bg-red-600 border-red-600 hover:bg-red-500 hover:border-red-500 focus:border-red-700 focus:ring-red active:bg-red-700 active:border-red-700'
|
'text-white bg-red-600 bg-opacity-80 border-red-500 hover:bg-opacity-100 hover:border-red-500 focus:border-red-700 focus:ring-red active:bg-red-700 active:border-red-700'
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'warning':
|
case 'warning':
|
||||||
buttonStyle.push(
|
buttonStyle.push(
|
||||||
'text-white bg-yellow-500 border-yellow-500 hover:bg-yellow-400 hover:border-yellow-400 focus:border-yellow-700 focus:ring-yellow active:bg-yellow-700 active:border-yellow-700'
|
'text-white border border-yellow-500 backdrop-blur bg-yellow-500 bg-opacity-80 hover:bg-opacity-100 hover:border-yellow-400 focus:border-yellow-700 focus:ring-yellow active:bg-opacity-100 active:border-yellow-700'
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'success':
|
case 'success':
|
||||||
buttonStyle.push(
|
buttonStyle.push(
|
||||||
'text-white bg-green-500 border-green-500 hover:bg-green-400 hover:border-green-400 focus:border-green-700 focus:ring-green active:bg-green-700 active:border-green-700'
|
'text-white bg-green-500 bg-opacity-80 border-green-500 hover:bg-opacity-100 hover:border-green-400 focus:border-green-700 focus:ring-green active:bg-opacity-100 active:border-green-700'
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'ghost':
|
case 'ghost':
|
||||||
@@ -76,7 +76,7 @@ function Button<P extends ElementTypes = 'button'>(
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
buttonStyle.push(
|
buttonStyle.push(
|
||||||
'text-gray-200 bg-gray-600 border-gray-600 hover:text-white hover:bg-gray-500 hover:border-gray-500 group-hover:text-white group-hover:bg-gray-500 group-hover:border-gray-500 focus:border-blue-300 focus:ring-blue active:text-gray-200 active:bg-gray-500 active:border-gray-500'
|
'text-gray-200 bg-gray-800 bg-opacity-80 border-gray-600 hover:text-white hover:bg-gray-700 hover:border-gray-600 group-hover:text-white group-hover:bg-gray-700 group-hover:border-gray-600 focus:border-blue-300 focus:ring-blue active:text-gray-200 active:bg-gray-700 active:border-gray-600'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,9 +70,9 @@ const ButtonWithDropdown = ({
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
styleClasses.mainButtonClasses +=
|
styleClasses.mainButtonClasses +=
|
||||||
' bg-indigo-600 border-indigo-600 hover:bg-indigo-500 hover:border-indigo-500 active:bg-indigo-700 active:border-indigo-700 focus:ring-blue';
|
' bg-indigo-600 border-indigo-500 bg-opacity-80 hover:bg-opacity-100 hover:border-indigo-500 active:bg-indigo-700 active:border-indigo-700 focus:ring-blue';
|
||||||
styleClasses.dropdownSideButtonClasses +=
|
styleClasses.dropdownSideButtonClasses +=
|
||||||
' bg-indigo-700 border-indigo-600 hover:bg-indigo-500 active:bg-indigo-700 focus:ring-blue';
|
' bg-indigo-600 bg-opacity-80 border-indigo-500 hover:bg-opacity-100 active:bg-opacity-100 focus:ring-blue';
|
||||||
styleClasses.dropdownClasses += ' bg-indigo-600 p-1';
|
styleClasses.dropdownClasses += ' bg-indigo-600 p-1';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ const Header = ({ children, extraMargin = 0, subtext }: HeaderProps) => {
|
|||||||
className="mb-4 truncate text-2xl font-bold leading-7 text-gray-100 sm:overflow-visible sm:text-4xl sm:leading-9 md:mb-0"
|
className="mb-4 truncate text-2xl font-bold leading-7 text-gray-100 sm:overflow-visible sm:text-4xl sm:leading-9 md:mb-0"
|
||||||
data-testid="page-header"
|
data-testid="page-header"
|
||||||
>
|
>
|
||||||
<span className="bg-gradient-to-br from-indigo-400 to-purple-400 bg-clip-text text-transparent">
|
<span className="text-overseerr">{children}</span>
|
||||||
{children}
|
|
||||||
</span>
|
|
||||||
</h2>
|
</h2>
|
||||||
{subtext && <div className="mt-2 text-gray-400">{subtext}</div>}
|
{subtext && <div className="mt-2 text-gray-400">{subtext}</div>}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { useIntl } from 'react-intl';
|
|||||||
|
|
||||||
interface ModalProps {
|
interface ModalProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
subTitle?: string;
|
||||||
onCancel?: (e?: MouseEvent<HTMLElement>) => void;
|
onCancel?: (e?: MouseEvent<HTMLElement>) => void;
|
||||||
onOk?: (e?: MouseEvent<HTMLButtonElement>) => void;
|
onOk?: (e?: MouseEvent<HTMLButtonElement>) => void;
|
||||||
onSecondary?: (e?: MouseEvent<HTMLButtonElement>) => void;
|
onSecondary?: (e?: MouseEvent<HTMLButtonElement>) => void;
|
||||||
@@ -30,7 +31,6 @@ interface ModalProps {
|
|||||||
tertiaryButtonType?: ButtonType;
|
tertiaryButtonType?: ButtonType;
|
||||||
disableScrollLock?: boolean;
|
disableScrollLock?: boolean;
|
||||||
backgroundClickable?: boolean;
|
backgroundClickable?: boolean;
|
||||||
iconSvg?: React.ReactNode;
|
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
backdrop?: string;
|
backdrop?: string;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@@ -40,6 +40,7 @@ const Modal = React.forwardRef<HTMLDivElement, ModalProps>(
|
|||||||
(
|
(
|
||||||
{
|
{
|
||||||
title,
|
title,
|
||||||
|
subTitle,
|
||||||
onCancel,
|
onCancel,
|
||||||
onOk,
|
onOk,
|
||||||
cancelText,
|
cancelText,
|
||||||
@@ -50,7 +51,6 @@ const Modal = React.forwardRef<HTMLDivElement, ModalProps>(
|
|||||||
children,
|
children,
|
||||||
disableScrollLock,
|
disableScrollLock,
|
||||||
backgroundClickable = true,
|
backgroundClickable = true,
|
||||||
iconSvg,
|
|
||||||
secondaryButtonType = 'default',
|
secondaryButtonType = 'default',
|
||||||
secondaryDisabled = false,
|
secondaryDisabled = false,
|
||||||
onSecondary,
|
onSecondary,
|
||||||
@@ -67,9 +67,9 @@ const Modal = React.forwardRef<HTMLDivElement, ModalProps>(
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const modalRef = useRef<HTMLDivElement>(null);
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
useClickOutside(modalRef, () => {
|
useClickOutside(modalRef, () => {
|
||||||
typeof onCancel === 'function' && backgroundClickable
|
if (onCancel && backgroundClickable) {
|
||||||
? onCancel()
|
onCancel();
|
||||||
: undefined;
|
}
|
||||||
});
|
});
|
||||||
useLockBodyScroll(true, disableScrollLock);
|
useLockBodyScroll(true, disableScrollLock);
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ const Modal = React.forwardRef<HTMLDivElement, ModalProps>(
|
|||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
<Transition
|
<Transition
|
||||||
className="hide-scrollbar relative inline-block w-full transform overflow-auto bg-gray-700 px-4 pt-5 pb-4 text-left align-bottom shadow-xl ring-1 ring-gray-500 transition-all sm:my-8 sm:max-w-3xl sm:rounded-lg sm:align-middle"
|
className="hide-scrollbar relative inline-block w-full transform overflow-auto bg-gray-800 px-4 pt-4 pb-4 text-left align-bottom shadow-xl ring-1 ring-gray-700 transition-all sm:my-8 sm:max-w-3xl sm:rounded-lg sm:align-middle"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
aria-labelledby="modal-headline"
|
aria-labelledby="modal-headline"
|
||||||
@@ -133,26 +133,36 @@ const Modal = React.forwardRef<HTMLDivElement, ModalProps>(
|
|||||||
className="absolute inset-0"
|
className="absolute inset-0"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
'linear-gradient(180deg, rgba(55, 65, 81, 0.85) 0%, rgba(55, 65, 81, 1) 100%)',
|
'linear-gradient(180deg, rgba(31, 41, 55, 0.75) 0%, rgba(31, 41, 55, 1) 100%)',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="relative overflow-x-hidden p-0.5 sm:flex sm:items-center">
|
<div className="relative -mx-4 overflow-x-hidden px-4 pt-0.5 sm:flex sm:items-center">
|
||||||
{iconSvg && <div className="modal-icon">{iconSvg}</div>}
|
|
||||||
<div
|
<div
|
||||||
className={`mt-3 truncate text-center text-white sm:mt-0 sm:text-left ${
|
className={`mt-3 truncate text-center text-white sm:mt-0 sm:text-left`}
|
||||||
iconSvg ? 'sm:ml-4' : 'sm:mb-4'
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{title && (
|
{(title || subTitle) && (
|
||||||
<span
|
<div className="flex flex-col space-y-1">
|
||||||
className="truncate text-lg font-bold leading-6"
|
{title && (
|
||||||
id="modal-headline"
|
<span
|
||||||
data-testid="modal-title"
|
className="text-overseerr truncate pb-0.5 text-2xl font-bold leading-6"
|
||||||
>
|
id="modal-headline"
|
||||||
{title}
|
data-testid="modal-title"
|
||||||
</span>
|
>
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{subTitle && (
|
||||||
|
<span
|
||||||
|
className="truncate text-lg font-semibold leading-6 text-gray-200"
|
||||||
|
id="modal-headline"
|
||||||
|
data-testid="modal-title"
|
||||||
|
>
|
||||||
|
{subTitle}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const SlideOver = ({
|
|||||||
<div className="hide-scrollbar flex h-full flex-col overflow-y-scroll rounded-lg bg-gray-800 bg-opacity-80 shadow-xl ring-1 ring-gray-700 backdrop-blur">
|
<div className="hide-scrollbar flex h-full flex-col overflow-y-scroll rounded-lg bg-gray-800 bg-opacity-80 shadow-xl ring-1 ring-gray-700 backdrop-blur">
|
||||||
<header className="space-y-1 border-b border-gray-700 py-4 px-4">
|
<header className="space-y-1 border-b border-gray-700 py-4 px-4">
|
||||||
<div className="flex items-center justify-between space-x-3">
|
<div className="flex items-center justify-between space-x-3">
|
||||||
<h2 className="bg-gradient-to-br from-indigo-400 to-purple-400 bg-clip-text text-2xl font-bold leading-7 text-transparent">
|
<h2 className="text-overseerr text-2xl font-bold leading-7">
|
||||||
{title}
|
{title}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex h-7 items-center">
|
<div className="flex h-7 items-center">
|
||||||
@@ -89,7 +89,9 @@ const SlideOver = ({
|
|||||||
</div>
|
</div>
|
||||||
{subText && (
|
{subText && (
|
||||||
<div>
|
<div>
|
||||||
<p className="leading-5 text-gray-300">{subText}</p>
|
<p className="font-semibold leading-5 text-gray-300">
|
||||||
|
{subText}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import Button from '@app/components/Common/Button';
|
|||||||
import Modal from '@app/components/Common/Modal';
|
import Modal from '@app/components/Common/Modal';
|
||||||
import { Permission, useUser } from '@app/hooks/useUser';
|
import { Permission, useUser } from '@app/hooks/useUser';
|
||||||
import { Menu, Transition } from '@headlessui/react';
|
import { Menu, Transition } from '@headlessui/react';
|
||||||
import { ExclamationIcon } from '@heroicons/react/outline';
|
|
||||||
import { DotsVerticalIcon } from '@heroicons/react/solid';
|
import { DotsVerticalIcon } from '@heroicons/react/solid';
|
||||||
import type { default as IssueCommentType } from '@server/entity/IssueComment';
|
import type { default as IssueCommentType } from '@server/entity/IssueComment';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
@@ -65,7 +64,7 @@ const IssueComment = ({
|
|||||||
} mt-4 space-x-4`}
|
} mt-4 space-x-4`}
|
||||||
>
|
>
|
||||||
<Transition
|
<Transition
|
||||||
as="div"
|
as={Fragment}
|
||||||
enter="transition opacity-0 duration-300"
|
enter="transition opacity-0 duration-300"
|
||||||
enterFrom="opacity-0"
|
enterFrom="opacity-0"
|
||||||
enterTo="opacity-100"
|
enterTo="opacity-100"
|
||||||
@@ -80,7 +79,6 @@ const IssueComment = ({
|
|||||||
onOk={() => deleteComment()}
|
onOk={() => deleteComment()}
|
||||||
okText={intl.formatMessage(messages.delete)}
|
okText={intl.formatMessage(messages.delete)}
|
||||||
okButtonType="danger"
|
okButtonType="danger"
|
||||||
iconSvg={<ExclamationIcon />}
|
|
||||||
>
|
>
|
||||||
{intl.formatMessage(messages.areyousuredelete)}
|
{intl.formatMessage(messages.areyousuredelete)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { Transition } from '@headlessui/react';
|
|||||||
import {
|
import {
|
||||||
ChatIcon,
|
ChatIcon,
|
||||||
CheckCircleIcon,
|
CheckCircleIcon,
|
||||||
ExclamationIcon,
|
|
||||||
PlayIcon,
|
PlayIcon,
|
||||||
ServerIcon,
|
ServerIcon,
|
||||||
} from '@heroicons/react/outline';
|
} from '@heroicons/react/outline';
|
||||||
@@ -189,7 +188,6 @@ const IssueDetails = () => {
|
|||||||
onOk={() => deleteIssue()}
|
onOk={() => deleteIssue()}
|
||||||
okText={intl.formatMessage(messages.deleteissue)}
|
okText={intl.formatMessage(messages.deleteissue)}
|
||||||
okButtonType="danger"
|
okButtonType="danger"
|
||||||
iconSvg={<ExclamationIcon />}
|
|
||||||
>
|
>
|
||||||
{intl.formatMessage(messages.deleteissueconfirm)}
|
{intl.formatMessage(messages.deleteissueconfirm)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import useSettings from '@app/hooks/useSettings';
|
|||||||
import { Permission, useUser } from '@app/hooks/useUser';
|
import { Permission, useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { RadioGroup } from '@headlessui/react';
|
import { RadioGroup } from '@headlessui/react';
|
||||||
import { ExclamationIcon } from '@heroicons/react/outline';
|
|
||||||
import { ArrowCircleRightIcon } from '@heroicons/react/solid';
|
import { ArrowCircleRightIcon } from '@heroicons/react/solid';
|
||||||
import { MediaStatus } from '@server/constants/media';
|
import { MediaStatus } from '@server/constants/media';
|
||||||
import type Issue from '@server/entity/Issue';
|
import type Issue from '@server/entity/Issue';
|
||||||
@@ -150,23 +149,14 @@ const CreateIssueModal = ({
|
|||||||
<Modal
|
<Modal
|
||||||
backgroundClickable
|
backgroundClickable
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
iconSvg={<ExclamationIcon />}
|
|
||||||
title={intl.formatMessage(messages.reportissue)}
|
title={intl.formatMessage(messages.reportissue)}
|
||||||
|
subTitle={data && isMovie(data) ? data?.title : data?.name}
|
||||||
cancelText={intl.formatMessage(globalMessages.close)}
|
cancelText={intl.formatMessage(globalMessages.close)}
|
||||||
onOk={() => handleSubmit()}
|
onOk={() => handleSubmit()}
|
||||||
okText={intl.formatMessage(messages.submitissue)}
|
okText={intl.formatMessage(messages.submitissue)}
|
||||||
loading={!data && !error}
|
loading={!data && !error}
|
||||||
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
|
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
|
||||||
>
|
>
|
||||||
{data && (
|
|
||||||
<div className="flex items-center">
|
|
||||||
<span className="mr-1 font-semibold">
|
|
||||||
{intl.formatMessage(messages.issomethingwrong, {
|
|
||||||
title: isMovie(data) ? data.title : data.name,
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{mediaType === 'tv' && data && !isMovie(data) && (
|
{mediaType === 'tv' && data && !isMovie(data) && (
|
||||||
<>
|
<>
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
@@ -264,7 +254,7 @@ const CreateIssueModal = ({
|
|||||||
? 'rounded-bl-md rounded-br-md'
|
? 'rounded-bl-md rounded-br-md'
|
||||||
: '',
|
: '',
|
||||||
checked
|
checked
|
||||||
? 'z-10 border-indigo-500 bg-indigo-600'
|
? 'z-10 border border-indigo-500 bg-indigo-400 bg-opacity-20'
|
||||||
: 'border-gray-500',
|
: 'border-gray-500',
|
||||||
'relative flex cursor-pointer border p-4 focus:outline-none'
|
'relative flex cursor-pointer border p-4 focus:outline-none'
|
||||||
)
|
)
|
||||||
@@ -275,7 +265,7 @@ const CreateIssueModal = ({
|
|||||||
<span
|
<span
|
||||||
className={`${
|
className={`${
|
||||||
checked
|
checked
|
||||||
? 'border-transparent bg-indigo-800'
|
? 'border-transparent bg-indigo-600'
|
||||||
: 'border-gray-300 bg-white'
|
: 'border-gray-300 bg-white'
|
||||||
} ${
|
} ${
|
||||||
active ? 'ring-2 ring-indigo-300 ring-offset-2' : ''
|
active ? 'ring-2 ring-indigo-300 ring-offset-2' : ''
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import 'ace-builds/src-noconflict/ace';
|
||||||
import 'ace-builds/src-noconflict/mode-json';
|
import 'ace-builds/src-noconflict/mode-json';
|
||||||
import 'ace-builds/src-noconflict/theme-dracula';
|
import 'ace-builds/src-noconflict/theme-dracula';
|
||||||
import type { HTMLAttributes } from 'react';
|
import type { HTMLAttributes } from 'react';
|
||||||
import AceEditor from 'react-ace';
|
import AceEditor from 'react-ace';
|
||||||
|
|
||||||
interface JSONEditorProps extends HTMLAttributes<HTMLDivElement> {
|
interface JSONEditorProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
|
|||||||
@@ -408,7 +408,7 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
|
|||||||
{hasPermission(Permission.MANAGE_REQUESTS) && data.mediaInfo && (
|
{hasPermission(Permission.MANAGE_REQUESTS) && data.mediaInfo && (
|
||||||
<Tooltip content={intl.formatMessage(messages.managemovie)}>
|
<Tooltip content={intl.formatMessage(messages.managemovie)}>
|
||||||
<Button
|
<Button
|
||||||
buttonType="default"
|
buttonType="ghost"
|
||||||
onClick={() => setShowManager(true)}
|
onClick={() => setShowManager(true)}
|
||||||
className="relative ml-2 first:ml-0"
|
className="relative ml-2 first:ml-0"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ const RequestBlock = ({ request, onUpdate }: RequestBlockProps) => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip content={intl.formatMessage(messages.edit)}>
|
<Tooltip content={intl.formatMessage(messages.edit)}>
|
||||||
<Button
|
<Button
|
||||||
buttonType="primary"
|
buttonType="warning"
|
||||||
onClick={() => setShowEditModal(true)}
|
onClick={() => setShowEditModal(true)}
|
||||||
disabled={isUpdating}
|
disabled={isUpdating}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { Permission, useUser } from '@app/hooks/useUser';
|
|||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { formatBytes } from '@app/utils/numberHelpers';
|
import { formatBytes } from '@app/utils/numberHelpers';
|
||||||
import { Listbox, Transition } from '@headlessui/react';
|
import { Listbox, Transition } from '@headlessui/react';
|
||||||
import { AdjustmentsIcon } from '@heroicons/react/outline';
|
|
||||||
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/solid';
|
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/solid';
|
||||||
import type {
|
import type {
|
||||||
ServiceCommonServer,
|
ServiceCommonServer,
|
||||||
@@ -281,11 +280,10 @@ const AdvancedRequester = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mt-4 mb-2 flex items-center font-bold tracking-wider">
|
<div className="mt-4 mb-2 flex items-center text-lg font-semibold">
|
||||||
<AdjustmentsIcon className="mr-1.5 h-5 w-5" />
|
|
||||||
{intl.formatMessage(messages.advancedoptions)}
|
{intl.formatMessage(messages.advancedoptions)}
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-md bg-gray-600 p-4 shadow">
|
<div className="rounded-md">
|
||||||
{!!data && selectedServer !== null && (
|
{!!data && selectedServer !== null && (
|
||||||
<div className="flex flex-col md:flex-row">
|
<div className="flex flex-col md:flex-row">
|
||||||
{data.filter((server) => server.is4k === is4k).length > 1 && (
|
{data.filter((server) => server.is4k === is4k).length > 1 && (
|
||||||
@@ -561,7 +559,7 @@ const AdvancedRequester = ({
|
|||||||
leave="transition ease-in duration-100"
|
leave="transition ease-in duration-100"
|
||||||
leaveFrom="opacity-100"
|
leaveFrom="opacity-100"
|
||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0"
|
||||||
className="mt-1 w-full rounded-md bg-gray-800 shadow-lg"
|
className="mt-1 w-full rounded-md border border-gray-700 bg-gray-800 shadow-lg"
|
||||||
>
|
>
|
||||||
<Listbox.Options
|
<Listbox.Options
|
||||||
static
|
static
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import AdvancedRequester from '@app/components/RequestModal/AdvancedRequester';
|
|||||||
import QuotaDisplay from '@app/components/RequestModal/QuotaDisplay';
|
import QuotaDisplay from '@app/components/RequestModal/QuotaDisplay';
|
||||||
import { useUser } from '@app/hooks/useUser';
|
import { useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { DownloadIcon } from '@heroicons/react/outline';
|
|
||||||
import { MediaRequestStatus, MediaStatus } from '@server/constants/media';
|
import { MediaRequestStatus, MediaStatus } from '@server/constants/media';
|
||||||
import type { MediaRequest } from '@server/entity/MediaRequest';
|
import type { MediaRequest } from '@server/entity/MediaRequest';
|
||||||
import type { QuotaResponse } from '@server/interfaces/api/userInterfaces';
|
import type { QuotaResponse } from '@server/interfaces/api/userInterfaces';
|
||||||
@@ -22,8 +21,8 @@ import useSWR from 'swr';
|
|||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
requestadmin: 'This request will be approved automatically.',
|
requestadmin: 'This request will be approved automatically.',
|
||||||
requestSuccess: '<strong>{title}</strong> requested successfully!',
|
requestSuccess: '<strong>{title}</strong> requested successfully!',
|
||||||
requesttitle: 'Request {title}',
|
requestcollectiontitle: 'Request Collection',
|
||||||
request4ktitle: 'Request {title} in 4K',
|
requestcollection4ktitle: 'Request Collection in 4K',
|
||||||
requesterror: 'Something went wrong while submitting the request.',
|
requesterror: 'Something went wrong while submitting the request.',
|
||||||
selectmovies: 'Select Movie(s)',
|
selectmovies: 'Select Movie(s)',
|
||||||
requestmovies: 'Request {count} {count, plural, one {Movie} other {Movies}}',
|
requestmovies: 'Request {count} {count, plural, one {Movie} other {Movies}}',
|
||||||
@@ -249,9 +248,11 @@ const CollectionRequestModal = ({
|
|||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
onOk={sendRequest}
|
onOk={sendRequest}
|
||||||
title={intl.formatMessage(
|
title={intl.formatMessage(
|
||||||
is4k ? messages.request4ktitle : messages.requesttitle,
|
is4k
|
||||||
{ title: data?.name }
|
? messages.requestcollection4ktitle
|
||||||
|
: messages.requestcollectiontitle
|
||||||
)}
|
)}
|
||||||
|
subTitle={data?.name}
|
||||||
okText={
|
okText={
|
||||||
isUpdating
|
isUpdating
|
||||||
? intl.formatMessage(globalMessages.requesting)
|
? intl.formatMessage(globalMessages.requesting)
|
||||||
@@ -266,7 +267,6 @@ const CollectionRequestModal = ({
|
|||||||
}
|
}
|
||||||
okDisabled={selectedParts.length === 0}
|
okDisabled={selectedParts.length === 0}
|
||||||
okButtonType={'primary'}
|
okButtonType={'primary'}
|
||||||
iconSvg={<DownloadIcon />}
|
|
||||||
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
|
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
|
||||||
>
|
>
|
||||||
{hasAutoApprove && !quota?.movie.restricted && (
|
{hasAutoApprove && !quota?.movie.restricted && (
|
||||||
@@ -292,11 +292,11 @@ const CollectionRequestModal = ({
|
|||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="-mx-4 sm:mx-0">
|
<div className="-mx-4 sm:mx-0">
|
||||||
<div className="inline-block min-w-full py-2 align-middle">
|
<div className="inline-block min-w-full py-2 align-middle">
|
||||||
<div className="overflow-hidden shadow sm:rounded-lg">
|
<div className="overflow-hidden border border-gray-700 backdrop-blur sm:rounded-lg">
|
||||||
<table className="min-w-full">
|
<table className="min-w-full">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="w-16 bg-gray-500 px-4 py-3">
|
<th className="w-16 bg-gray-700 bg-opacity-80 px-4 py-3">
|
||||||
<span
|
<span
|
||||||
role="checkbox"
|
role="checkbox"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@@ -328,15 +328,15 @@ const CollectionRequestModal = ({
|
|||||||
></span>
|
></span>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
<th className="bg-gray-500 px-1 py-3 text-left text-xs font-medium uppercase leading-4 tracking-wider text-gray-200 md:px-6">
|
<th className="bg-gray-700 bg-opacity-80 px-1 py-3 text-left text-xs font-medium uppercase leading-4 tracking-wider text-gray-200 md:px-6">
|
||||||
{intl.formatMessage(globalMessages.movie)}
|
{intl.formatMessage(globalMessages.movie)}
|
||||||
</th>
|
</th>
|
||||||
<th className="bg-gray-500 px-2 py-3 text-left text-xs font-medium uppercase leading-4 tracking-wider text-gray-200 md:px-6">
|
<th className="bg-gray-700 bg-opacity-80 px-2 py-3 text-left text-xs font-medium uppercase leading-4 tracking-wider text-gray-200 md:px-6">
|
||||||
{intl.formatMessage(globalMessages.status)}
|
{intl.formatMessage(globalMessages.status)}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-gray-700 bg-gray-600">
|
<tbody className="divide-y divide-gray-700">
|
||||||
{data?.parts.map((part) => {
|
{data?.parts.map((part) => {
|
||||||
const partRequest = getPartRequest(part.id);
|
const partRequest = getPartRequest(part.id);
|
||||||
const partMedia =
|
const partMedia =
|
||||||
@@ -378,7 +378,7 @@ const CollectionRequestModal = ({
|
|||||||
partRequest ||
|
partRequest ||
|
||||||
isSelectedPart(part.id)
|
isSelectedPart(part.id)
|
||||||
? 'bg-indigo-500'
|
? 'bg-indigo-500'
|
||||||
: 'bg-gray-800'
|
: 'bg-gray-700'
|
||||||
} absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out`}
|
} absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out`}
|
||||||
></span>
|
></span>
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import AdvancedRequester from '@app/components/RequestModal/AdvancedRequester';
|
|||||||
import QuotaDisplay from '@app/components/RequestModal/QuotaDisplay';
|
import QuotaDisplay from '@app/components/RequestModal/QuotaDisplay';
|
||||||
import { useUser } from '@app/hooks/useUser';
|
import { useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { DownloadIcon } from '@heroicons/react/outline';
|
|
||||||
import { MediaStatus } from '@server/constants/media';
|
import { MediaStatus } from '@server/constants/media';
|
||||||
import type { MediaRequest } from '@server/entity/MediaRequest';
|
import type { MediaRequest } from '@server/entity/MediaRequest';
|
||||||
import type { QuotaResponse } from '@server/interfaces/api/userInterfaces';
|
import type { QuotaResponse } from '@server/interfaces/api/userInterfaces';
|
||||||
@@ -21,13 +20,13 @@ const messages = defineMessages({
|
|||||||
requestadmin: 'This request will be approved automatically.',
|
requestadmin: 'This request will be approved automatically.',
|
||||||
requestSuccess: '<strong>{title}</strong> requested successfully!',
|
requestSuccess: '<strong>{title}</strong> requested successfully!',
|
||||||
requestCancel: 'Request for <strong>{title}</strong> canceled.',
|
requestCancel: 'Request for <strong>{title}</strong> canceled.',
|
||||||
requesttitle: 'Request {title}',
|
requestmovietitle: 'Request Movie',
|
||||||
request4ktitle: 'Request {title} in 4K',
|
requestmovie4ktitle: 'Request Movie in 4K',
|
||||||
edit: 'Edit Request',
|
edit: 'Edit Request',
|
||||||
approve: 'Approve Request',
|
approve: 'Approve Request',
|
||||||
cancel: 'Cancel Request',
|
cancel: 'Cancel Request',
|
||||||
pendingrequest: 'Pending Request for {title}',
|
pendingrequest: 'Pending Movie Request',
|
||||||
pending4krequest: 'Pending 4K Request for {title}',
|
pending4krequest: 'Pending 4K Movie Request',
|
||||||
requestfrom: "{username}'s request is pending approval.",
|
requestfrom: "{username}'s request is pending approval.",
|
||||||
errorediting: 'Something went wrong while editing the request.',
|
errorediting: 'Something went wrong while editing the request.',
|
||||||
requestedited: 'Request for <strong>{title}</strong> edited successfully!',
|
requestedited: 'Request for <strong>{title}</strong> edited successfully!',
|
||||||
@@ -218,9 +217,9 @@ const MovieRequestModal = ({
|
|||||||
backgroundClickable
|
backgroundClickable
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
title={intl.formatMessage(
|
title={intl.formatMessage(
|
||||||
is4k ? messages.pending4krequest : messages.pendingrequest,
|
is4k ? messages.pending4krequest : messages.pendingrequest
|
||||||
{ title: data?.title }
|
|
||||||
)}
|
)}
|
||||||
|
subTitle={data?.title}
|
||||||
onOk={() =>
|
onOk={() =>
|
||||||
hasPermission(Permission.MANAGE_REQUESTS)
|
hasPermission(Permission.MANAGE_REQUESTS)
|
||||||
? updateRequest(true)
|
? updateRequest(true)
|
||||||
@@ -264,7 +263,6 @@ const MovieRequestModal = ({
|
|||||||
}
|
}
|
||||||
secondaryButtonType="danger"
|
secondaryButtonType="danger"
|
||||||
cancelText={intl.formatMessage(globalMessages.close)}
|
cancelText={intl.formatMessage(globalMessages.close)}
|
||||||
iconSvg={<DownloadIcon />}
|
|
||||||
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
|
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
|
||||||
>
|
>
|
||||||
{isOwner
|
{isOwner
|
||||||
@@ -310,9 +308,9 @@ const MovieRequestModal = ({
|
|||||||
onOk={sendRequest}
|
onOk={sendRequest}
|
||||||
okDisabled={isUpdating || quota?.movie.restricted}
|
okDisabled={isUpdating || quota?.movie.restricted}
|
||||||
title={intl.formatMessage(
|
title={intl.formatMessage(
|
||||||
is4k ? messages.request4ktitle : messages.requesttitle,
|
is4k ? messages.requestmovie4ktitle : messages.requestmovietitle
|
||||||
{ title: data?.title }
|
|
||||||
)}
|
)}
|
||||||
|
subTitle={data?.title}
|
||||||
okText={
|
okText={
|
||||||
isUpdating
|
isUpdating
|
||||||
? intl.formatMessage(globalMessages.requesting)
|
? intl.formatMessage(globalMessages.requesting)
|
||||||
@@ -321,7 +319,6 @@ const MovieRequestModal = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
okButtonType={'primary'}
|
okButtonType={'primary'}
|
||||||
iconSvg={<DownloadIcon />}
|
|
||||||
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
|
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
|
||||||
>
|
>
|
||||||
{hasAutoApprove && !quota?.movie.restricted && (
|
{hasAutoApprove && !quota?.movie.restricted && (
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const QuotaDisplay = ({
|
|||||||
const [showDetails, setShowDetails] = useState(false);
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="my-4 flex flex-col rounded-md bg-gray-800 p-4"
|
className="my-4 flex flex-col rounded-md border border-gray-700 p-4 backdrop-blur"
|
||||||
onClick={() => setShowDetails((s) => !s)}
|
onClick={() => setShowDetails((s) => !s)}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import Alert from '@app/components/Common/Alert';
|
|||||||
import { SmallLoadingSpinner } from '@app/components/Common/LoadingSpinner';
|
import { SmallLoadingSpinner } from '@app/components/Common/LoadingSpinner';
|
||||||
import Modal from '@app/components/Common/Modal';
|
import Modal from '@app/components/Common/Modal';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { DownloadIcon } from '@heroicons/react/outline';
|
|
||||||
import type { SonarrSeries } from '@server/api/servarr/sonarr';
|
import type { SonarrSeries } from '@server/api/servarr/sonarr';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
@@ -20,6 +19,7 @@ interface SearchByNameModalProps {
|
|||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
closeModal: () => void;
|
closeModal: () => void;
|
||||||
modalTitle: string;
|
modalTitle: string;
|
||||||
|
modalSubTitle: string;
|
||||||
tmdbId: number;
|
tmdbId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +30,7 @@ const SearchByNameModal = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
closeModal,
|
closeModal,
|
||||||
modalTitle,
|
modalTitle,
|
||||||
|
modalSubTitle,
|
||||||
tmdbId,
|
tmdbId,
|
||||||
}: SearchByNameModalProps) => {
|
}: SearchByNameModalProps) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@@ -48,10 +49,10 @@ const SearchByNameModal = ({
|
|||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
onOk={closeModal}
|
onOk={closeModal}
|
||||||
title={modalTitle}
|
title={modalTitle}
|
||||||
|
subTitle={modalSubTitle}
|
||||||
okText={intl.formatMessage(globalMessages.next)}
|
okText={intl.formatMessage(globalMessages.next)}
|
||||||
okDisabled={!tvdbId}
|
okDisabled={!tvdbId}
|
||||||
okButtonType="primary"
|
okButtonType="primary"
|
||||||
iconSvg={<DownloadIcon />}
|
|
||||||
>
|
>
|
||||||
<Alert
|
<Alert
|
||||||
title={intl.formatMessage(messages.notvdbiddescription)}
|
title={intl.formatMessage(messages.notvdbiddescription)}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import SearchByNameModal from '@app/components/RequestModal/SearchByNameModal';
|
|||||||
import useSettings from '@app/hooks/useSettings';
|
import useSettings from '@app/hooks/useSettings';
|
||||||
import { useUser } from '@app/hooks/useUser';
|
import { useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { DownloadIcon } from '@heroicons/react/outline';
|
|
||||||
import { ANIME_KEYWORD_ID } from '@server/api/themoviedb/constants';
|
import { ANIME_KEYWORD_ID } from '@server/api/themoviedb/constants';
|
||||||
import { MediaRequestStatus, MediaStatus } from '@server/constants/media';
|
import { MediaRequestStatus, MediaStatus } from '@server/constants/media';
|
||||||
import type { MediaRequest } from '@server/entity/MediaRequest';
|
import type { MediaRequest } from '@server/entity/MediaRequest';
|
||||||
@@ -25,13 +24,13 @@ import useSWR, { mutate } from 'swr';
|
|||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
requestadmin: 'This request will be approved automatically.',
|
requestadmin: 'This request will be approved automatically.',
|
||||||
requestSuccess: '<strong>{title}</strong> requested successfully!',
|
requestSuccess: '<strong>{title}</strong> requested successfully!',
|
||||||
requesttitle: 'Request {title}',
|
requestseriestitle: 'Request Series',
|
||||||
request4ktitle: 'Request {title} in 4K',
|
requestseries4ktitle: 'Request Series in 4K',
|
||||||
edit: 'Edit Request',
|
edit: 'Edit Request',
|
||||||
approve: 'Approve Request',
|
approve: 'Approve Request',
|
||||||
cancel: 'Cancel Request',
|
cancel: 'Cancel Request',
|
||||||
pendingrequest: 'Pending Request for {title}',
|
pendingrequest: 'Pending Request',
|
||||||
pending4krequest: 'Pending 4K Request for {title}',
|
pending4krequest: 'Pending 4K Request',
|
||||||
requestfrom: "{username}'s request is pending approval.",
|
requestfrom: "{username}'s request is pending approval.",
|
||||||
requestseasons:
|
requestseasons:
|
||||||
'Request {seasonCount} {seasonCount, plural, one {Season} other {Seasons}}',
|
'Request {seasonCount} {seasonCount, plural, one {Season} other {Seasons}}',
|
||||||
@@ -367,9 +366,9 @@ const TvRequestModal = ({
|
|||||||
loading={!error}
|
loading={!error}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
modalTitle={intl.formatMessage(
|
modalTitle={intl.formatMessage(
|
||||||
is4k ? messages.request4ktitle : messages.requesttitle,
|
is4k ? messages.requestseries4ktitle : messages.requestseriestitle
|
||||||
{ title: data?.name }
|
|
||||||
)}
|
)}
|
||||||
|
modalSubTitle={data.name}
|
||||||
tmdbId={tmdbId}
|
tmdbId={tmdbId}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@@ -390,10 +389,10 @@ const TvRequestModal = ({
|
|||||||
? messages.pending4krequest
|
? messages.pending4krequest
|
||||||
: messages.pendingrequest
|
: messages.pendingrequest
|
||||||
: is4k
|
: is4k
|
||||||
? messages.request4ktitle
|
? messages.requestseries4ktitle
|
||||||
: messages.requesttitle,
|
: messages.requestseriestitle
|
||||||
{ title: data?.name }
|
|
||||||
)}
|
)}
|
||||||
|
subTitle={data?.name}
|
||||||
okText={
|
okText={
|
||||||
editRequest
|
editRequest
|
||||||
? selectedSeasons.length === 0
|
? selectedSeasons.length === 0
|
||||||
@@ -444,7 +443,6 @@ const TvRequestModal = ({
|
|||||||
? intl.formatMessage(globalMessages.back)
|
? intl.formatMessage(globalMessages.back)
|
||||||
: intl.formatMessage(globalMessages.cancel)
|
: intl.formatMessage(globalMessages.cancel)
|
||||||
}
|
}
|
||||||
iconSvg={<DownloadIcon />}
|
|
||||||
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
|
backdrop={`https://image.tmdb.org/t/p/w1920_and_h800_multi_faces/${data?.backdropPath}`}
|
||||||
>
|
>
|
||||||
{editRequest
|
{editRequest
|
||||||
@@ -502,12 +500,12 @@ const TvRequestModal = ({
|
|||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="-mx-4 sm:mx-0">
|
<div className="-mx-4 sm:mx-0">
|
||||||
<div className="inline-block min-w-full py-2 align-middle">
|
<div className="inline-block min-w-full py-2 align-middle">
|
||||||
<div className="overflow-hidden shadow sm:rounded-lg">
|
<div className="overflow-hidden border border-gray-700 shadow backdrop-blur sm:rounded-lg">
|
||||||
<table className="min-w-full">
|
<table className="min-w-full">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th
|
||||||
className={`w-16 bg-gray-500 px-4 py-3 ${
|
className={`w-16 bg-gray-700 bg-opacity-80 px-4 py-3 ${
|
||||||
!settings.currentSettings.partialRequestsEnabled &&
|
!settings.currentSettings.partialRequestsEnabled &&
|
||||||
'hidden'
|
'hidden'
|
||||||
}`}
|
}`}
|
||||||
@@ -544,18 +542,18 @@ const TvRequestModal = ({
|
|||||||
></span>
|
></span>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
<th className="bg-gray-500 px-1 py-3 text-left text-xs font-medium uppercase leading-4 tracking-wider text-gray-200 md:px-6">
|
<th className="bg-gray-700 bg-opacity-80 px-1 py-3 text-left text-xs font-medium uppercase leading-4 tracking-wider text-gray-200 md:px-6">
|
||||||
{intl.formatMessage(messages.season)}
|
{intl.formatMessage(messages.season)}
|
||||||
</th>
|
</th>
|
||||||
<th className="bg-gray-500 px-5 py-3 text-left text-xs font-medium uppercase leading-4 tracking-wider text-gray-200 md:px-6">
|
<th className="bg-gray-700 bg-opacity-80 px-5 py-3 text-left text-xs font-medium uppercase leading-4 tracking-wider text-gray-200 md:px-6">
|
||||||
{intl.formatMessage(messages.numberofepisodes)}
|
{intl.formatMessage(messages.numberofepisodes)}
|
||||||
</th>
|
</th>
|
||||||
<th className="bg-gray-500 px-2 py-3 text-left text-xs font-medium uppercase leading-4 tracking-wider text-gray-200 md:px-6">
|
<th className="bg-gray-700 bg-opacity-80 px-2 py-3 text-left text-xs font-medium uppercase leading-4 tracking-wider text-gray-200 md:px-6">
|
||||||
{intl.formatMessage(globalMessages.status)}
|
{intl.formatMessage(globalMessages.status)}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-gray-700 bg-gray-600">
|
<tbody className="divide-y divide-gray-700">
|
||||||
{data?.seasons
|
{data?.seasons
|
||||||
.filter((season) => season.seasonNumber !== 0)
|
.filter((season) => season.seasonNumber !== 0)
|
||||||
.map((season) => {
|
.map((season) => {
|
||||||
@@ -614,7 +612,7 @@ const TvRequestModal = ({
|
|||||||
)) ||
|
)) ||
|
||||||
isSelectedSeason(season.seasonNumber)
|
isSelectedSeason(season.seasonNumber)
|
||||||
? 'bg-indigo-500'
|
? 'bg-indigo-500'
|
||||||
: 'bg-gray-800'
|
: 'bg-gray-700'
|
||||||
} absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out`}
|
} absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out`}
|
||||||
></span>
|
></span>
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ import { useToasts } from 'react-toast-notifications';
|
|||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
const JSONEditor = dynamic(() => import('../../../JSONEditor'), { ssr: false });
|
const JSONEditor = dynamic(() => import('@app/components/JSONEditor'), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
const defaultPayload = {
|
const defaultPayload = {
|
||||||
notification_type: '{{notification_type}}',
|
notification_type: '{{notification_type}}',
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import Modal from '@app/components/Common/Modal';
|
|||||||
import SensitiveInput from '@app/components/Common/SensitiveInput';
|
import SensitiveInput from '@app/components/Common/SensitiveInput';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { Transition } from '@headlessui/react';
|
import { Transition } from '@headlessui/react';
|
||||||
import { PencilIcon, PlusIcon } from '@heroicons/react/solid';
|
|
||||||
import type { RadarrSettings } from '@server/lib/settings';
|
import type { RadarrSettings } from '@server/lib/settings';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Formik } from 'formik';
|
import { Field, Formik } from 'formik';
|
||||||
@@ -340,7 +339,6 @@ const RadarrModal = ({ onClose, radarr, onSave }: RadarrModalProps) => {
|
|||||||
values.is4k ? messages.edit4kradarr : messages.editradarr
|
values.is4k ? messages.edit4kradarr : messages.editradarr
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
iconSvg={!radarr ? <PlusIcon /> : <PencilIcon />}
|
|
||||||
>
|
>
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
|
|||||||
@@ -5,11 +5,18 @@ import Modal from '@app/components/Common/Modal';
|
|||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { Transition } from '@headlessui/react';
|
import { Transition } from '@headlessui/react';
|
||||||
import { DocumentTextIcon } from '@heroicons/react/outline';
|
import { DocumentTextIcon } from '@heroicons/react/outline';
|
||||||
import { useState } from 'react';
|
import dynamic from 'next/dynamic';
|
||||||
|
import { Fragment, useState } from 'react';
|
||||||
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
||||||
import ReactMarkdown from 'react-markdown';
|
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
|
// dyanmic is having trouble extracting the props for react-markdown here so we are just ignoring it since its really
|
||||||
|
// only children we are using
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const ReactMarkdown = dynamic<any>(() => import('react-markdown'), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
releases: 'Releases',
|
releases: 'Releases',
|
||||||
releasedataMissing: 'Release data is currently unavailable.',
|
releasedataMissing: 'Release data is currently unavailable.',
|
||||||
@@ -55,7 +62,7 @@ const Release = ({ currentVersion, release, isLatest }: ReleaseProps) => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex w-full flex-col space-y-3 rounded-md bg-gray-800 px-4 py-2 shadow-md ring-1 ring-gray-700 sm:flex-row sm:space-y-0 sm:space-x-3">
|
<div className="flex w-full flex-col space-y-3 rounded-md bg-gray-800 px-4 py-2 shadow-md ring-1 ring-gray-700 sm:flex-row sm:space-y-0 sm:space-x-3">
|
||||||
<Transition
|
<Transition
|
||||||
as="div"
|
as={Fragment}
|
||||||
enter="opacity-0 transition duration-300"
|
enter="opacity-0 transition duration-300"
|
||||||
enterFrom="opacity-0"
|
enterFrom="opacity-0"
|
||||||
enterTo="opacity-100"
|
enterTo="opacity-100"
|
||||||
@@ -66,7 +73,6 @@ const Release = ({ currentVersion, release, isLatest }: ReleaseProps) => {
|
|||||||
>
|
>
|
||||||
<Modal
|
<Modal
|
||||||
onCancel={() => setModalOpen(false)}
|
onCancel={() => setModalOpen(false)}
|
||||||
iconSvg={<DocumentTextIcon />}
|
|
||||||
title={intl.formatMessage(messages.versionChangelog, {
|
title={intl.formatMessage(messages.versionChangelog, {
|
||||||
version: release.name,
|
version: release.name,
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -60,19 +60,19 @@ const SettingsAbout = () => {
|
|||||||
intl.formatMessage(globalMessages.settings),
|
intl.formatMessage(globalMessages.settings),
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<div className="mt-6 rounded-md bg-indigo-700 p-4">
|
<div className="mt-6 rounded-md border border-indigo-500 bg-indigo-400 bg-opacity-20 p-4 backdrop-blur">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<InformationCircleIcon className="h-5 w-5 text-white" />
|
<InformationCircleIcon className="h-5 w-5 text-gray-100" />
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3 flex-1 md:flex md:justify-between">
|
<div className="ml-3 flex-1 md:flex md:justify-between">
|
||||||
<p className="text-sm leading-5 text-white">
|
<p className="text-sm leading-5 text-gray-100">
|
||||||
{intl.formatMessage(messages.betawarning)}
|
{intl.formatMessage(messages.betawarning)}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-3 text-sm leading-5 md:mt-0 md:ml-6">
|
<p className="mt-3 text-sm leading-5 md:mt-0 md:ml-6">
|
||||||
<a
|
<a
|
||||||
href="http://github.com/sct/overseerr"
|
href="http://github.com/sct/overseerr"
|
||||||
className="whitespace-nowrap font-medium text-indigo-100 transition duration-150 ease-in-out hover:text-white"
|
className="whitespace-nowrap font-medium text-gray-100 transition duration-150 ease-in-out hover:text-white"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { PencilIcon } from '@heroicons/react/solid';
|
|||||||
import type { CacheItem } from '@server/interfaces/api/settingsInterfaces';
|
import type { CacheItem } from '@server/interfaces/api/settingsInterfaces';
|
||||||
import type { JobId } from '@server/lib/settings';
|
import type { JobId } from '@server/lib/settings';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useState } from 'react';
|
import { Fragment, useState } from 'react';
|
||||||
import type { MessageDescriptor } from 'react-intl';
|
import type { MessageDescriptor } from 'react-intl';
|
||||||
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
import { defineMessages, FormattedRelativeTime, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
@@ -187,7 +187,7 @@ const SettingsJobs = () => {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Transition
|
<Transition
|
||||||
as="div"
|
as={Fragment}
|
||||||
enter="opacity-0 transition duration-300"
|
enter="opacity-0 transition duration-300"
|
||||||
enterFrom="opacity-0"
|
enterFrom="opacity-0"
|
||||||
enterTo="opacity-100"
|
enterTo="opacity-100"
|
||||||
@@ -203,7 +203,6 @@ const SettingsJobs = () => {
|
|||||||
? intl.formatMessage(globalMessages.saving)
|
? intl.formatMessage(globalMessages.saving)
|
||||||
: intl.formatMessage(globalMessages.save)
|
: intl.formatMessage(globalMessages.save)
|
||||||
}
|
}
|
||||||
iconSvg={<PencilIcon />}
|
|
||||||
onCancel={() => setJobEditModal({ isOpen: false })}
|
onCancel={() => setJobEditModal({ isOpen: false })}
|
||||||
okDisabled={isSaving}
|
okDisabled={isSaving}
|
||||||
onOk={() => scheduleJob()}
|
onOk={() => scheduleJob()}
|
||||||
@@ -325,7 +324,7 @@ const SettingsJobs = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<PencilIcon />
|
<PencilIcon />
|
||||||
{intl.formatMessage(globalMessages.edit)}
|
<span>{intl.formatMessage(globalMessages.edit)}</span>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{job.running ? (
|
{job.running ? (
|
||||||
@@ -335,7 +334,7 @@ const SettingsJobs = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button buttonType="primary" onClick={() => runJob(job)}>
|
<Button buttonType="primary" onClick={() => runJob(job)}>
|
||||||
<PlayIcon className="mr-1 h-5 w-5" />
|
<PlayIcon />
|
||||||
<span>{intl.formatMessage(messages.runnow)}</span>
|
<span>{intl.formatMessage(messages.runnow)}</span>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import type {
|
|||||||
} from '@server/interfaces/api/settingsInterfaces';
|
} from '@server/interfaces/api/settingsInterfaces';
|
||||||
import copy from 'copy-to-clipboard';
|
import copy from 'copy-to-clipboard';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useEffect, useState } from 'react';
|
import { Fragment, useEffect, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import { useToasts } from 'react-toast-notifications';
|
import { useToasts } from 'react-toast-notifications';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
@@ -138,7 +138,7 @@ const SettingsLogs = () => {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<Transition
|
<Transition
|
||||||
as="div"
|
as={Fragment}
|
||||||
enter="opacity-0 transition duration-300"
|
enter="opacity-0 transition duration-300"
|
||||||
enterFrom="opacity-0"
|
enterFrom="opacity-0"
|
||||||
enterTo="opacity-100"
|
enterTo="opacity-100"
|
||||||
@@ -150,7 +150,6 @@ const SettingsLogs = () => {
|
|||||||
>
|
>
|
||||||
<Modal
|
<Modal
|
||||||
title={intl.formatMessage(messages.logDetails)}
|
title={intl.formatMessage(messages.logDetails)}
|
||||||
iconSvg={<DocumentSearchIcon />}
|
|
||||||
onCancel={() => setActiveLog({ log: activeLog.log, isOpen: false })}
|
onCancel={() => setActiveLog({ log: activeLog.log, isOpen: false })}
|
||||||
cancelText={intl.formatMessage(globalMessages.close)}
|
cancelText={intl.formatMessage(globalMessages.close)}
|
||||||
onOk={() =>
|
onOk={() =>
|
||||||
|
|||||||
@@ -269,7 +269,6 @@ const SettingsServices = () => {
|
|||||||
serverType:
|
serverType:
|
||||||
deleteServerModal.type === 'radarr' ? 'Radarr' : 'Sonarr',
|
deleteServerModal.type === 'radarr' ? 'Radarr' : 'Sonarr',
|
||||||
})}
|
})}
|
||||||
iconSvg={<TrashIcon />}
|
|
||||||
>
|
>
|
||||||
{intl.formatMessage(messages.deleteserverconfirm)}
|
{intl.formatMessage(messages.deleteserverconfirm)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import Modal from '@app/components/Common/Modal';
|
|||||||
import SensitiveInput from '@app/components/Common/SensitiveInput';
|
import SensitiveInput from '@app/components/Common/SensitiveInput';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { Transition } from '@headlessui/react';
|
import { Transition } from '@headlessui/react';
|
||||||
import { PencilIcon, PlusIcon } from '@heroicons/react/solid';
|
|
||||||
import type { SonarrSettings } from '@server/lib/settings';
|
import type { SonarrSettings } from '@server/lib/settings';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Field, Formik } from 'formik';
|
import { Field, Formik } from 'formik';
|
||||||
@@ -369,7 +368,6 @@ const SonarrModal = ({ onClose, sonarr, onSave }: SonarrModalProps) => {
|
|||||||
values.is4k ? messages.edit4ksonarr : messages.editsonarr
|
values.is4k ? messages.edit4ksonarr : messages.editsonarr
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
iconSvg={!sonarr ? <PlusIcon /> : <PencilIcon />}
|
|
||||||
>
|
>
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ import useSettings from '@app/hooks/useSettings';
|
|||||||
import { Permission, useUser } from '@app/hooks/useUser';
|
import { Permission, useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { Transition } from '@headlessui/react';
|
import { Transition } from '@headlessui/react';
|
||||||
import { RefreshIcon, SparklesIcon } from '@heroicons/react/outline';
|
|
||||||
import type { StatusResponse } from '@server/interfaces/api/settingsInterfaces';
|
import type { StatusResponse } from '@server/interfaces/api/settingsInterfaces';
|
||||||
import { useEffect, useState } from 'react';
|
import { Fragment, useEffect, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
|
||||||
@@ -44,7 +43,7 @@ const StatusChecker = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition
|
<Transition
|
||||||
as="div"
|
as={Fragment}
|
||||||
enter="opacity-0 transition duration-300"
|
enter="opacity-0 transition duration-300"
|
||||||
enterFrom="opacity-0"
|
enterFrom="opacity-0"
|
||||||
enterTo="opacity-100"
|
enterTo="opacity-100"
|
||||||
@@ -60,7 +59,6 @@ const StatusChecker = () => {
|
|||||||
>
|
>
|
||||||
{hasPermission(Permission.ADMIN) && data.restartRequired ? (
|
{hasPermission(Permission.ADMIN) && data.restartRequired ? (
|
||||||
<Modal
|
<Modal
|
||||||
iconSvg={<RefreshIcon />}
|
|
||||||
title={intl.formatMessage(messages.restartRequired)}
|
title={intl.formatMessage(messages.restartRequired)}
|
||||||
backgroundClickable={false}
|
backgroundClickable={false}
|
||||||
onOk={() => {
|
onOk={() => {
|
||||||
@@ -75,7 +73,6 @@ const StatusChecker = () => {
|
|||||||
</Modal>
|
</Modal>
|
||||||
) : (
|
) : (
|
||||||
<Modal
|
<Modal
|
||||||
iconSvg={<SparklesIcon />}
|
|
||||||
title={intl.formatMessage(messages.appUpdated, {
|
title={intl.formatMessage(messages.appUpdated, {
|
||||||
applicationTitle: settings.currentSettings.applicationTitle,
|
applicationTitle: settings.currentSettings.applicationTitle,
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -418,7 +418,7 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
|
|||||||
{hasPermission(Permission.MANAGE_REQUESTS) && data.mediaInfo && (
|
{hasPermission(Permission.MANAGE_REQUESTS) && data.mediaInfo && (
|
||||||
<Tooltip content={intl.formatMessage(messages.manageseries)}>
|
<Tooltip content={intl.formatMessage(messages.manageseries)}>
|
||||||
<Button
|
<Button
|
||||||
buttonType="default"
|
buttonType="ghost"
|
||||||
onClick={() => setShowManager(true)}
|
onClick={() => setShowManager(true)}
|
||||||
className="relative ml-2 first:ml-0"
|
className="relative ml-2 first:ml-0"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import PermissionEdit from '@app/components/PermissionEdit';
|
|||||||
import type { User } from '@app/hooks/useUser';
|
import type { User } from '@app/hooks/useUser';
|
||||||
import { useUser } from '@app/hooks/useUser';
|
import { useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { PencilIcon } from '@heroicons/react/solid';
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
@@ -86,7 +85,6 @@ const BulkEditModal = ({
|
|||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={intl.formatMessage(messages.edituser)}
|
title={intl.formatMessage(messages.edituser)}
|
||||||
iconSvg={<PencilIcon />}
|
|
||||||
onOk={() => {
|
onOk={() => {
|
||||||
updateUsers();
|
updateUsers();
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import Alert from '@app/components/Common/Alert';
|
|||||||
import Modal from '@app/components/Common/Modal';
|
import Modal from '@app/components/Common/Modal';
|
||||||
import useSettings from '@app/hooks/useSettings';
|
import useSettings from '@app/hooks/useSettings';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { InboxInIcon } from '@heroicons/react/solid';
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
@@ -105,7 +104,6 @@ const PlexImportModal = ({ onCancel, onComplete }: PlexImportProps) => {
|
|||||||
<Modal
|
<Modal
|
||||||
loading={!data && !error}
|
loading={!data && !error}
|
||||||
title={intl.formatMessage(messages.importfromplex)}
|
title={intl.formatMessage(messages.importfromplex)}
|
||||||
iconSvg={<InboxInIcon />}
|
|
||||||
onOk={() => {
|
onOk={() => {
|
||||||
importUsers();
|
importUsers();
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import type { User } from '@app/hooks/useUser';
|
|||||||
import { Permission, UserType, useUser } from '@app/hooks/useUser';
|
import { Permission, UserType, useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
import { Transition } from '@headlessui/react';
|
import { Transition } from '@headlessui/react';
|
||||||
import { TrashIcon } from '@heroicons/react/outline';
|
|
||||||
import {
|
import {
|
||||||
ChevronLeftIcon,
|
ChevronLeftIcon,
|
||||||
ChevronRightIcon,
|
ChevronRightIcon,
|
||||||
@@ -49,7 +48,7 @@ const messages = defineMessages({
|
|||||||
owner: 'Owner',
|
owner: 'Owner',
|
||||||
admin: 'Admin',
|
admin: 'Admin',
|
||||||
plexuser: 'Plex User',
|
plexuser: 'Plex User',
|
||||||
deleteuser: 'Delete {username}',
|
deleteuser: 'Delete User',
|
||||||
userdeleted: 'User deleted successfully!',
|
userdeleted: 'User deleted successfully!',
|
||||||
userdeleteerror: 'Something went wrong while deleting the user.',
|
userdeleteerror: 'Something went wrong while deleting the user.',
|
||||||
deleteconfirm:
|
deleteconfirm:
|
||||||
@@ -249,10 +248,8 @@ const UserList = () => {
|
|||||||
onCancel={() =>
|
onCancel={() =>
|
||||||
setDeleteModal({ isOpen: false, user: deleteModal.user })
|
setDeleteModal({ isOpen: false, user: deleteModal.user })
|
||||||
}
|
}
|
||||||
title={intl.formatMessage(messages.deleteuser, {
|
title={intl.formatMessage(messages.deleteuser)}
|
||||||
username: `${deleteModal.user?.displayName}`,
|
subTitle={deleteModal.user?.displayName}
|
||||||
})}
|
|
||||||
iconSvg={<TrashIcon />}
|
|
||||||
>
|
>
|
||||||
{intl.formatMessage(messages.deleteconfirm)}
|
{intl.formatMessage(messages.deleteconfirm)}
|
||||||
</Modal>
|
</Modal>
|
||||||
@@ -317,7 +314,6 @@ const UserList = () => {
|
|||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={intl.formatMessage(messages.createlocaluser)}
|
title={intl.formatMessage(messages.createlocaluser)}
|
||||||
iconSvg={<UserAddIcon />}
|
|
||||||
onOk={() => handleSubmit()}
|
onOk={() => handleSubmit()}
|
||||||
okText={
|
okText={
|
||||||
isSubmitting
|
isSubmitting
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const ProfileHeader = ({ user, isSettingsPage }: ProfileHeaderProps) => {
|
|||||||
user.id === loggedInUser?.id ? '/profile' : `/users/${user.id}`
|
user.id === loggedInUser?.id ? '/profile' : `/users/${user.id}`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<a className="bg-gradient-to-br from-indigo-400 to-purple-400 bg-clip-text text-lg font-bold text-transparent hover:to-purple-200 sm:text-2xl">
|
<a className="text-overseerr text-lg font-bold hover:to-purple-200 sm:text-2xl">
|
||||||
{user.displayName}
|
{user.displayName}
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -380,23 +380,27 @@
|
|||||||
"components.RequestModal.errorediting": "Something went wrong while editing the request.",
|
"components.RequestModal.errorediting": "Something went wrong while editing the request.",
|
||||||
"components.RequestModal.extras": "Extras",
|
"components.RequestModal.extras": "Extras",
|
||||||
"components.RequestModal.numberofepisodes": "# of Episodes",
|
"components.RequestModal.numberofepisodes": "# of Episodes",
|
||||||
"components.RequestModal.pending4krequest": "Pending 4K Request for {title}",
|
"components.RequestModal.pending4krequest": "Pending 4K Request",
|
||||||
"components.RequestModal.pendingapproval": "Your request is pending approval.",
|
"components.RequestModal.pendingapproval": "Your request is pending approval.",
|
||||||
"components.RequestModal.pendingrequest": "Pending Request for {title}",
|
"components.RequestModal.pendingrequest": "Pending Request",
|
||||||
"components.RequestModal.request4ktitle": "Request {title} in 4K",
|
|
||||||
"components.RequestModal.requestApproved": "Request for <strong>{title}</strong> approved!",
|
"components.RequestModal.requestApproved": "Request for <strong>{title}</strong> approved!",
|
||||||
"components.RequestModal.requestCancel": "Request for <strong>{title}</strong> canceled.",
|
"components.RequestModal.requestCancel": "Request for <strong>{title}</strong> canceled.",
|
||||||
"components.RequestModal.requestSuccess": "<strong>{title}</strong> requested successfully!",
|
"components.RequestModal.requestSuccess": "<strong>{title}</strong> requested successfully!",
|
||||||
"components.RequestModal.requestadmin": "This request will be approved automatically.",
|
"components.RequestModal.requestadmin": "This request will be approved automatically.",
|
||||||
"components.RequestModal.requestcancelled": "Request for <strong>{title}</strong> canceled.",
|
"components.RequestModal.requestcancelled": "Request for <strong>{title}</strong> canceled.",
|
||||||
|
"components.RequestModal.requestcollection4ktitle": "Request Collection in 4K",
|
||||||
|
"components.RequestModal.requestcollectiontitle": "Request Collection",
|
||||||
"components.RequestModal.requestedited": "Request for <strong>{title}</strong> edited successfully!",
|
"components.RequestModal.requestedited": "Request for <strong>{title}</strong> edited successfully!",
|
||||||
"components.RequestModal.requesterror": "Something went wrong while submitting the request.",
|
"components.RequestModal.requesterror": "Something went wrong while submitting the request.",
|
||||||
"components.RequestModal.requestfrom": "{username}'s request is pending approval.",
|
"components.RequestModal.requestfrom": "{username}'s request is pending approval.",
|
||||||
|
"components.RequestModal.requestmovie4ktitle": "Request Movie in 4K",
|
||||||
"components.RequestModal.requestmovies": "Request {count} {count, plural, one {Movie} other {Movies}}",
|
"components.RequestModal.requestmovies": "Request {count} {count, plural, one {Movie} other {Movies}}",
|
||||||
"components.RequestModal.requestmovies4k": "Request {count} {count, plural, one {Movie} other {Movies}} in 4K",
|
"components.RequestModal.requestmovies4k": "Request {count} {count, plural, one {Movie} other {Movies}} in 4K",
|
||||||
|
"components.RequestModal.requestmovietitle": "Request Movie",
|
||||||
"components.RequestModal.requestseasons": "Request {seasonCount} {seasonCount, plural, one {Season} other {Seasons}}",
|
"components.RequestModal.requestseasons": "Request {seasonCount} {seasonCount, plural, one {Season} other {Seasons}}",
|
||||||
"components.RequestModal.requestseasons4k": "Request {seasonCount} {seasonCount, plural, one {Season} other {Seasons}} in 4K",
|
"components.RequestModal.requestseasons4k": "Request {seasonCount} {seasonCount, plural, one {Season} other {Seasons}} in 4K",
|
||||||
"components.RequestModal.requesttitle": "Request {title}",
|
"components.RequestModal.requestseries4ktitle": "Request Series in 4K",
|
||||||
|
"components.RequestModal.requestseriestitle": "Request Series",
|
||||||
"components.RequestModal.season": "Season",
|
"components.RequestModal.season": "Season",
|
||||||
"components.RequestModal.seasonnumber": "Season {number}",
|
"components.RequestModal.seasonnumber": "Season {number}",
|
||||||
"components.RequestModal.selectmovies": "Select Movie(s)",
|
"components.RequestModal.selectmovies": "Select Movie(s)",
|
||||||
@@ -920,7 +924,7 @@
|
|||||||
"components.UserList.createlocaluser": "Create Local User",
|
"components.UserList.createlocaluser": "Create Local User",
|
||||||
"components.UserList.creating": "Creating…",
|
"components.UserList.creating": "Creating…",
|
||||||
"components.UserList.deleteconfirm": "Are you sure you want to delete this user? All of their request data will be permanently removed.",
|
"components.UserList.deleteconfirm": "Are you sure you want to delete this user? All of their request data will be permanently removed.",
|
||||||
"components.UserList.deleteuser": "Delete {username}",
|
"components.UserList.deleteuser": "Delete User",
|
||||||
"components.UserList.displayName": "Display Name",
|
"components.UserList.displayName": "Display Name",
|
||||||
"components.UserList.edituser": "Edit User Permissions",
|
"components.UserList.edituser": "Edit User Permissions",
|
||||||
"components.UserList.email": "Email Address",
|
"components.UserList.email": "Email Address",
|
||||||
|
|||||||
@@ -307,7 +307,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
button.input-action {
|
button.input-action {
|
||||||
@apply relative -ml-px inline-flex items-center border border-gray-500 bg-indigo-600 px-3 py-2 text-sm font-medium leading-5 text-white transition duration-150 ease-in-out last:rounded-r-md hover:bg-indigo-500 active:bg-gray-100 active:text-gray-700 sm:px-3.5;
|
@apply relative -ml-px inline-flex items-center border border-gray-500 bg-indigo-600 bg-opacity-80 px-3 py-2 text-sm font-medium leading-5 text-white transition duration-150 ease-in-out last:rounded-r-md hover:bg-opacity-100 active:bg-gray-100 active:text-gray-700 sm:px-3.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-md svg,
|
.button-md svg,
|
||||||
@@ -320,14 +320,6 @@
|
|||||||
@apply ml-1.5 mr-1.5 h-4 w-4 first:ml-0 last:mr-0;
|
@apply ml-1.5 mr-1.5 h-4 w-4 first:ml-0 last:mr-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-icon {
|
|
||||||
@apply mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-gray-800 text-white ring-1 ring-gray-500 sm:mx-0 sm:h-10 sm:w-10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-icon svg {
|
|
||||||
@apply h-6 w-6;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg.icon-md {
|
svg.icon-md {
|
||||||
@apply h-5 w-5;
|
@apply h-5 w-5;
|
||||||
}
|
}
|
||||||
@@ -451,6 +443,10 @@
|
|||||||
scrollbar-width: none; /* Firefox */
|
scrollbar-width: none; /* Firefox */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-overseerr {
|
||||||
|
@apply bg-gradient-to-br from-indigo-400 to-purple-400 bg-clip-text text-transparent;
|
||||||
|
}
|
||||||
|
|
||||||
@media all and (display-mode: browser) {
|
@media all and (display-mode: browser) {
|
||||||
.pwa-only {
|
.pwa-only {
|
||||||
@apply hidden;
|
@apply hidden;
|
||||||
|
|||||||
@@ -1809,10 +1809,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
|
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
|
||||||
integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==
|
integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==
|
||||||
|
|
||||||
"@headlessui/react@1.6.6":
|
"@headlessui/react@^0.0.0-insiders.b301f04":
|
||||||
version "1.6.6"
|
version "0.0.0-insiders.b301f04"
|
||||||
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.6.6.tgz#3073c066b85535c9d28783da0a4d9288b5354d0c"
|
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-0.0.0-insiders.b301f04.tgz#12f209097f987cddbba72de40240a12648ebe12b"
|
||||||
integrity sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q==
|
integrity sha512-I+UUEUkdYp+AgKU1teWUZaICEg//OWl0R1UxCB50Jzfi3APu7aSY0Y+ABrgfEWDhBYTG9hNgXvMVTNCnFu49yA==
|
||||||
|
|
||||||
"@heroicons/react@1.0.6":
|
"@heroicons/react@1.0.6":
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
|
|||||||
Reference in New Issue
Block a user