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