mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[PUI] Registration (#6309)
* optimize login layout * move auth/reg up * [PUI] Registration Fixes #6282 * [PUI] Registration Fixes #6282 * fix type * style: cleaned imports
This commit is contained in:
parent
2fbb8c757f
commit
d64fbfc254
@ -4,7 +4,6 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Group,
|
Group,
|
||||||
Loader,
|
Loader,
|
||||||
Paper,
|
|
||||||
PasswordInput,
|
PasswordInput,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
@ -17,7 +16,10 @@ import { IconCheck } from '@tabler/icons-react';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { api } from '../../App';
|
||||||
|
import { ApiPaths } from '../../enums/ApiEndpoints';
|
||||||
import { doClassicLogin, doSimpleLogin } from '../../functions/auth';
|
import { doClassicLogin, doSimpleLogin } from '../../functions/auth';
|
||||||
|
import { apiUrl } from '../../states/ApiState';
|
||||||
|
|
||||||
export function AuthenticationForm() {
|
export function AuthenticationForm() {
|
||||||
const classicForm = useForm({
|
const classicForm = useForm({
|
||||||
@ -79,50 +81,178 @@ export function AuthenticationForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper radius="md" p="xl" withBorder>
|
<form onSubmit={classicForm.onSubmit(() => {})}>
|
||||||
<Text size="lg" weight={500}>
|
{classicLoginMode ? (
|
||||||
<Trans>Welcome, log in below</Trans>
|
<Stack spacing={0}>
|
||||||
</Text>
|
<TextInput
|
||||||
<form onSubmit={classicForm.onSubmit(() => {})}>
|
required
|
||||||
{classicLoginMode ? (
|
label={t`Username`}
|
||||||
<Stack>
|
placeholder={t`Your username`}
|
||||||
<TextInput
|
{...classicForm.getInputProps('username')}
|
||||||
required
|
/>
|
||||||
label={t`Username`}
|
<PasswordInput
|
||||||
placeholder={t`Your username`}
|
required
|
||||||
{...classicForm.getInputProps('username')}
|
label={t`Password`}
|
||||||
/>
|
placeholder={t`Your password`}
|
||||||
<PasswordInput
|
{...classicForm.getInputProps('password')}
|
||||||
required
|
/>
|
||||||
label={t`Password`}
|
<Group position="apart" mt="0">
|
||||||
placeholder={t`Your password`}
|
<Anchor
|
||||||
{...classicForm.getInputProps('password')}
|
component="button"
|
||||||
/>
|
type="button"
|
||||||
<Group position="apart" mt="0">
|
color="dimmed"
|
||||||
<Anchor
|
size="xs"
|
||||||
component="button"
|
onClick={() => navigate('/reset-password')}
|
||||||
type="button"
|
>
|
||||||
color="dimmed"
|
<Trans>Reset password</Trans>
|
||||||
size="xs"
|
</Anchor>
|
||||||
onClick={() => navigate('/reset-password')}
|
</Group>
|
||||||
>
|
</Stack>
|
||||||
<Trans>Reset password</Trans>
|
) : (
|
||||||
</Anchor>
|
<Stack>
|
||||||
</Group>
|
<TextInput
|
||||||
</Stack>
|
required
|
||||||
) : (
|
label={t`Email`}
|
||||||
<Stack>
|
description={t`We will send you a link to login - if you are registered`}
|
||||||
<TextInput
|
placeholder="email@example.org"
|
||||||
required
|
{...simpleForm.getInputProps('email')}
|
||||||
label={t`Email`}
|
/>
|
||||||
description={t`We will send you a link to login - if you are registered`}
|
</Stack>
|
||||||
placeholder="email@example.org"
|
)}
|
||||||
{...simpleForm.getInputProps('email')}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Group position="apart" mt="xl">
|
<Group position="apart" mt="xl">
|
||||||
|
<Anchor
|
||||||
|
component="button"
|
||||||
|
type="button"
|
||||||
|
color="dimmed"
|
||||||
|
size="xs"
|
||||||
|
onClick={() => setMode.toggle()}
|
||||||
|
>
|
||||||
|
{classicLoginMode ? (
|
||||||
|
<Trans>Send me an email</Trans>
|
||||||
|
) : (
|
||||||
|
<Trans>Use username and password</Trans>
|
||||||
|
)}
|
||||||
|
</Anchor>
|
||||||
|
<Button type="submit" disabled={isLoggingIn} onClick={handleLogin}>
|
||||||
|
{isLoggingIn ? (
|
||||||
|
<Loader size="sm" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{classicLoginMode ? (
|
||||||
|
<Trans>Log In</Trans>
|
||||||
|
) : (
|
||||||
|
<Trans>Send Email</Trans>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RegistrationForm() {
|
||||||
|
const registrationForm = useForm({
|
||||||
|
initialValues: { username: '', email: '', password1: '', password2: '' }
|
||||||
|
});
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [isRegistering, setIsRegistering] = useState<boolean>(false);
|
||||||
|
|
||||||
|
function handleRegistration() {
|
||||||
|
setIsRegistering(true);
|
||||||
|
api
|
||||||
|
.post(apiUrl(ApiPaths.user_register), registrationForm.values, {
|
||||||
|
headers: { Authorization: '' }
|
||||||
|
})
|
||||||
|
.then((ret) => {
|
||||||
|
if (ret?.status === 204) {
|
||||||
|
setIsRegistering(false);
|
||||||
|
notifications.show({
|
||||||
|
title: t`Registration successful`,
|
||||||
|
message: t`Please confirm your email address to complete the registration`,
|
||||||
|
color: 'green',
|
||||||
|
icon: <IconCheck size="1rem" />
|
||||||
|
});
|
||||||
|
navigate('/home');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err.response.status === 400) {
|
||||||
|
setIsRegistering(false);
|
||||||
|
for (const [key, value] of Object.entries(err.response.data)) {
|
||||||
|
registrationForm.setFieldError(key, value as string);
|
||||||
|
}
|
||||||
|
let err_msg = '';
|
||||||
|
if (err.response?.data?.non_field_errors) {
|
||||||
|
err_msg = err.response.data.non_field_errors;
|
||||||
|
}
|
||||||
|
notifications.show({
|
||||||
|
title: t`Input error`,
|
||||||
|
message: t`Check your input and try again. ` + err_msg,
|
||||||
|
color: 'red',
|
||||||
|
autoClose: 30000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={registrationForm.onSubmit(() => {})}>
|
||||||
|
<Stack spacing={0}>
|
||||||
|
<TextInput
|
||||||
|
required
|
||||||
|
label={t`Username`}
|
||||||
|
placeholder={t`Your username`}
|
||||||
|
{...registrationForm.getInputProps('username')}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
required
|
||||||
|
label={t`Email`}
|
||||||
|
description={t`This will be used for a confirmation`}
|
||||||
|
placeholder="email@example.org"
|
||||||
|
{...registrationForm.getInputProps('email')}
|
||||||
|
/>
|
||||||
|
<PasswordInput
|
||||||
|
required
|
||||||
|
label={t`Password`}
|
||||||
|
placeholder={t`Your password`}
|
||||||
|
{...registrationForm.getInputProps('password1')}
|
||||||
|
/>
|
||||||
|
<PasswordInput
|
||||||
|
required
|
||||||
|
label={t`Password repeat`}
|
||||||
|
placeholder={t`Repeat password`}
|
||||||
|
{...registrationForm.getInputProps('password2')}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Group position="apart" mt="xl">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
disabled={isRegistering}
|
||||||
|
onClick={handleRegistration}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
<Trans>Register</Trans>
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ModeSelector({
|
||||||
|
loginMode,
|
||||||
|
setMode
|
||||||
|
}: {
|
||||||
|
loginMode: boolean;
|
||||||
|
setMode: any;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Text ta="center" size={'xs'} mt={'md'}>
|
||||||
|
{loginMode ? (
|
||||||
|
<>
|
||||||
|
<Trans>Don't have an account?</Trans>{' '}
|
||||||
<Anchor
|
<Anchor
|
||||||
component="button"
|
component="button"
|
||||||
type="button"
|
type="button"
|
||||||
@ -130,27 +260,20 @@ export function AuthenticationForm() {
|
|||||||
size="xs"
|
size="xs"
|
||||||
onClick={() => setMode.toggle()}
|
onClick={() => setMode.toggle()}
|
||||||
>
|
>
|
||||||
{classicLoginMode ? (
|
<Trans>Register</Trans>
|
||||||
<Trans>Send me an email</Trans>
|
|
||||||
) : (
|
|
||||||
<Trans>I will use username and password</Trans>
|
|
||||||
)}
|
|
||||||
</Anchor>
|
</Anchor>
|
||||||
<Button type="submit" disabled={isLoggingIn} onClick={handleLogin}>
|
</>
|
||||||
{isLoggingIn ? (
|
) : (
|
||||||
<Loader size="sm" />
|
<Anchor
|
||||||
) : (
|
component="button"
|
||||||
<>
|
type="button"
|
||||||
{classicLoginMode ? (
|
color="dimmed"
|
||||||
<Trans>Log In</Trans>
|
size="xs"
|
||||||
) : (
|
onClick={() => setMode.toggle()}
|
||||||
<Trans>Send Email</Trans>
|
>
|
||||||
)}
|
<Trans>Go back to login</Trans>
|
||||||
</>
|
</Anchor>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Text>
|
||||||
</Group>
|
|
||||||
</form>
|
|
||||||
</Paper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ export enum ApiPaths {
|
|||||||
user_email_primary = 'api-user-email-primary',
|
user_email_primary = 'api-user-email-primary',
|
||||||
user_email_remove = 'api-user-email-remove',
|
user_email_remove = 'api-user-email-remove',
|
||||||
user_logout = 'api-user-logout',
|
user_logout = 'api-user-logout',
|
||||||
|
user_register = 'api-user-register',
|
||||||
|
|
||||||
user_list = 'api-user-list',
|
user_list = 'api-user-list',
|
||||||
group_list = 'api-group-list',
|
group_list = 'api-group-list',
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { Trans, t } from '@lingui/macro';
|
||||||
import { Center, Container } from '@mantine/core';
|
import { Center, Container, Paper, Text } from '@mantine/core';
|
||||||
import { useToggle } from '@mantine/hooks';
|
import { useDisclosure, useToggle } from '@mantine/hooks';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { setApiDefaults } from '../../App';
|
import { setApiDefaults } from '../../App';
|
||||||
import { AuthFormOptions } from '../../components/forms/AuthFormOptions';
|
import { AuthFormOptions } from '../../components/forms/AuthFormOptions';
|
||||||
import { AuthenticationForm } from '../../components/forms/AuthenticationForm';
|
import {
|
||||||
|
AuthenticationForm,
|
||||||
|
ModeSelector,
|
||||||
|
RegistrationForm
|
||||||
|
} from '../../components/forms/AuthenticationForm';
|
||||||
import { InstanceOptions } from '../../components/forms/InstanceOptions';
|
import { InstanceOptions } from '../../components/forms/InstanceOptions';
|
||||||
import { defaultHostKey } from '../../defaults/defaultHostList';
|
import { defaultHostKey } from '../../defaults/defaultHostList';
|
||||||
import { checkLoginState } from '../../functions/auth';
|
import { checkLoginState } from '../../functions/auth';
|
||||||
@ -26,6 +30,7 @@ export default function Login() {
|
|||||||
const hostname =
|
const hostname =
|
||||||
hostList[hostKey] === undefined ? t`No selection` : hostList[hostKey]?.name;
|
hostList[hostKey] === undefined ? t`No selection` : hostList[hostKey]?.name;
|
||||||
const [hostEdit, setHostEdit] = useToggle([false, true] as const);
|
const [hostEdit, setHostEdit] = useToggle([false, true] as const);
|
||||||
|
const [loginMode, setMode] = useDisclosure(true);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
// Data manipulation functions
|
// Data manipulation functions
|
||||||
@ -63,7 +68,13 @@ export default function Login() {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<AuthenticationForm />
|
<Paper radius="md" p="xl" withBorder>
|
||||||
|
<Text size="lg" weight={500}>
|
||||||
|
<Trans>Welcome, log in below</Trans>
|
||||||
|
</Text>
|
||||||
|
{loginMode ? <AuthenticationForm /> : <RegistrationForm />}
|
||||||
|
<ModeSelector loginMode={loginMode} setMode={setMode} />
|
||||||
|
</Paper>
|
||||||
<AuthFormOptions hostname={hostname} toggleHostEdit={setHostEdit} />
|
<AuthFormOptions hostname={hostname} toggleHostEdit={setHostEdit} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -97,6 +97,8 @@ export function apiEndpoint(path: ApiPaths): string {
|
|||||||
return 'auth/emails/:id/primary/';
|
return 'auth/emails/:id/primary/';
|
||||||
case ApiPaths.user_logout:
|
case ApiPaths.user_logout:
|
||||||
return 'auth/logout/';
|
return 'auth/logout/';
|
||||||
|
case ApiPaths.user_register:
|
||||||
|
return 'auth/registration/';
|
||||||
case ApiPaths.currency_list:
|
case ApiPaths.currency_list:
|
||||||
return 'currency/exchange/';
|
return 'currency/exchange/';
|
||||||
case ApiPaths.currency_refresh:
|
case ApiPaths.currency_refresh:
|
||||||
|
Loading…
Reference in New Issue
Block a user