Edit Crud, add loaders during authentication

Took 2 hours 49 minutes
This commit is contained in:
Lucàs
2023-03-26 17:02:24 +02:00
parent 668e092dd3
commit 5cc56e46ec
16 changed files with 3319 additions and 154 deletions
+2006 -1
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -12,6 +12,7 @@
"@chakra-ui/react": "^2.5.1", "@chakra-ui/react": "^2.5.1",
"@emotion/react": "^11.10.6", "@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6", "@emotion/styled": "^11.10.6",
"@premieroctet/next-crud": "^2.2.0",
"@prisma/client": "^4.11.0", "@prisma/client": "^4.11.0",
"@types/react": "18.0.28", "@types/react": "18.0.28",
"@types/react-dom": "18.0.11", "@types/react-dom": "18.0.11",
+9 -9
View File
@@ -43,15 +43,6 @@ model User {
OtherUserLikes User[] @relation("Likes", fields: [OtherUserLikesID], references: [id]) OtherUserLikes User[] @relation("Likes", fields: [OtherUserLikesID], references: [id])
} }
model Chat {
id String @id @default(auto()) @map("_id") @db.ObjectId
messages Message[]
// Les utilisateurs qui ont un chat en commun
User User[] @relation(fields: [UserId], references: [id])
UserId String[] @db.ObjectId
}
model Passion { model Passion {
id String @id @default(auto()) @map("_id") @db.ObjectId id String @id @default(auto()) @map("_id") @db.ObjectId
name String name String
@@ -61,6 +52,15 @@ model Passion {
User User[] @relation(fields: [UserId], references: [id]) User User[] @relation(fields: [UserId], references: [id])
} }
model Chat {
id String @id @default(auto()) @map("_id") @db.ObjectId
messages Message[]
// Les utilisateurs qui ont un chat en commun
User User[] @relation(fields: [UserId], references: [id])
UserId String[] @db.ObjectId
}
model Message { model Message {
id String @id @default(auto()) @map("_id") @db.ObjectId id String @id @default(auto()) @map("_id") @db.ObjectId
message String message String
+9 -4
View File
@@ -16,6 +16,7 @@ import {LoginData} from '@/models/form/LoginData';
export default function LoginForm() { export default function LoginForm() {
const router = useRouter(); const router = useRouter();
const [loginData, setLoginData] = useState(new LoginData()); const [loginData, setLoginData] = useState(new LoginData());
const [isLoading, setIsLoading] = useState(false);
const [invalidInput, setInvalidInput] = useState(false); const [invalidInput, setInvalidInput] = useState(false);
const buttonWidth = {base: '100%', md: 'unset'}; const buttonWidth = {base: '100%', md: 'unset'};
@@ -26,11 +27,15 @@ export default function LoginForm() {
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
await signIn('credentials', setIsLoading(true)
{...loginData, redirect: false}).then((res) => { signIn('credentials', {...loginData, redirect: false})
.then((res: unknown) => {
const {ok: connexionSuccess} = res as SignInResponse; const {ok: connexionSuccess} = res as SignInResponse;
if (!connexionSuccess) setInvalidInput(true); if (!connexionSuccess) {
setIsLoading(false);
setInvalidInput(true);
}
else router.push('/dashboard'); else router.push('/dashboard');
}); });
}; };
@@ -76,7 +81,7 @@ export default function LoginForm() {
justifyContent={'space-between'}> justifyContent={'space-between'}>
<Button onClick={() => router.push('/')} <Button onClick={() => router.push('/')}
w={buttonWidth}>Retour</Button> w={buttonWidth}>Retour</Button>
<Button onClick={handleSubmit} <Button isLoading={isLoading} onClick={handleSubmit}
w={buttonWidth} colorScheme="purple">Connexion</Button> w={buttonWidth} colorScheme="purple">Connexion</Button>
</Flex> </Flex>
</Container> </Container>
+20 -15
View File
@@ -9,12 +9,13 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import {useRouter} from 'next/router'; import {useRouter} from 'next/router';
import {useState} from 'react'; import {useState} from 'react';
import {RegisterData} from '@/models/form/RegisterData';
import {signIn, SignInResponse} from 'next-auth/react'; import {signIn, SignInResponse} from 'next-auth/react';
import {RegisterData} from '@/models/form/RegisterData';
export default function RegisterForm() { export default function RegisterForm() {
const router = useRouter(); const router = useRouter();
const [registerData, setRegisterData] = useState(new RegisterData()); const [registerData, setRegisterData] = useState(new RegisterData());
const [isLoading, setIsLoading] = useState(false);
const [invalidInput, setInvalidInput] = useState(false); const [invalidInput, setInvalidInput] = useState(false);
const buttonWidth = {base: '100%', md: 'unset'}; const buttonWidth = {base: '100%', md: 'unset'};
@@ -28,31 +29,34 @@ export default function RegisterForm() {
let {email, firstName, lastName, password, confirmPassword} = registerData; let {email, firstName, lastName, password, confirmPassword} = registerData;
if (password !== confirmPassword) setInvalidInput(true); if (password !== confirmPassword) setInvalidInput(true);
fetch('/api/user', { const options = {
method: 'PUT', 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}),
}).then(() => { };
setIsLoading(true);
fetch('/api/users', options).then(() => {
signIn('credentials', {email, password, redirect: false}) signIn('credentials', {email, password, redirect: false})
.then((res) => { .then((res: unknown) => {
const {ok: connexionSuccess} = res as SignInResponse; const {ok: connexionSuccess} = res as SignInResponse;
// TODO If success -> goto interactive form else login
router.push( connexionSuccess ? "/" : "/login");
});
// TODO If success -> goto interactive form else login
router.push(connexionSuccess ? '/' : '/login');
});
}).catch(() => { }).catch(() => {
setIsLoading(false);
setInvalidInput(true); setInvalidInput(true);
setRegisterData({...registerData, password: '', confirmPassword: ''}); setRegisterData({...registerData, password: '', confirmPassword: ''});
}); });
}; };
return ( return (
<Box flexBasis={"100%"}> <Box flexBasis={'100%'}>
<Container> <Container>
<Heading textAlign={"center"} size={"2xl"}>Inscription</Heading> <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>
<FormLabel>Prénom</FormLabel> <FormLabel>Prénom</FormLabel>
@@ -119,8 +123,9 @@ export default function RegisterForm() {
<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}>Retour</Button> <Button onClick={() => router.push('/')}
<Button onClick={handleSubmit} w={buttonWidth} colorScheme="purple">Je w={buttonWidth}>Retour</Button>
<Button isLoading={isLoading} onClick={handleSubmit} w={buttonWidth} colorScheme="purple">Je
m&apos;inscris</Button> m&apos;inscris</Button>
</Flex> </Flex>
</Container> </Container>
+1 -1
View File
@@ -6,7 +6,7 @@ export async function hashPassword(unHashedPassword: string): Promise<string> {
export async function isSamePassword( export async function isSamePassword(
unHashedPassword: string, unHashedPassword: string,
hashedPassword: string hashedPassword: string,
): Promise<boolean> { ): Promise<boolean> {
return await bcrypt.compare(unHashedPassword, hashedPassword). return await bcrypt.compare(unHashedPassword, hashedPassword).
then((result: boolean) => result); then((result: boolean) => result);
-8
View File
@@ -1,8 +0,0 @@
const CRUD = {
CREATE: "PUT",
READ: "GET",
UPDATE: "POST",
DELETE: "DELETE",
};
export default CRUD;
-16
View File
@@ -1,16 +0,0 @@
type CreateUserQuery = {
email: string
password: string
firstName: string
lastName: string
}
type ReadUserQuery = {
id?: string
}
type DeleteUserQuery = {
id: string
}
export type {CreateUserQuery, ReadUserQuery, DeleteUserQuery};
@@ -1,4 +1,4 @@
import {User} from '@/models/data_models/User'; import type {User} from "@prisma/client";
export type Session = { export type Session = {
user: User user: User
-8
View File
@@ -1,8 +0,0 @@
export type User = {
id: string
email: string
password: string
firstName: string
lastName: string
role: "USER" | "ADMIN"
}
+34
View File
@@ -0,0 +1,34 @@
import NextCrud, {PrismaAdapter} from '@premieroctet/next-crud';
import prismaClient from '@/lib/prismaClient';
import {NextApiRequest, NextApiResponse} from 'next';
import {hashPassword} from '@/lib/PasswordTools';
type CreateUserQuery = {
email: string
password: string
firstName: string
lastName: string
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const nextCrudHandler = await NextCrud({
adapter: new PrismaAdapter({prismaClient: prismaClient}),
});
// Hash le mot de passe quand on crée un utilisateur
if (req.url === "/api/users" && req.method === "POST") {
const {email, password, firstName, lastName} = req.body as CreateUserQuery;
if (!email || !password || !firstName || !lastName)
return res.status(400).send({message: req.body});
const hashedPassword: string = await hashPassword(password);
req.body = {...req.body, password: hashedPassword}
}
return nextCrudHandler(req, res);
}
+18 -11
View File
@@ -3,30 +3,37 @@ import CredentialsProvider from 'next-auth/providers/credentials';
import {NextApiRequest, NextApiResponse} from 'next'; import {NextApiRequest, NextApiResponse} from 'next';
import {LoginData} from '@/models/form/LoginData'; import {LoginData} from '@/models/form/LoginData';
import {isSamePassword} from '@/lib/PasswordTools'; import {isSamePassword} from '@/lib/PasswordTools';
import {User} from '@prisma/client';
import prismaClient from '@/lib/prismaClient'; import prismaClient from '@/lib/prismaClient';
import {dmmf} from '.prisma/client/edge';
export default async function auth(req: NextApiRequest, res: NextApiResponse) { export default async function auth(req: NextApiRequest, res: NextApiResponse) {
const providers = [ const providers = [
CredentialsProvider({ CredentialsProvider({
name: 'Credentials', name: 'Credentials',
credentials: { credentials: {
email: {label: 'Email', type: 'text', placeholder: 'adress@email.com'}, email: {
password: {label: 'Password', type: 'password'}, label: 'Email',
type: 'text',
placeholder: 'adresse@email.com'
},
password: {
label: 'Password',
type: 'password',
placeholder: 'mot de passe',
},
}, },
async authorize(credentials) { async authorize(credentials: unknown) {
const {email, password} = credentials as LoginData; const {email, password} = credentials as LoginData;
if (!email || !password) return null; if (!email || !password) return null;
// Appel à la base de donnée // Appel à la base de donnée
const user = await getUserByEmail(email); const user = await getUserByEmail(email);
if (!user) return null;
// Vérification de la connexion // Vérification de la connexion
if (user && await isSamePassword(password, user.password)) { const passwordIsValid = await isSamePassword(password, user.password);
return user; if (passwordIsValid) return user;
} else return null;
return null;
}, },
}), }),
]; ];
@@ -46,8 +53,8 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) {
}); });
} }
async function getUserByEmail(email: string) { async function getUserByEmail(email: string): Promise<null | User> {
return prismaClient.user.findUnique({ return prismaClient.user.findUnique({
where: {email}, where: {email}
}); });
} }
-51
View File
@@ -1,51 +0,0 @@
import type {NextApiRequest, NextApiResponse} from 'next';
import CRUD from '@/models/api/CRUD';
import {CreateUserQuery} from '@/models/api/UserQuery';
import {hashPassword} from '@/lib/PasswordTools';
import prismaClient from '@/lib/prismaClient';
export default function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
switch (req.method) {
case CRUD.CREATE:
return createUser(req, res);
case CRUD.READ:
return readUser(req, res);
default:
return help(res);
}
}
function help(res: NextApiResponse) {
res.status(400).send({message: 'error'}); // TODO add help message
}
const prisma = prismaClient;
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 hashedPassword = await hashPassword(password);
const newUser = await prisma.user.create({
data: {...req.body, password: hashedPassword},
});
return res.status(201).send({message: 'createUser', newUser});
}
async function readUser(req: NextApiRequest, res: NextApiResponse) {
const {id, email} = req.query as { id: string, email: string };
const user = (id)
? await prisma.user.findUnique({where: {id}})
: await prisma.user.findMany()
;
return res.status(200).send({message: 'readUser', user});
}
+1 -1
View File
@@ -2,7 +2,7 @@ import {Grid, GridItem, Text, Box} from '@chakra-ui/react';
import {useSession} from 'next-auth/react'; import {useSession} from 'next-auth/react';
import {useRouter} from 'next/router'; import {useRouter} from 'next/router';
import type {Session} from '@/models/data_models/Session'; import type {Session} from '@/models/auth/Session';
import CardUser from '../components/layout/dashboard/card_user/CardUser'; import CardUser from '../components/layout/dashboard/card_user/CardUser';
import LeftPanel from '../components/layout/dashboard/left_panel/LeftPanel'; import LeftPanel from '../components/layout/dashboard/left_panel/LeftPanel';
import Head from 'next/head'; import Head from 'next/head';
+1 -1
View File
@@ -10,7 +10,7 @@ export default function Home() {
<> <>
<Head><title>{websiteName}</title></Head> <Head><title>{websiteName}</title></Head>
<Navbar/> <Navbar variant={"fixed"}/>
<HeroBanner/> <HeroBanner/>
<HomePresentation/> <HomePresentation/>
</> </>
+1218 -27
View File
File diff suppressed because it is too large Load Diff