mirror of
https://github.com/sct/overseerr.git
synced 2025-12-26 16:27:17 +01:00
feat: add divider between buttons and form on login page
This commit is contained in:
@@ -1,72 +0,0 @@
|
||||
import type * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import AnimateHeight from 'react-animate-height';
|
||||
|
||||
export interface AccordionProps {
|
||||
children: (args: AccordionChildProps) => JSX.Element;
|
||||
/** If true, only one accordion item can be open at any time */
|
||||
single?: boolean;
|
||||
/** If true, at least one accordion item will always be open */
|
||||
atLeastOne?: boolean;
|
||||
initialOpenIndexes?: number[];
|
||||
}
|
||||
export interface AccordionChildProps {
|
||||
openIndexes: number[];
|
||||
handleClick(index: number): void;
|
||||
AccordionContent: typeof AccordionContent;
|
||||
}
|
||||
|
||||
type AccordionContentProps = {
|
||||
isOpen: boolean;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const AccordionContent = ({
|
||||
isOpen,
|
||||
children,
|
||||
}: AccordionContentProps) => {
|
||||
return <AnimateHeight height={isOpen ? 'auto' : 0}>{children}</AnimateHeight>;
|
||||
};
|
||||
|
||||
const Accordion = ({
|
||||
single,
|
||||
atLeastOne,
|
||||
initialOpenIndexes,
|
||||
children,
|
||||
}: AccordionProps) => {
|
||||
const initialState = initialOpenIndexes || (atLeastOne && [0]) || [];
|
||||
const [openIndexes, setOpenIndexes] = useState<number[]>(initialState);
|
||||
|
||||
const close = (index: number) => {
|
||||
const openCount = openIndexes.length;
|
||||
const newListOfIndexes =
|
||||
atLeastOne && openCount === 1 && openIndexes.includes(index)
|
||||
? openIndexes
|
||||
: openIndexes.filter((i) => i !== index);
|
||||
|
||||
setOpenIndexes(newListOfIndexes);
|
||||
};
|
||||
|
||||
const open = (index: number) => {
|
||||
const newListOfIndexes = single ? [index] : [...openIndexes, index];
|
||||
setOpenIndexes(newListOfIndexes);
|
||||
};
|
||||
|
||||
const handleItemClick = (index: number) => {
|
||||
const action = openIndexes.includes(index) ? 'closing' : 'opening';
|
||||
|
||||
if (action === 'closing') {
|
||||
close(index);
|
||||
} else {
|
||||
open(index);
|
||||
}
|
||||
};
|
||||
|
||||
return children({
|
||||
openIndexes: openIndexes,
|
||||
handleClick: handleItemClick,
|
||||
AccordionContent,
|
||||
});
|
||||
};
|
||||
|
||||
export default Accordion;
|
||||
@@ -76,85 +76,84 @@ const LocalLogin = ({ onError }: LocalLoginProps) => {
|
||||
>
|
||||
{({ errors, touched, isSubmitting, isValid }) => {
|
||||
return (
|
||||
<>
|
||||
<Form>
|
||||
<div>
|
||||
<label htmlFor="email" className="text-label">
|
||||
{intl.formatMessage(messages.email)}
|
||||
</label>
|
||||
<div className="mt-1 mb-2 sm:col-span-2 sm:mt-0">
|
||||
<div className="form-input-field">
|
||||
<Field
|
||||
id="email"
|
||||
name="email"
|
||||
type="text"
|
||||
inputMode="email"
|
||||
data-testid="email"
|
||||
/>
|
||||
</div>
|
||||
{errors.email &&
|
||||
touched.email &&
|
||||
typeof errors.email === 'string' && (
|
||||
<div className="error">{errors.email}</div>
|
||||
)}
|
||||
<Form>
|
||||
<div>
|
||||
<label htmlFor="email" className="text-label">
|
||||
{intl.formatMessage(messages.email)}
|
||||
</label>
|
||||
<div className="mt-1 mb-2 sm:col-span-2 sm:mt-0">
|
||||
<div className="form-input-field">
|
||||
<Field
|
||||
id="email"
|
||||
name="email"
|
||||
type="text"
|
||||
inputMode="email"
|
||||
data-testid="email"
|
||||
/>
|
||||
</div>
|
||||
<label htmlFor="password" className="text-label">
|
||||
{intl.formatMessage(messages.password)}
|
||||
</label>
|
||||
<div className="mt-1 mb-2 sm:col-span-2 sm:mt-0">
|
||||
<div className="form-input-field">
|
||||
<SensitiveInput
|
||||
as="field"
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
data-testid="password"
|
||||
data-1pignore="false"
|
||||
data-lpignore="false"
|
||||
data-bwignore="false"
|
||||
/>
|
||||
</div>
|
||||
{errors.password &&
|
||||
touched.password &&
|
||||
typeof errors.password === 'string' && (
|
||||
<div className="error">{errors.password}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-8 border-t border-gray-700 pt-5">
|
||||
<div className="flex flex-row-reverse justify-between">
|
||||
<span className="inline-flex rounded-md shadow-sm">
|
||||
<Button
|
||||
buttonType="primary"
|
||||
type="submit"
|
||||
disabled={isSubmitting || !isValid}
|
||||
data-testid="local-signin-button"
|
||||
>
|
||||
<ArrowLeftOnRectangleIcon />
|
||||
<span>
|
||||
{isSubmitting
|
||||
? intl.formatMessage(messages.signingin)
|
||||
: intl.formatMessage(messages.signin)}
|
||||
</span>
|
||||
</Button>
|
||||
</span>
|
||||
{passwordResetEnabled && (
|
||||
<span className="inline-flex rounded-md shadow-sm">
|
||||
<Link href="/resetpassword" passHref>
|
||||
<Button as="a" buttonType="ghost">
|
||||
<LifebuoyIcon />
|
||||
<span>
|
||||
{intl.formatMessage(messages.forgotpassword)}
|
||||
</span>
|
||||
</Button>
|
||||
</Link>
|
||||
</span>
|
||||
{errors.email &&
|
||||
touched.email &&
|
||||
typeof errors.email === 'string' && (
|
||||
<div className="error">{errors.email}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</>
|
||||
<label htmlFor="password" className="text-label">
|
||||
{intl.formatMessage(messages.password)}
|
||||
</label>
|
||||
<div className="mt-1 mb-2 sm:col-span-2 sm:mt-0">
|
||||
<div className="form-input-field">
|
||||
<SensitiveInput
|
||||
as="field"
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
data-testid="password"
|
||||
data-1pignore="false"
|
||||
data-lpignore="false"
|
||||
data-bwignore="false"
|
||||
/>
|
||||
|
||||
</div>
|
||||
{errors.password &&
|
||||
touched.password &&
|
||||
typeof errors.password === 'string' && (
|
||||
<div className="error">{errors.password}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-8 border-t border-gray-700 pt-5">
|
||||
<div className="flex flex-row-reverse justify-between">
|
||||
<span className="inline-flex rounded-md shadow-sm">
|
||||
<Button
|
||||
buttonType="primary"
|
||||
type="submit"
|
||||
disabled={isSubmitting || !isValid}
|
||||
data-testid="local-signin-button"
|
||||
>
|
||||
<LoginIcon />
|
||||
<span>
|
||||
{isSubmitting
|
||||
? intl.formatMessage(messages.signingin)
|
||||
: intl.formatMessage(messages.signin)}
|
||||
</span>
|
||||
</Button>
|
||||
</span>
|
||||
{passwordResetEnabled && (
|
||||
<span className="inline-flex rounded-md shadow-sm">
|
||||
<Link href="/resetpassword" passHref>
|
||||
<Button as="a" buttonType="ghost">
|
||||
<SupportIcon />
|
||||
<span>
|
||||
{intl.formatMessage(messages.forgotpassword)}
|
||||
</span>
|
||||
</Button>
|
||||
</Link>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
|
||||
@@ -49,7 +49,7 @@ const Login = () => {
|
||||
</div>
|
||||
<div className="relative z-50 mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div
|
||||
className="flex flex-col space-y-4 bg-gray-800 bg-opacity-50 p-4 shadow sm:rounded-lg "
|
||||
className="flex flex-col bg-gray-800 bg-opacity-50 p-4 shadow sm:rounded-lg "
|
||||
style={{ backdropFilter: 'blur(5px)' }}
|
||||
>
|
||||
<>
|
||||
@@ -79,61 +79,17 @@ const Login = () => {
|
||||
{settings.currentSettings.plexLoginEnabled && (
|
||||
<PlexLogin onError={(msg) => setError(msg)} />
|
||||
)}
|
||||
{settings.currentSettings.plexLoginEnabled &&
|
||||
settings.currentSettings.localLogin && (
|
||||
<div className="relative flex items-center py-4">
|
||||
<div className="flex-grow border-t border-gray-500"></div>
|
||||
<span className="mx-4 flex-shrink text-gray-400">or</span>
|
||||
<div className="flex-grow border-t border-gray-500"></div>
|
||||
</div>
|
||||
)}
|
||||
{settings.currentSettings.localLogin && (
|
||||
<LocalLogin onError={(msg) => setError(msg)} />
|
||||
)}
|
||||
{/* <Accordion single atLeastOne>
|
||||
{({ openIndexes, handleClick, AccordionContent }) => (
|
||||
<>
|
||||
{settings.currentSettings.plexLoginEnabled && (
|
||||
<>
|
||||
<button
|
||||
className={`w-full cursor-default bg-gray-800 bg-opacity-70 py-2 text-center text-sm font-bold text-gray-400 transition-colors duration-200 focus:outline-none sm:rounded-t-lg ${
|
||||
openIndexes.includes(0) && 'text-indigo-500'
|
||||
} ${
|
||||
settings.currentSettings.localLogin &&
|
||||
'hover:cursor-pointer hover:bg-gray-700'
|
||||
}`}
|
||||
onClick={() => handleClick(0)}
|
||||
disabled={!settings.currentSettings.localLogin}
|
||||
>
|
||||
{intl.formatMessage(messages.signinwithplex)}
|
||||
</button>
|
||||
<AccordionContent isOpen={openIndexes.includes(0)}>
|
||||
<div className="px-10 py-8">
|
||||
<PlexLoginButton
|
||||
isProcessing={isProcessing}
|
||||
onAuthToken={(authToken) => setAuthToken(authToken)}
|
||||
/>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</>
|
||||
)}
|
||||
{settings.currentSettings.localLogin && (
|
||||
<div>
|
||||
<button
|
||||
className={`w-full cursor-default bg-gray-800 bg-opacity-70 py-2 text-center text-sm font-bold text-gray-400 transition-colors duration-200 hover:cursor-pointer hover:bg-gray-700 focus:outline-none ${
|
||||
openIndexes.includes(1)
|
||||
? 'text-indigo-500'
|
||||
: 'sm:rounded-b-lg'
|
||||
}`}
|
||||
onClick={() => handleClick(1)}
|
||||
>
|
||||
{intl.formatMessage(messages.signinwithoverseerr, {
|
||||
applicationTitle:
|
||||
settings.currentSettings.applicationTitle,
|
||||
})}
|
||||
</button>
|
||||
<AccordionContent isOpen={openIndexes.includes(1)}>
|
||||
<div className="px-10 py-8">
|
||||
<LocalLogin revalidate={revalidate} />
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Accordion> */}
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user