refactor: modal redesign and fix for transitions (#2987)

pull/2993/head
Ryan Cohen 2 years ago committed by GitHub
parent 4d56320870
commit 889caaa733
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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,27 +133,37 @@ 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 || subTitle) && (
<div className="flex flex-col space-y-1">
{title && (
<span
className="truncate text-lg font-bold leading-6"
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>
{children && (

@ -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"

Loading…
Cancel
Save