feat(login / register form): Added validation and birthdate to form

This commit is contained in:
Laurian-Dufrechou
2023-04-07 22:36:39 +02:00
parent 53c61b71fc
commit 2e1f56661c
4 changed files with 284 additions and 178 deletions
+46 -21
View File
@@ -5,37 +5,42 @@ import {
ButtonGroup, ButtonGroup,
Button, Button,
Image, Image,
} from '@chakra-ui/react'; Link,
import {useRouter} from 'next/router'; } from "@chakra-ui/react";
import {signOut, useSession} from 'next-auth/react'; import { useRouter } from "next/router";
import { signOut, useSession } from "next-auth/react";
import Nextlink from "next/link";
type Props = { type Props = {
variant: "static" | "fixed" variant: "static" | "fixed";
} };
export default function Navbar({ variant = "fixed" }: Props) { export default function Navbar({ variant = "fixed" }: Props) {
const router = useRouter(); const router = useRouter();
const { data: session, status } = useSession(); const { data: session, status } = useSession();
const RightSection = () => { const RightSection = () => {
if (status === "authenticated" && session.user) return ( if (status === "authenticated" && session.user)
<Flex justify={'right'} flexBasis={'100%'}> return (
<Text>{session.user.email}</Text> <Flex justify={"right"} flexBasis={"100%"}>
<ButtonGroup> <ButtonGroup>
<Button colorScheme={'purple'} onClick={() => signOut()}> <Button colorScheme={"purple"} onClick={() => signOut()}>
Déconnexion Déconnexion
</Button> </Button>
</ButtonGroup> </ButtonGroup>
</Flex> </Flex>
); );
else return ( else
<Flex justify={'right'} flexBasis={'100%'}> return (
<Flex justify={"right"} flexBasis={"100%"}>
<ButtonGroup> <ButtonGroup>
<Button onClick={() => router.push("/register")}>
Inscription
</Button>
<Button <Button
onClick={() => router.push('/register')}>Inscription</Button> colorScheme={"purple"}
<Button colorScheme={'purple'} onClick={() => router.push("/login")}
onClick={() => router.push('/login')}> >
Connexion Connexion
</Button> </Button>
</ButtonGroup> </ButtonGroup>
@@ -44,22 +49,42 @@ export default function Navbar({variant = "fixed"}: Props) {
}; };
return ( return (
<Box position={variant} zIndex={9999} top={0} width={'100vw'} <Box
backdropFilter={'auto'} backdropBlur={'20px'} px={10} py={2}> position={variant}
<Flex align={'center'}> zIndex={9999}
<Box flexBasis={'100%'}> top={0}
width={"100vw"}
backdropFilter={"auto"}
backdropBlur={"20px"}
px={10}
py={2}
>
<Flex align={"center"}>
<Box flexBasis={"100%"}>
<Image src={"/logo.svg"} h={"3rem"} objectFit={"contain"} /> <Image src={"/logo.svg"} h={"3rem"} objectFit={"contain"} />
</Box> </Box>
<Flex gap={5} justify={'center'} flexBasis={'100%'}> <Flex gap={5} justify={"center"} flexBasis={"100%"}>
{status === "authenticated" ? (
<>
<Link href={"/dashboard"} color={"purple.500"}>
Tableau de bord
</Link>
<Link href={"/userProfile"} color={"purple.500"}>
Profile
</Link>
</>
) : (
<>
<Text>A propos</Text> <Text>A propos</Text>
<Text>Contact</Text> <Text>Contact</Text>
<Text>Aide</Text> <Text>Aide</Text>
</>
)}
</Flex> </Flex>
<RightSection /> <RightSection />
</Flex> </Flex>
</Box> </Box>
); );
} }
+77 -53
View File
@@ -1,91 +1,115 @@
import { import {
Box, Button, Box,
Button,
Flex, Flex,
FormControl, FormControl,
FormLabel, FormLabel,
Heading, Heading,
Input, Input,
Container, Container,
} from '@chakra-ui/react'; FormErrorMessage,
import {useState} from 'react'; } from "@chakra-ui/react";
import {useRouter} from 'next/router'; import { useState } from "react";
import {signIn, SignInResponse} from 'next-auth/react'; import { useRouter } from "next/router";
import { signIn, SignInResponse } from "next-auth/react";
import {LoginData} from '@/models/form/LoginData'; import { LoginData } from "@/models/form/LoginData";
import { useForm } from "react-hook-form";
export default function LoginForm() { export default function LoginForm() {
const router = useRouter(); const router = useRouter();
const [loginData, setLoginData] = useState(new LoginData());
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [invalidInput, setInvalidInput] = useState(false); const [wrongPasswordError, setWrongPasswordError] = useState(false);
const buttonWidth = {base: '100%', md: 'unset'}; const {
handleSubmit,
register,
formState: { errors, isSubmitting },
} = useForm();
const handleInput = (data: any) => { const buttonWidth = { base: "100%", md: "unset" };
setInvalidInput(false);
setLoginData({...loginData, ...data});
};
const handleSubmit = async () => { const errorPassword = "Mot de passe incorrect";
setIsLoading(true)
signIn('credentials', {...loginData, redirect: false}) const loginUser = async (value: LoginData) => {
.then((res: unknown) => { setIsLoading(true);
signIn("credentials", { ...value, redirect: false }).then(
(res: unknown) => {
const { ok: connexionSuccess } = res as SignInResponse; const { ok: connexionSuccess } = res as SignInResponse;
if (!connexionSuccess) {
setIsLoading(false); setIsLoading(false);
setInvalidInput(true);
if (connexionSuccess) router.push("/dashboard");
else setWrongPasswordError(true);
} }
else router.push('/dashboard'); );
});
}; };
return ( return (
<Box flexBasis={'100%'}> <Box flexBasis={"100%"}>
<Container> <Container>
<Heading textAlign={'center'} size={'2xl'}>Connexion</Heading> <form id="login_form" onSubmit={handleSubmit(loginUser)}>
<Heading textAlign={"center"} size={"2xl"}>
Connexion
</Heading>
{/*Email*/} {/*Email*/}
<FormControl mb={'1rem'} mt={'100px'} isInvalid={invalidInput}> <FormControl mb={"1rem"} id={"email"} isInvalid={errors.email}>
<FormLabel>Adresse email</FormLabel> <FormLabel>Adresse email</FormLabel>
<Input <Input
isInvalid={invalidInput} type="text"
id={'email'} {...register("email", {
type={'email'} required: { value: true, message: "Adresse email requise" },
value={loginData.email} pattern: {
onChange={(evt) => handleInput({email: evt.target.value})} value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
placeholder={'adresse@email.com'} message: "Adresse email invalide",
required={true} },
})}
placeholder="adresse@email.com"
/> />
<FormErrorMessage>{errors.email?.message}</FormErrorMessage>
</FormControl> </FormControl>
{/* Mot de passe */ {/*Mot de passe*/}
} <FormControl
<FormControl mb={'1rem'}> mb={"1rem"}
id="password"
isInvalid={errors.password || wrongPasswordError}
>
<FormLabel>Mot de passe</FormLabel> <FormLabel>Mot de passe</FormLabel>
<Input <Input
isInvalid={invalidInput} type="password"
id={'password'} placeholder="Mot de passe"
type={'password'} {...register("password", {
value={loginData.password} required: { value: true, message: "Mot de passe requis" },
onChange={(evt) => handleInput({password: evt.target.value})} })}
placeholder={'Mot de passe'}
required={true}
/> />
<FormErrorMessage>{errors.password?.message}</FormErrorMessage>
<FormErrorMessage>
{wrongPasswordError && errorPassword}
</FormErrorMessage>
</FormControl> </FormControl>
{/*Boutons*/ {/*Boutons*/}
} <Flex
<Flex justify={'center'} mt={'50px'} gap={5} justify={"center"}
justifyContent={'space-between'}> mt={"50px"}
<Button onClick={() => router.push('/')} gap={5}
w={buttonWidth}>Retour</Button> justifyContent={"space-between"}
<Button isLoading={isLoading} onClick={handleSubmit} >
w={buttonWidth} colorScheme="purple">Connexion</Button> <Button onClick={() => router.push("/")} w={buttonWidth}>
Retour
</Button>
<Button
isLoading={isLoading}
w={buttonWidth}
colorScheme="purple"
type="submit"
>
Connexion
</Button>
</Flex> </Flex>
</form>
</Container> </Container>
</Box> </Box>
) );
} }
+121 -64
View File
@@ -1,135 +1,192 @@
import { import {
Box, Button, Box,
Button,
Flex, Flex,
FormControl, FormControl,
FormLabel, FormLabel,
Heading, Heading,
Input, Input,
Container, Container,
} from '@chakra-ui/react'; FormErrorMessage,
import {useRouter} from 'next/router'; } from "@chakra-ui/react";
import {useState} from 'react'; import { useRouter } from "next/router";
import {signIn, SignInResponse} from 'next-auth/react'; import { useState } from "react";
import {RegisterData} from '@/models/form/RegisterData'; import { signIn, SignInResponse } from "next-auth/react";
import { RegisterData } from "@/models/form/RegisterData";
import { useForm } from "react-hook-form";
export default function RegisterForm() { export default function RegisterForm() {
const router = useRouter(); const router = useRouter();
const [registerData, setRegisterData] = useState(new RegisterData());
const [isLoading, setIsLoading] = useState(false);
const [invalidInput, setInvalidInput] = useState(false);
const buttonWidth = {base: '100%', md: 'unset'}; const {
handleSubmit,
register,
watch,
formState: { errors, isSubmitting },
} = useForm();
const handleInput = (data: any) => { const buttonWidth = { base: "100%", md: "unset" };
setInvalidInput(false);
setRegisterData({...registerData, ...data}); const validateDate = (value: string) => {
const selected = new Date(value).getFullYear();
const now = new Date().getFullYear();
return now - selected >= 18 || "Vous devez avoir 18 ans ou plus";
}; };
const handleSubmit = () => { const registerUser = (values: RegisterData) => {
let {email, firstName, lastName, password, confirmPassword} = registerData; let { email, firstName, lastName, birthdate, password, confirmPassword } =
if (password !== confirmPassword) setInvalidInput(true); values;
const date = new Date(birthdate);
const options = { const options = {
method: 'POST', method: "POST",
headers: {'Content-Type': 'application/json'}, headers: { "Content-Type": "application/json" },
body: JSON.stringify({email, firstName, lastName, password}), body: JSON.stringify({
email,
firstName,
lastName,
password,
birthdate: date,
}),
}; };
setIsLoading(true); fetch("/api/users", options)
fetch('/api/users', options).then(() => { .then(() => {
signIn('credentials', {email, password, redirect: false}) signIn("credentials", { email, password, redirect: false }).then(
.then((res: unknown) => { (res: unknown) => {
const { ok: connexionSuccess } = res as SignInResponse; const { ok: connexionSuccess } = res as SignInResponse;
// TODO If success -> goto interactive form else login // TODO If success -> goto interactive form else login
router.push(connexionSuccess ? '/' : '/login'); router.push(connexionSuccess ? "/dashboard" : "/");
}); }
}).catch(() => { );
setIsLoading(false); })
setInvalidInput(true); .catch(() => {});
setRegisterData({...registerData, password: '', confirmPassword: ''});
});
}; };
return ( return (
<Box flexBasis={'100%'}> <Box flexBasis={"100%"}>
<Container> <Container>
<Heading textAlign={'center'} size={'2xl'}>Inscription</Heading> <form id="register_form" onSubmit={handleSubmit(registerUser)}>
<Heading textAlign={"center"} size={"2xl"}>
Inscription
</Heading>
<Flex mt={'100px'} mb={'1rem'} gap={5}> <Flex mt={"100px"} mb={"1rem"} gap={5}>
{/*Prénom*/} {/*Prénom*/}
<FormControl> <FormControl id="firstName" isInvalid={errors.firstName}>
<FormLabel>Prénom</FormLabel> <FormLabel>Prénom</FormLabel>
<Input <Input
id="firstName"
type="text" type="text"
value={registerData.firstName}
onChange={(evt) => handleInput({firstName: evt.target.value})}
placeholder="Prénom" placeholder="Prénom"
required={true} {...register("firstName", {
required: { value: true, message: "Prénom requis" },
})}
/> />
<FormErrorMessage>{errors.firstName?.message}</FormErrorMessage>
</FormControl> </FormControl>
{/*Nom*/} {/*Nom*/}
<FormControl> <FormControl id="lastName" isInvalid={errors.lastName}>
<FormLabel>Nom</FormLabel> <FormLabel>Nom</FormLabel>
<Input <Input
id="lastName"
type="text" type="text"
value={registerData.lastName}
onChange={(evt) => handleInput({lastName: evt.target.value})}
placeholder="Nom" placeholder="Nom"
required={true} {...register("lastName", {
required: { value: true, message: "Nom requis" },
})}
/> />
<FormErrorMessage>{errors.lastName?.message}</FormErrorMessage>
</FormControl> </FormControl>
</Flex> </Flex>
{/*Email*/} {/*Email*/}
<FormControl mb={'1rem'} isInvalid={invalidInput}> <FormControl mb={"1rem"} id={"email"} isInvalid={errors.email}>
<FormLabel>Adresse email</FormLabel> <FormLabel>Adresse email</FormLabel>
<Input <Input
id="email" type="text"
type="email" {...register("email", {
value={registerData.email} required: { value: true, message: "Adresse email requise" },
onChange={(evt) => handleInput({email: evt.target.value})} pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "Adresse email invalide",
},
})}
placeholder="adresse@email.com" placeholder="adresse@email.com"
/> />
<FormErrorMessage>{errors.email?.message}</FormErrorMessage>
</FormControl>
{/*Date de naissance*/}
<FormControl
mb={"1rem"}
id={"birthdate"}
isInvalid={errors.birthdate}
>
<FormLabel>Date de naissance</FormLabel>
<Input
type="date"
{...register("birthdate", {
required: { value: true, message: "Date de naissance requise" },
validate: validateDate,
})}
/>
<FormErrorMessage>{errors.birthdate?.message}</FormErrorMessage>
</FormControl> </FormControl>
{/*Mot de passe*/} {/*Mot de passe*/}
<FormControl mb={'1rem'} isInvalid={invalidInput}> <FormControl mb={"1rem"} id="password" isInvalid={errors.password}>
<FormLabel>Mot de passe</FormLabel> <FormLabel>Mot de passe</FormLabel>
<Input <Input
id="password"
type="password" type="password"
value={registerData.password}
onChange={(evt) => handleInput({password: evt.target.value})}
placeholder="Mot de passe" placeholder="Mot de passe"
{...register("password", {
required: { value: true, message: "Mot de passe requis" },
})}
/> />
<FormErrorMessage>{errors.password?.message}</FormErrorMessage>
</FormControl> </FormControl>
{/*Mot de passe (2)*/} {/*Mot de passe (2)*/}
<FormControl mb={'1rem'} isInvalid={invalidInput}> <FormControl
mb={"1rem"}
id="confirmPassword"
isInvalid={errors.confirmPassword}
>
<FormLabel>Confirmation du mot de passe</FormLabel> <FormLabel>Confirmation du mot de passe</FormLabel>
<Input <Input
id="confirmPassword"
type="password" type="password"
value={registerData.confirmPassword}
onChange={(evt) => handleInput(
{confirmPassword: evt.target.value})}
placeholder="Mot de passe" placeholder="Mot de passe"
{...register("confirmPassword", {
required: { value: true, message: "Confirmation requise" },
validate: (value: string) => {
return (
watch("password") === value ||
"Les mots de passe ne correspondent pas"
);
},
})}
/> />
<FormErrorMessage>
{errors.confirmPassword?.message}
</FormErrorMessage>
</FormControl> </FormControl>
<Flex mt={"50px"} w={"100%"} justify={"space-between"} gap={5}>
<Flex mt={'50px'} w={'100%'} justify={'space-between'} gap={5}> <Button onClick={() => router.push("/")} w={buttonWidth}>
<Button onClick={() => router.push('/')} Retour
w={buttonWidth}>Retour</Button> </Button>
<Button isLoading={isLoading} onClick={handleSubmit} w={buttonWidth} colorScheme="purple">Je <Button
m&apos;inscris</Button> isLoading={isSubmitting}
w={buttonWidth}
colorScheme="purple"
type="submit"
>
Je m&apos;inscris
</Button>
</Flex> </Flex>
</form>
</Container> </Container>
</Box> </Box>
); );
} }
+2 -2
View File
@@ -1,10 +1,10 @@
export class RegisterData { export class RegisterData {
constructor( constructor(
public firstName: string = "", public firstName: string = "",
public lastName: string = "", public lastName: string = "",
public email: string = "", public email: string = "",
public birthdate: string = "",
public password: string = "", public password: string = "",
public confirmPassword: string = "", public confirmPassword: string = ""
) {} ) {}
} }