mirror of
https://github.com/LucasVbr/meeting-app.git
synced 2026-05-16 09:06:54 +00:00
Create User Login/Logout
Took 3 hours 40 minutes
This commit is contained in:
+46
-49
@@ -1,58 +1,55 @@
|
||||
import { Box, Flex, Text, ButtonGroup, Button } from "@chakra-ui/react";
|
||||
import { useRouter } from "next/router";
|
||||
import {Box, Flex, Text, ButtonGroup, Button} from '@chakra-ui/react';
|
||||
import {useRouter} from 'next/router';
|
||||
import {signOut, useSession} from 'next-auth/react';
|
||||
|
||||
export default function Navbar() {
|
||||
const router = useRouter();
|
||||
const {data: session, status} = useSession();
|
||||
|
||||
const redirect_connexion = () => {
|
||||
router.push("/login");
|
||||
const RightSection = () => {
|
||||
|
||||
if (status === "authenticated" && session.user) return (
|
||||
<Flex justify={'right'} flexBasis={'100%'}>
|
||||
<Text>{session.user.email}</Text>
|
||||
|
||||
<ButtonGroup>
|
||||
<Button colorScheme={'purple'} onClick={() => signOut()}>
|
||||
Déconnexion
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
);
|
||||
else return (
|
||||
<Flex justify={'right'} flexBasis={'100%'}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
onClick={() => router.push('/register')}>Inscription</Button>
|
||||
<Button colorScheme={'purple'}
|
||||
onClick={() => router.push('/login')}>
|
||||
Connexion
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const redirect_inscription = () => {
|
||||
router.push("/register");
|
||||
};
|
||||
|
||||
const LeftContent = () => (
|
||||
<Box flexBasis={"100%"}>
|
||||
<Text>Logo</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
const CenterContent = () => (
|
||||
<Flex gap={5} justify={"center"} flexBasis={"100%"}>
|
||||
<Text>A propos</Text>
|
||||
<Text>Contact</Text>
|
||||
<Text>Aide</Text>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
const RightContent = () => (
|
||||
<Flex justify={"right"} flexBasis={"100%"}>
|
||||
<ButtonGroup>
|
||||
<Button onClick={redirect_inscription}>Inscription</Button>
|
||||
<Button colorScheme={"purple"} onClick={redirect_connexion}>
|
||||
Connexion
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
position={"fixed"}
|
||||
zIndex={9999}
|
||||
top={0}
|
||||
width={"100vw"}
|
||||
backdropFilter={"auto"}
|
||||
backdropBlur={"20px"}
|
||||
px={10}
|
||||
py={2}
|
||||
>
|
||||
<Flex align={"center"}>
|
||||
<LeftContent />
|
||||
<CenterContent />
|
||||
<RightContent />
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box position={'fixed'} zIndex={9999} top={0} width={'100vw'}
|
||||
backdropFilter={'auto'} backdropBlur={'20px'} px={10} py={2}>
|
||||
<Flex align={'center'}>
|
||||
<Box flexBasis={'100%'}>
|
||||
<Text>Logo</Text>
|
||||
</Box>
|
||||
|
||||
<Flex gap={5} justify={'center'} flexBasis={'100%'}>
|
||||
<Text>A propos</Text>
|
||||
<Text>Contact</Text>
|
||||
<Text>Aide</Text>
|
||||
</Flex>
|
||||
|
||||
<RightSection/>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { createContext, useContext, useState } from "react";
|
||||
|
||||
// faudra récuperer les users de la base de données
|
||||
|
||||
const UsersContext = createContext(null);
|
||||
|
||||
export const UsersProvider = ({ children }) => {
|
||||
const [users, setUsers] = useState(users);
|
||||
|
||||
return (
|
||||
<UserContext.Provider value={{ users, setUsers }}>
|
||||
{children}
|
||||
</UserContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useUser = () => {
|
||||
const usersState = useContext(UsersContext);
|
||||
|
||||
if (!usersState) {
|
||||
throw new Error("useUsers must be used within a UsersProvider");
|
||||
}
|
||||
return usersState;
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
import {
|
||||
Box, Button,
|
||||
Flex,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Heading,
|
||||
Input,
|
||||
Container,
|
||||
} from '@chakra-ui/react';
|
||||
import {useState} from 'react';
|
||||
import {useRouter} from 'next/router';
|
||||
import {signIn, SignInResponse,} from 'next-auth/react';
|
||||
|
||||
import {LoginData} from '@/models/form/LoginData';
|
||||
|
||||
export default function LoginForm() {
|
||||
const router = useRouter();
|
||||
const [loginData, setLoginData] = useState(new LoginData());
|
||||
const [invalidInput, setInvalidInput] = useState(false);
|
||||
|
||||
const buttonWidth = {base: '100%', md: 'unset'};
|
||||
|
||||
const handleInput = ({email = loginData.email, password = loginData.password}) => {
|
||||
setInvalidInput(false);
|
||||
setLoginData(new LoginData(email, password));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const {email, password} = loginData;
|
||||
await signIn('credentials',
|
||||
{email, password, redirect: false})
|
||||
.then((res) => {
|
||||
const {ok} = res as SignInResponse
|
||||
|
||||
if (!ok) setInvalidInput(true)
|
||||
else router.push("/")
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box flexBasis={'100%'}>
|
||||
<Container>
|
||||
<Heading textAlign={'center'} size={'2xl'}>Connexion</Heading>
|
||||
|
||||
{/* Email */}
|
||||
<FormControl mt={'100px'} isInvalid={invalidInput}>
|
||||
<Box mb={'1rem'}>
|
||||
<FormLabel>Adresse email</FormLabel>
|
||||
<Input
|
||||
isInvalid={invalidInput}
|
||||
id={'email'}
|
||||
type={'email'}
|
||||
value={loginData.email}
|
||||
onChange={(evt) => handleInput({email: evt.target.value})}
|
||||
placeholder={'adresse@email.com'}
|
||||
required={true}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Mot de passe */}
|
||||
<Box mb={'1rem'}>
|
||||
<FormLabel>Mot de passe</FormLabel>
|
||||
<Input
|
||||
isInvalid={invalidInput}
|
||||
id={'password'}
|
||||
type={'password'}
|
||||
value={loginData.password}
|
||||
onChange={(evt) => handleInput({password: evt.target.value})}
|
||||
placeholder={'Mot de passe'}
|
||||
required={true}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/*Boutons*/}
|
||||
<Flex justify={'center'} mt={'50px'} gap={5}
|
||||
justifyContent={'space-between'}>
|
||||
<Button onClick={() => router.push('/')}
|
||||
w={buttonWidth}>Retour</Button>
|
||||
<Button onClick={handleSubmit}
|
||||
w={buttonWidth} colorScheme="purple">Connexion</Button>
|
||||
</Flex>
|
||||
|
||||
</FormControl>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -21,13 +21,7 @@ export default function HomeHero() {
|
||||
);
|
||||
|
||||
const LeftSide = () => (
|
||||
<Box flexBasis={"100%"}>
|
||||
<Image
|
||||
boxShadow={"lg"}
|
||||
minH={"100vh"}
|
||||
src={"/couple_funny.png"}
|
||||
alt="funny couple"
|
||||
/>
|
||||
<Box flexBasis={"100%"} minH={"100vh"} bgImage={"/couple_funny.png"} bgSize={'cover'} bgPos={"center"}>
|
||||
</Box>
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export class LoginData {
|
||||
|
||||
constructor(
|
||||
public email: string = "",
|
||||
public password: string = ""
|
||||
) {}
|
||||
}
|
||||
+5
-2
@@ -1,11 +1,14 @@
|
||||
import '@/styles/globals.css';
|
||||
import type {AppProps} from 'next/app';
|
||||
import {ChakraProvider} from '@chakra-ui/react';
|
||||
import { SessionProvider } from "next-auth/react"
|
||||
|
||||
export default function App({Component, pageProps}: AppProps) {
|
||||
export default function App({Component, pageProps: { session, ...pageProps }}: AppProps) {
|
||||
return (
|
||||
<ChakraProvider>
|
||||
<Component {...pageProps} />
|
||||
<SessionProvider session={session}>
|
||||
<Component {...pageProps} />
|
||||
</SessionProvider>
|
||||
</ChakraProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import NextAuth from "next-auth"
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
import {PrismaClient} from '@prisma/client';
|
||||
import {NextApiRequest, NextApiResponse} from 'next';
|
||||
import {LoginData} from '@/models/form/LoginData';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default async function auth(req: NextApiRequest, res: NextApiResponse) {
|
||||
const providers = [
|
||||
CredentialsProvider({
|
||||
name: "Credentials",
|
||||
credentials: {
|
||||
email: { label: "Email", type: "text", placeholder: "adress@email.com" },
|
||||
password: { label: "Password", type: "password" }
|
||||
},
|
||||
async authorize(credentials) {
|
||||
const {email, password} = credentials as LoginData;
|
||||
if (!email || !password) return null;
|
||||
|
||||
// Appel à la base de donnée
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {email}
|
||||
});
|
||||
|
||||
// Vérification de la connexion
|
||||
if (user && user.password === password) return user;
|
||||
return null;
|
||||
},
|
||||
})
|
||||
];
|
||||
|
||||
return await NextAuth(req, res, {
|
||||
providers,
|
||||
session: {strategy: "jwt",},
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
callbacks: {
|
||||
async session({ session, token }: { session: any; token: any }) {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: { email: session.user.email }
|
||||
});
|
||||
|
||||
session.token = token.sub
|
||||
session.user = user
|
||||
return session
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import {NextApiRequest, NextApiResponse} from 'next';
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import type {CreateUserQuery} from '@/models/api/user';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default async function createUser(req: NextApiRequest, res: NextApiResponse) {
|
||||
const {email, password, firstName, lastName} = req.body as CreateUserQuery;
|
||||
|
||||
if (!email || !password || !firstName || !lastName)
|
||||
return res.status(400).send({message: req.body});
|
||||
|
||||
const newUser = await prisma.user.create({
|
||||
data: {email, password, firstName, lastName},
|
||||
});
|
||||
|
||||
return res.status(201).send({message: "createUser", newUser});
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import {NextApiRequest, NextApiResponse} from 'next';
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import type {DeleteUserQuery} from '@/models/api/user';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default async function deleteUser(req: NextApiRequest, res: NextApiResponse) {
|
||||
const {id} = req.query as DeleteUserQuery
|
||||
if (!id) return res.status(400).send({message: "error"});
|
||||
|
||||
const deletedUser = await prisma.user.delete({
|
||||
where: { id }
|
||||
});
|
||||
return res.status(200).send({message: "deleteUser", deletedUser});
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import type {NextApiRequest, NextApiResponse} from 'next';
|
||||
import updateUser from '@/pages/api/user/updateUser';
|
||||
import CRUD from '@/utils/CRUD';
|
||||
import {CreateUserQuery, DeleteUserQuery} from '@/models/api/user';
|
||||
import {PrismaClient} from '@prisma/client';
|
||||
@@ -8,13 +7,13 @@ export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
) {
|
||||
const {method} = req;
|
||||
|
||||
if (method === CRUD.CREATE) return createUser(req, res);
|
||||
if (method === CRUD.READ) return readUser(req, res);
|
||||
if (method === CRUD.UPDATE) return updateUser(req, res);
|
||||
if (method === CRUD.DELETE) return deleteUser(req, res);
|
||||
return help(res);
|
||||
switch (req.method) {
|
||||
case CRUD.CREATE: return createUser(req, res);
|
||||
case CRUD.READ: return readUser(req, res);
|
||||
// case CRUD.UPDATE: return updateUser(req, res);
|
||||
case CRUD.DELETE: return deleteUser(req, res);
|
||||
default: return help(res);
|
||||
}
|
||||
}
|
||||
|
||||
function help(res: NextApiResponse) {
|
||||
@@ -35,7 +34,6 @@ async function createUser(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
return res.status(201).send({message: 'createUser', newUser});
|
||||
}
|
||||
|
||||
async function deleteUser(req: NextApiRequest, res: NextApiResponse) {
|
||||
const {id} = req.query as DeleteUserQuery;
|
||||
if (!id) return res.status(400).send({message: 'error'});
|
||||
@@ -45,7 +43,6 @@ async function deleteUser(req: NextApiRequest, res: NextApiResponse) {
|
||||
});
|
||||
return res.status(200).send({message: 'deleteUser', deletedUser});
|
||||
}
|
||||
|
||||
async function readUser(req: NextApiRequest, res: NextApiResponse) {
|
||||
const users = await prisma.user.findMany();
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default async function readUser(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const { email, password } = req.query;
|
||||
|
||||
if (!email && !password) {
|
||||
const users = await prisma.user.findMany();
|
||||
return res.status(200).send({ message: "readUser", users });
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({ where: { email: email } });
|
||||
|
||||
if (user?.password == password) {
|
||||
res.status(200).send({ message: "User found", user });
|
||||
}
|
||||
|
||||
res.status(400).send({ message: "Mot de passe incorrect" });
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import {NextApiRequest, NextApiResponse} from 'next';
|
||||
|
||||
export default function updateUser(req: NextApiRequest, res: NextApiResponse) {
|
||||
return res.status(200).send({message: "updateUser"}); // TODO
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Heading,
|
||||
Image,
|
||||
Input,
|
||||
Spacer,
|
||||
} from "@chakra-ui/react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
export default function Login() {
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
formState: { errors, isSubmitting },
|
||||
} = useForm();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const onLogin = async (values) => {
|
||||
try {
|
||||
const response = await fetch(`/api/user/?email=${values.email}&password=${values.password}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data.error) {
|
||||
alert(data.message);
|
||||
} else {
|
||||
console.log(data);
|
||||
alert("connexion réussie");
|
||||
// router.push("/");
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const redirect_home = () => {
|
||||
router.push("/");
|
||||
};
|
||||
|
||||
const RightSide = () => (
|
||||
<Flex
|
||||
justify={"center"}
|
||||
direction={"column"}
|
||||
flexBasis={"100%"}
|
||||
align={"center"}
|
||||
>
|
||||
<Heading mb={"2.5rem"}>Connexion</Heading>
|
||||
<Box w={"25vw"}>
|
||||
<form onSubmit={handleSubmit(onLogin)}>
|
||||
<FormControl isInvalid={errors.name}>
|
||||
<Box mb={"1rem"}>
|
||||
<FormLabel>Adresse email</FormLabel>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="Adresse@email.com"
|
||||
{...register("email", {
|
||||
required: "This is required",
|
||||
})}
|
||||
/>
|
||||
</Box>
|
||||
<Box mb={"1rem"}>
|
||||
<FormLabel>Mot de passe</FormLabel>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="Mot de passe"
|
||||
{...register("password", {
|
||||
required: "This is required",
|
||||
})}
|
||||
/>
|
||||
</Box>
|
||||
<Flex mt={"1rem"} w={"100%"}>
|
||||
<Button onClick={redirect_home}>Retour</Button>
|
||||
<Spacer />
|
||||
<Button colorScheme="purple" type="submit">
|
||||
Connexion
|
||||
</Button>
|
||||
</Flex>
|
||||
</FormControl>
|
||||
</form>
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
const LeftSide = () => (
|
||||
<Box flexBasis={"100%"}>
|
||||
<Image
|
||||
h={"100vh"}
|
||||
w={"100%"}
|
||||
src={"/couple_horizon.png"}
|
||||
alt="couple looking at horizon"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Flex gap={10}>
|
||||
<LeftSide />
|
||||
<RightSide />
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
+11
-92
@@ -1,102 +1,21 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Heading,
|
||||
Image,
|
||||
Input,
|
||||
Spacer,
|
||||
} from '@chakra-ui/react';
|
||||
import {useRouter} from 'next/router';
|
||||
import {useForm} from 'react-hook-form';
|
||||
import LoginForm from '@/components/form/LoginForm';
|
||||
|
||||
type FormValues = {
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export default function Login() {
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
formState: {errors, isSubmitting},
|
||||
} = useForm();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const onLogin = async (values: FormValues) => {
|
||||
alert(JSON.stringify(values, null, 2));
|
||||
// faut voir ce qu'on fait quand on se connecte
|
||||
};
|
||||
|
||||
const goHome = () => {
|
||||
router.push('/');
|
||||
};
|
||||
|
||||
const RightSide = () => (
|
||||
<Flex
|
||||
justify={'center'}
|
||||
direction={'column'}
|
||||
flexBasis={'100%'}
|
||||
align={'center'}
|
||||
>
|
||||
<Heading mb={'2.5rem'}>Connexion</Heading>
|
||||
|
||||
<Box w={'25vw'}>
|
||||
<form onSubmit={handleSubmit(onLogin)}>
|
||||
<FormControl isInvalid={errors.name}>
|
||||
<Box mb={'1rem'}>
|
||||
<FormLabel>Adresse email</FormLabel>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="Adresse@email.com"
|
||||
{...register('email', {
|
||||
required: 'This is required',
|
||||
})}
|
||||
/>
|
||||
</Box>
|
||||
<Box mb={'1rem'}>
|
||||
<FormLabel>Mot de passe</FormLabel>
|
||||
<Input
|
||||
id="pwd"
|
||||
type="password"
|
||||
placeholder="Mot de passe"
|
||||
{...register('password', {
|
||||
required: 'This is required',
|
||||
})}
|
||||
/>
|
||||
</Box>
|
||||
<Flex mt={'1rem'} w={'100%'}>
|
||||
<Button onClick={goHome}>Retour</Button>
|
||||
<Spacer/>
|
||||
<Button colorScheme="purple" type="submit">Connexion</Button>
|
||||
</Flex>
|
||||
</FormControl>
|
||||
</form>
|
||||
</Box>
|
||||
return (
|
||||
<Flex gap={5} h={'100vh'} justify={'center'} alignItems={'center'}>
|
||||
<Box flexBasis={'100%'}
|
||||
display={{base: "none", md: "block"}}
|
||||
h={"100%"}
|
||||
bgImage={'/couple_horizon.png'}
|
||||
bgSize={'cover'}
|
||||
bgPos={'center'}
|
||||
/>
|
||||
<LoginForm/>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
const LeftSide = () => (
|
||||
<Box flexBasis={'100%'}>
|
||||
<Image
|
||||
h={'100vh'}
|
||||
w={'100%'}
|
||||
src={'/couple_horizon.png'}
|
||||
alt="couple looking at horizon"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Flex gap={10}>
|
||||
<LeftSide/>
|
||||
<RightSide/>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user