Split up topbar
This commit is contained in:
5
ui/src/app/profile/page.tsx
Normal file
5
ui/src/app/profile/page.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
260
ui/src/components/Topbar/LoginModal.tsx
Normal file
260
ui/src/components/Topbar/LoginModal.tsx
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { login, register, refreshLoggedIn } from '@/api/auth';
|
||||||
|
import { User } from '@/api/auth.types';
|
||||||
|
import {
|
||||||
|
Modal,
|
||||||
|
Container,
|
||||||
|
Title,
|
||||||
|
Anchor,
|
||||||
|
Paper,
|
||||||
|
TextInput,
|
||||||
|
Button,
|
||||||
|
PasswordInput,
|
||||||
|
Group,
|
||||||
|
Checkbox,
|
||||||
|
Text
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { useForm } from '@mantine/form';
|
||||||
|
import { notifications } from '@mantine/notifications';
|
||||||
|
|
||||||
|
interface LoginModalProps {
|
||||||
|
type?: string;
|
||||||
|
toggle: any;
|
||||||
|
setUser: (user: User) => void;
|
||||||
|
setRefreshId: (id: NodeJS.Timeout) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LoginModal({ type, toggle, setUser, setRefreshId }: LoginModalProps) {
|
||||||
|
function passwordValidator(value: string) {
|
||||||
|
if (value.trim().length < 10) {
|
||||||
|
return 'Password must be at least 10 characters';
|
||||||
|
}
|
||||||
|
if (value.trim().length >= 128) {
|
||||||
|
return 'Password must be at most 128 characters';
|
||||||
|
}
|
||||||
|
if (!/(\d)/.test(value)) {
|
||||||
|
return 'Password must contain at least one number';
|
||||||
|
}
|
||||||
|
if (!/[a-z]/.test(value)) {
|
||||||
|
return 'Password must contain at least one lowercase letter';
|
||||||
|
}
|
||||||
|
if (!/[A-Z]/.test(value)) {
|
||||||
|
return 'Password must contain at least one uppercase letter';
|
||||||
|
}
|
||||||
|
if (!/[!@#$%^&*]/.test(value)) {
|
||||||
|
return 'Password must contain at least one special character';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function emailValidator(value: string) {
|
||||||
|
if (value.trim().length == 0) {
|
||||||
|
return 'Email is required';
|
||||||
|
}
|
||||||
|
if (!/^\S+@\S+$/.test(value)) {
|
||||||
|
return 'Invalid email';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const registerForm = useForm({
|
||||||
|
initialValues: {
|
||||||
|
firstName: '',
|
||||||
|
lastName: '',
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
},
|
||||||
|
validate: {
|
||||||
|
firstName: (value) => (value.trim().length > 0 ? null : 'First name is required'),
|
||||||
|
lastName: (value) => (value.trim().length > 0 ? null : 'Last name is required'),
|
||||||
|
email: emailValidator,
|
||||||
|
password: passwordValidator
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const loginForm = useForm({
|
||||||
|
initialValues: {
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
remember: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const resetForm = useForm({
|
||||||
|
initialValues: {
|
||||||
|
email: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onClose() {
|
||||||
|
toggle(undefined);
|
||||||
|
registerForm.reset();
|
||||||
|
resetForm.reset();
|
||||||
|
if (!loginForm.values.remember) {
|
||||||
|
loginForm.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal opened={type !== undefined} onClose={onClose} withCloseButton={false}>
|
||||||
|
{type == 'reset' ? (
|
||||||
|
<Container>
|
||||||
|
<Title ta='center'>Reset password</Title>
|
||||||
|
<Text c='dimmed' size='sm' ta='center' mt={5}>
|
||||||
|
Enter your email and we will send you a link to reset your password.{' '}
|
||||||
|
<Anchor size='sm' component='a' onClick={() => toggle('login')}>
|
||||||
|
Go Back
|
||||||
|
</Anchor>
|
||||||
|
</Text>
|
||||||
|
<Paper withBorder shadow='md' p={30} mt={30} radius='md'>
|
||||||
|
<form onSubmit={resetForm.onSubmit(async (values) => console.log(values))}>
|
||||||
|
<TextInput label='Email' placeholder='you@example.com' required {...resetForm.getInputProps('email')} />
|
||||||
|
<Button type='submit' fullWidth mt='xl'>
|
||||||
|
Reset password
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Paper>
|
||||||
|
</Container>
|
||||||
|
) : type == 'register' ? (
|
||||||
|
<Container>
|
||||||
|
<Title ta='center'>Create account</Title>
|
||||||
|
<Text c='dimmed' size='sm' ta='center' mt={5}>
|
||||||
|
Already have an account?{' '}
|
||||||
|
<Anchor size='sm' component='a' onClick={() => toggle('login')}>
|
||||||
|
Sign in
|
||||||
|
</Anchor>
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Paper withBorder shadow='md' p={30} mt={30} radius='md'>
|
||||||
|
<form
|
||||||
|
onSubmit={registerForm.onSubmit(async (values) => {
|
||||||
|
const id = notifications.show({
|
||||||
|
loading: true,
|
||||||
|
title: `Creating account`,
|
||||||
|
message: `Please wait...`,
|
||||||
|
autoClose: false,
|
||||||
|
withCloseButton: false
|
||||||
|
});
|
||||||
|
const registerResponse = await register({
|
||||||
|
first_name: values.firstName,
|
||||||
|
last_name: values.lastName,
|
||||||
|
email: values.email,
|
||||||
|
password: values.password
|
||||||
|
});
|
||||||
|
if (registerResponse) {
|
||||||
|
const loginResponse = await login(values.email, values.password);
|
||||||
|
if (loginResponse) {
|
||||||
|
setUser(loginResponse.user);
|
||||||
|
setRefreshId(refreshLoggedIn());
|
||||||
|
onClose();
|
||||||
|
notifications.update({
|
||||||
|
id,
|
||||||
|
title: `Account created`,
|
||||||
|
message: `Welcome ${loginResponse.user.first_name}!`,
|
||||||
|
color: 'green',
|
||||||
|
autoClose: 2000,
|
||||||
|
loading: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notifications.update({
|
||||||
|
id,
|
||||||
|
title: `Unable to Login`,
|
||||||
|
message: `Please try again.`,
|
||||||
|
color: 'red',
|
||||||
|
autoClose: 2000,
|
||||||
|
loading: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
notifications.update({
|
||||||
|
id,
|
||||||
|
title: `Unable to Register`,
|
||||||
|
message: `Please try again.`,
|
||||||
|
color: 'error',
|
||||||
|
autoClose: 2000,
|
||||||
|
loading: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<TextInput label='First name' placeholder='John' required {...registerForm.getInputProps('firstName')} />
|
||||||
|
<TextInput
|
||||||
|
label='Last name'
|
||||||
|
placeholder='Smith'
|
||||||
|
required
|
||||||
|
mt='md'
|
||||||
|
{...registerForm.getInputProps('lastName')}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label='Email'
|
||||||
|
placeholder='you@example.com'
|
||||||
|
required
|
||||||
|
{...registerForm.getInputProps('email')}
|
||||||
|
/>
|
||||||
|
<PasswordInput
|
||||||
|
label='Password'
|
||||||
|
description='Passwords must be at least 10 characters long, contain at least one number, one uppercase letter, one lowercase letter, and one special character.'
|
||||||
|
placeholder='Your password'
|
||||||
|
required
|
||||||
|
mt='md'
|
||||||
|
{...registerForm.getInputProps('password')}
|
||||||
|
/>
|
||||||
|
<Button type='submit' fullWidth mt='xl'>
|
||||||
|
Sign up
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Paper>
|
||||||
|
</Container>
|
||||||
|
) : (
|
||||||
|
<Container>
|
||||||
|
<Title ta='center'>Welcome back!</Title>
|
||||||
|
<Text c='dimmed' size='sm' ta='center' mt={5}>
|
||||||
|
Do not have an account yet?{' '}
|
||||||
|
<Anchor size='sm' component='a' onClick={() => toggle('register')}>
|
||||||
|
Create account
|
||||||
|
</Anchor>
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Paper withBorder shadow='md' p={30} mt={30} radius='md'>
|
||||||
|
<form
|
||||||
|
onSubmit={loginForm.onSubmit(async (values) => {
|
||||||
|
const response = await login(values.email, values.password);
|
||||||
|
if (response) {
|
||||||
|
setUser(response.user);
|
||||||
|
setRefreshId(refreshLoggedIn());
|
||||||
|
onClose();
|
||||||
|
} else {
|
||||||
|
notifications.show({
|
||||||
|
title: `Unable to Login`,
|
||||||
|
message: `Please try again.`,
|
||||||
|
color: 'red',
|
||||||
|
autoClose: 2000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<TextInput label='Email' placeholder='you@example.com' required {...loginForm.getInputProps('email')} />
|
||||||
|
<PasswordInput
|
||||||
|
label='Password'
|
||||||
|
placeholder='Your password'
|
||||||
|
required
|
||||||
|
mt='md'
|
||||||
|
{...loginForm.getInputProps('password')}
|
||||||
|
/>
|
||||||
|
<Group justify='space-between' mt='lg'>
|
||||||
|
<Checkbox label='Remember me' {...loginForm.getInputProps('remember')} />
|
||||||
|
<Anchor component='a' size='sm' onClick={() => toggle('reset')}>
|
||||||
|
Forgot password?
|
||||||
|
</Anchor>
|
||||||
|
</Group>
|
||||||
|
<Button type='submit' fullWidth mt='xl'>
|
||||||
|
Sign in
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Paper>
|
||||||
|
</Container>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
ui/src/components/Topbar/headerItems.ts
Normal file
41
ui/src/components/Topbar/headerItems.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
export interface HeaderItem {
|
||||||
|
name: string;
|
||||||
|
link: string;
|
||||||
|
role?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const headerItems: HeaderItem[] = [
|
||||||
|
{
|
||||||
|
name: 'Races',
|
||||||
|
link: '/races'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Classes',
|
||||||
|
link: '/classes'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Feats',
|
||||||
|
link: '/feats'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Options & Features',
|
||||||
|
link: '/options'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Backgrounds',
|
||||||
|
link: '/backgrounds'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Items',
|
||||||
|
link: '/items'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Spells',
|
||||||
|
link: '/spells'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Management',
|
||||||
|
link: '/management',
|
||||||
|
role: 'admin'
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -3,73 +3,14 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import './topbar.css';
|
import './topbar.css';
|
||||||
import {
|
import { Avatar, Button, Card, Grid, Group, Menu, Text, UnstyledButton } from '@mantine/core';
|
||||||
Anchor,
|
|
||||||
Avatar,
|
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
Checkbox,
|
|
||||||
Container,
|
|
||||||
Grid,
|
|
||||||
Group,
|
|
||||||
Menu,
|
|
||||||
Modal,
|
|
||||||
Paper,
|
|
||||||
PasswordInput,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
Title,
|
|
||||||
UnstyledButton
|
|
||||||
} from '@mantine/core';
|
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useForm } from '@mantine/form';
|
import { logout, me, refreshLoggedIn } from '@/api/auth';
|
||||||
import { login, register, logout, me, refreshLoggedIn } from '@/api/auth';
|
|
||||||
import { User } from '@/api/auth.types';
|
import { User } from '@/api/auth.types';
|
||||||
import { useToggle } from '@mantine/hooks';
|
import { useToggle } from '@mantine/hooks';
|
||||||
import { notifications } from '@mantine/notifications';
|
import { LoginModal } from './LoginModal';
|
||||||
|
import { HeaderItem, headerItems } from './headerItems';
|
||||||
interface HeaderItem {
|
|
||||||
name: string;
|
|
||||||
link: string;
|
|
||||||
role?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const headerItems: HeaderItem[] = [
|
|
||||||
{
|
|
||||||
name: 'Races',
|
|
||||||
link: '/races'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Classes',
|
|
||||||
link: '/classes'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Feats',
|
|
||||||
link: '/feats'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Options & Features',
|
|
||||||
link: '/options'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Backgrounds',
|
|
||||||
link: '/backgrounds'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Items',
|
|
||||||
link: '/items'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Spells',
|
|
||||||
link: '/spells'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Management',
|
|
||||||
link: '/management',
|
|
||||||
role: 'admin'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function Topbar() {
|
export default function Topbar() {
|
||||||
const pathName = usePathname();
|
const pathName = usePathname();
|
||||||
@@ -145,17 +86,11 @@ export default function Topbar() {
|
|||||||
</Text>
|
</Text>
|
||||||
<Grid mt='xl'>
|
<Grid mt='xl'>
|
||||||
<Grid.Col span={6}>
|
<Grid.Col span={6}>
|
||||||
<Button
|
<Link href='/profile'>
|
||||||
fullWidth
|
<Button fullWidth radius='md' size='xs' variant='default'>
|
||||||
radius='md'
|
|
||||||
size='xs'
|
|
||||||
variant='default'
|
|
||||||
onClick={() => {
|
|
||||||
toggle(undefined);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Profile
|
Profile
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={6}>
|
<Grid.Col span={6}>
|
||||||
<Button
|
<Button
|
||||||
@@ -195,244 +130,3 @@ export default function Topbar() {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoginModalProps {
|
|
||||||
type?: string;
|
|
||||||
toggle: any;
|
|
||||||
setUser: (user: User) => void;
|
|
||||||
setRefreshId: (id: NodeJS.Timeout) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function LoginModal({ type, toggle, setUser, setRefreshId }: LoginModalProps) {
|
|
||||||
function passwordValidator(value: string) {
|
|
||||||
if (value.trim().length < 10) {
|
|
||||||
return 'Password must be at least 10 characters';
|
|
||||||
}
|
|
||||||
if (value.trim().length >= 128) {
|
|
||||||
return 'Password must be at most 128 characters';
|
|
||||||
}
|
|
||||||
if (!/(\d)/.test(value)) {
|
|
||||||
return 'Password must contain at least one number';
|
|
||||||
}
|
|
||||||
if (!/[a-z]/.test(value)) {
|
|
||||||
return 'Password must contain at least one lowercase letter';
|
|
||||||
}
|
|
||||||
if (!/[A-Z]/.test(value)) {
|
|
||||||
return 'Password must contain at least one uppercase letter';
|
|
||||||
}
|
|
||||||
if (!/[!@#$%^&*]/.test(value)) {
|
|
||||||
return 'Password must contain at least one special character';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function emailValidator(value: string) {
|
|
||||||
if (value.trim().length == 0) {
|
|
||||||
return 'Email is required';
|
|
||||||
}
|
|
||||||
if (!/^\S+@\S+$/.test(value)) {
|
|
||||||
return 'Invalid email';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const registerForm = useForm({
|
|
||||||
initialValues: {
|
|
||||||
firstName: '',
|
|
||||||
lastName: '',
|
|
||||||
email: '',
|
|
||||||
password: ''
|
|
||||||
},
|
|
||||||
validate: {
|
|
||||||
firstName: (value) => (value.trim().length > 0 ? null : 'First name is required'),
|
|
||||||
lastName: (value) => (value.trim().length > 0 ? null : 'Last name is required'),
|
|
||||||
email: emailValidator,
|
|
||||||
password: passwordValidator
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const loginForm = useForm({
|
|
||||||
initialValues: {
|
|
||||||
email: '',
|
|
||||||
password: '',
|
|
||||||
remember: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const resetForm = useForm({
|
|
||||||
initialValues: {
|
|
||||||
email: ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function onClose() {
|
|
||||||
toggle(undefined);
|
|
||||||
registerForm.reset();
|
|
||||||
resetForm.reset();
|
|
||||||
if (!loginForm.values.remember) {
|
|
||||||
loginForm.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal opened={type !== undefined} onClose={onClose} withCloseButton={false}>
|
|
||||||
{type == 'reset' ? (
|
|
||||||
<Container>
|
|
||||||
<Title ta='center'>Reset password</Title>
|
|
||||||
<Text c='dimmed' size='sm' ta='center' mt={5}>
|
|
||||||
Enter your email and we will send you a link to reset your password.{' '}
|
|
||||||
<Anchor size='sm' component='a' onClick={() => toggle('login')}>
|
|
||||||
Go Back
|
|
||||||
</Anchor>
|
|
||||||
</Text>
|
|
||||||
<Paper withBorder shadow='md' p={30} mt={30} radius='md'>
|
|
||||||
<form onSubmit={resetForm.onSubmit(async (values) => console.log(values))}>
|
|
||||||
<TextInput label='Email' placeholder='you@example.com' required {...resetForm.getInputProps('email')} />
|
|
||||||
<Button type='submit' fullWidth mt='xl'>
|
|
||||||
Reset password
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</Paper>
|
|
||||||
</Container>
|
|
||||||
) : type == 'register' ? (
|
|
||||||
<Container>
|
|
||||||
<Title ta='center'>Create account</Title>
|
|
||||||
<Text c='dimmed' size='sm' ta='center' mt={5}>
|
|
||||||
Already have an account?{' '}
|
|
||||||
<Anchor size='sm' component='a' onClick={() => toggle('login')}>
|
|
||||||
Sign in
|
|
||||||
</Anchor>
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<Paper withBorder shadow='md' p={30} mt={30} radius='md'>
|
|
||||||
<form
|
|
||||||
onSubmit={registerForm.onSubmit(async (values) => {
|
|
||||||
const id = notifications.show({
|
|
||||||
loading: true,
|
|
||||||
title: `Creating account`,
|
|
||||||
message: `Please wait...`,
|
|
||||||
autoClose: false,
|
|
||||||
withCloseButton: false
|
|
||||||
});
|
|
||||||
const registerResponse = await register({
|
|
||||||
first_name: values.firstName,
|
|
||||||
last_name: values.lastName,
|
|
||||||
email: values.email,
|
|
||||||
password: values.password
|
|
||||||
});
|
|
||||||
if (registerResponse) {
|
|
||||||
const loginResponse = await login(values.email, values.password);
|
|
||||||
if (loginResponse) {
|
|
||||||
setUser(loginResponse.user);
|
|
||||||
setRefreshId(refreshLoggedIn());
|
|
||||||
onClose();
|
|
||||||
notifications.update({
|
|
||||||
id,
|
|
||||||
title: `Account created`,
|
|
||||||
message: `Welcome ${loginResponse.user.first_name}!`,
|
|
||||||
color: 'green',
|
|
||||||
autoClose: 2000,
|
|
||||||
loading: false
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
notifications.update({
|
|
||||||
id,
|
|
||||||
title: `Unable to Login`,
|
|
||||||
message: `Please try again.`,
|
|
||||||
color: 'red',
|
|
||||||
autoClose: 2000,
|
|
||||||
loading: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
notifications.update({
|
|
||||||
id,
|
|
||||||
title: `Unable to Register`,
|
|
||||||
message: `Please try again.`,
|
|
||||||
color: 'error',
|
|
||||||
autoClose: 2000,
|
|
||||||
loading: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<TextInput label='First name' placeholder='John' required {...registerForm.getInputProps('firstName')} />
|
|
||||||
<TextInput
|
|
||||||
label='Last name'
|
|
||||||
placeholder='Smith'
|
|
||||||
required
|
|
||||||
mt='md'
|
|
||||||
{...registerForm.getInputProps('lastName')}
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label='Email'
|
|
||||||
placeholder='you@example.com'
|
|
||||||
required
|
|
||||||
{...registerForm.getInputProps('email')}
|
|
||||||
/>
|
|
||||||
<PasswordInput
|
|
||||||
label='Password'
|
|
||||||
description='Passwords must be at least 10 characters long, contain at least one number, one uppercase letter, one lowercase letter, and one special character.'
|
|
||||||
placeholder='Your password'
|
|
||||||
required
|
|
||||||
mt='md'
|
|
||||||
{...registerForm.getInputProps('password')}
|
|
||||||
/>
|
|
||||||
<Button type='submit' fullWidth mt='xl'>
|
|
||||||
Sign up
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</Paper>
|
|
||||||
</Container>
|
|
||||||
) : (
|
|
||||||
<Container>
|
|
||||||
<Title ta='center'>Welcome back!</Title>
|
|
||||||
<Text c='dimmed' size='sm' ta='center' mt={5}>
|
|
||||||
Do not have an account yet?{' '}
|
|
||||||
<Anchor size='sm' component='a' onClick={() => toggle('register')}>
|
|
||||||
Create account
|
|
||||||
</Anchor>
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<Paper withBorder shadow='md' p={30} mt={30} radius='md'>
|
|
||||||
<form
|
|
||||||
onSubmit={loginForm.onSubmit(async (values) => {
|
|
||||||
const response = await login(values.email, values.password);
|
|
||||||
if (response) {
|
|
||||||
setUser(response.user);
|
|
||||||
setRefreshId(refreshLoggedIn());
|
|
||||||
onClose();
|
|
||||||
} else {
|
|
||||||
notifications.show({
|
|
||||||
title: `Unable to Login`,
|
|
||||||
message: `Please try again.`,
|
|
||||||
color: 'red',
|
|
||||||
autoClose: 2000
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<TextInput label='Email' placeholder='you@example.com' required {...loginForm.getInputProps('email')} />
|
|
||||||
<PasswordInput
|
|
||||||
label='Password'
|
|
||||||
placeholder='Your password'
|
|
||||||
required
|
|
||||||
mt='md'
|
|
||||||
{...loginForm.getInputProps('password')}
|
|
||||||
/>
|
|
||||||
<Group justify='space-between' mt='lg'>
|
|
||||||
<Checkbox label='Remember me' {...loginForm.getInputProps('remember')} />
|
|
||||||
<Anchor component='a' size='sm' onClick={() => toggle('reset')}>
|
|
||||||
Forgot password?
|
|
||||||
</Anchor>
|
|
||||||
</Group>
|
|
||||||
<Button type='submit' fullWidth mt='xl'>
|
|
||||||
Sign in
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</Paper>
|
|
||||||
</Container>
|
|
||||||
)}
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user