refactor: Set default colorScheme to purple

Took 1 hour 12 minutes
This commit is contained in:
Lucàs
2023-05-06 12:51:28 +02:00
parent 0c7c3081b5
commit e0dbda70a8
32 changed files with 811 additions and 1152 deletions
+3 -15
View File
@@ -1,27 +1,15 @@
import { Box, Flex, Text, Button, Image } from "@chakra-ui/react"; import { Box, Flex, Text, Button, Image } from "@chakra-ui/react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
export default function BottomBar(props) { export default function BottomBar(props: { variant: any; saveData: any; }) {
const router = useRouter(); const router = useRouter();
const { variant, saveData } = props; const { variant, saveData } = props;
return ( return (
<Box <Box position={variant} zIndex={0} bottom={0} backdropFilter={"auto"} px={10} py={2}>
position={variant}
zIndex={0}
bottom={0}
backdropFilter={"auto"}
px={10}
py={2}
>
<Flex align={"center"}> <Flex align={"center"}>
<Button <Button onClick={() => saveData()}>
colorScheme={"purple"}
onClick={() => {
saveData();
}}
>
Sauvegarder les modifications Sauvegarder les modifications
</Button> </Button>
</Flex> </Flex>
+17 -36
View File
@@ -1,13 +1,13 @@
import { useState } from "react"; import {useState} from 'react';
import { Flex, IconButton } from "@chakra-ui/react"; import {Flex, IconButton} from '@chakra-ui/react';
import { BiLeftArrowAlt, BiRightArrowAlt } from "react-icons/bi"; import {BiLeftArrowAlt, BiRightArrowAlt} from 'react-icons/bi';
interface Props { interface Props {
images: string[]; images: string[];
borderRadius?: string | number; borderRadius?: string | number;
} }
const Carousel = ({ images, borderRadius: bRadius }: Props) => { const Carousel = ({images, borderRadius: bRadius}: Props) => {
const [currentIndex, setCurrentIndex] = useState(0); const [currentIndex, setCurrentIndex] = useState(0);
const handleClickPrevious = () => { const handleClickPrevious = () => {
@@ -18,41 +18,22 @@ const Carousel = ({ images, borderRadius: bRadius }: Props) => {
setCurrentIndex(currentIndex === images.length - 1 ? 0 : currentIndex + 1); setCurrentIndex(currentIndex === images.length - 1 ? 0 : currentIndex + 1);
}; };
if (images.length === 0) images = ["blank_profile_picture.webp"]; if (images.length === 0) images = ['blank_profile_picture.webp'];
return ( return (
<Flex <Flex px={2} align="center" borderRadius={bRadius} overflow={'hidden'}
px={2} justify={'space-between'} bgColor={'purple.50'} height={500}
align="center" bgImage={images[currentIndex]} bgSize={'contain'}
borderRadius={bRadius} bgRepeat={'no-repeat'} bgPosition={'center'} width={'100%'}>
overflow={"hidden"} <IconButton aria-label="left-arrow" borderRadius="full"
justify={"space-between"} onClick={handleClickPrevious}>
bgColor={"purple.50"} <BiLeftArrowAlt/>
bgImage={images[currentIndex]} </IconButton>
bgSize={"contain"}
bgRepeat={"no-repeat"}
bgPosition={"center"}
width={"100%"}
height={500}
>
<IconButton
aria-label="left-arrow"
colorScheme="purple"
borderRadius="full"
onClick={handleClickPrevious}
>
<BiLeftArrowAlt />
</IconButton>
<IconButton <IconButton aria-label="left-arrow" borderRadius="full" onClick={handleClickNext}>
aria-label="left-arrow" <BiRightArrowAlt/>
colorScheme="purple" </IconButton>
borderRadius="full" </Flex>
onClick={handleClickNext}
>
<BiRightArrowAlt />
</IconButton>
</Flex>
); );
}; };
+3 -10
View File
@@ -6,20 +6,13 @@ export default function LoadingPage() {
const router = useRouter(); const router = useRouter();
return ( return (
<VStack <VStack justifyContent="center" alignItems="center" height="100vh"
justifyContent="center" width="100vw" bg="gray.100">
alignItems="center"
height="100vh"
width="100vw"
bg="gray.100"
>
<MdError color="purple.500" size="50%" /> <MdError color="purple.500" size="50%" />
<Text color="purple.500" fontSize="2xl" fontWeight="bold"> <Text color="purple.500" fontSize="2xl" fontWeight="bold">
Veillez à autoriser la géolocalisation Veillez à autoriser la géolocalisation
</Text> </Text>
<Button colorScheme={"purple"} onClick={() => router.push("/")}> <Button onClick={() => router.push("/")}>Page d'accueil</Button>
Page d'accueuil
</Button>
</VStack> </VStack>
); );
} }
+3 -10
View File
@@ -6,20 +6,13 @@ export default function LoadingPage() {
const router = useRouter(); const router = useRouter();
return ( return (
<VStack <VStack justifyContent="center" alignItems="center" height="100vh"
justifyContent="center" width="100vw" bg="gray.100">
alignItems="center"
height="100vh"
width="100vw"
bg="gray.100"
>
<MdError color="purple.500" size="50%" /> <MdError color="purple.500" size="50%" />
<Text color="purple.500" fontSize="2xl" fontWeight="bold"> <Text color="purple.500" fontSize="2xl" fontWeight="bold">
Une erreur est survenue Une erreur est survenue
</Text> </Text>
<Button colorScheme={"purple"} onClick={() => router.push("/")}> <Button onClick={() => router.push("/")}>Page d'accueil</Button>
Page d'accueuil
</Button>
</VStack> </VStack>
); );
} }
+3 -14
View File
@@ -2,20 +2,9 @@ import { Flex, Spinner } from "@chakra-ui/react";
export default function LoadingPage() { export default function LoadingPage() {
return ( return (
<Flex <Flex justifyContent="center" alignItems="center" height="100vh"
justifyContent="center" width="100vw" bg="gray.100">
alignItems="center" <Spinner thickness="4px" speed="0.65s" emptyColor="gray.200" size="xl"/>
height="100vh"
width="100vw"
bg="gray.100"
>
<Spinner
thickness="4px"
speed="0.65s"
emptyColor="gray.200"
color="purple.500"
size="xl"
/>
</Flex> </Flex>
); );
} }
+53 -67
View File
@@ -6,87 +6,73 @@ import {
Button, Button,
Image, Image,
Link, Link,
} from "@chakra-ui/react"; } from '@chakra-ui/react';
import { useRouter } from "next/router"; import {useRouter} from 'next/router';
import { signOut, useSession } from "next-auth/react"; import {signOut, useSession} from 'next-auth/react';
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 MiddleSection = () => {
const authenticatedLinks = (
<>
<Link href={'/dashboard'}>Tableau de bord</Link>
<Link href={'/profile'}>Profile</Link>
<Link href={'/map'}>Carte</Link>
</>
);
return (
<Flex gap={5} justify={'center'} flexBasis={'100%'}>
{status === 'authenticated'
? authenticatedLinks
: <></>
}
</Flex>
);
};
const RightSection = () => { const RightSection = () => {
if (status === "authenticated" && session.user)
return ( const authenticatedButtons = (
<Flex justify={"right"} flexBasis={"100%"}> <Button onClick={() => signOut()}>Déconnexion</Button>
);
const unauthenticatedButtons = (
<>
<Button onClick={() => router.push('/register')}>Inscription</Button>
<Button onClick={() => router.push('/login')}>Connexion</Button>
</>
);
return (
<Flex justify={'right'} flexBasis={'100%'}>
<ButtonGroup> <ButtonGroup>
<Button colorScheme={"purple"} onClick={() => signOut()}> {status === 'authenticated'
Déconnexion ? authenticatedButtons
</Button> : unauthenticatedButtons
}
</ButtonGroup> </ButtonGroup>
</Flex> </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>
);
}; };
return ( return (
<Box <Box position={variant} zIndex={9999} top={0} width={'100vw'}
position={variant} backdropFilter={'auto'} backdropBlur={'20px'} px={10} py={2}>
zIndex={9999} <Flex align={'center'}>
top={0} <Box flexBasis={'100%'}>
width={"100vw"} <Image src={'/logo.svg'} h={'3rem'} objectFit={'contain'}/>
backdropFilter={"auto"} </Box>
backdropBlur={"20px"}
px={10}
py={2}
>
<Flex align={"center"}>
<Box flexBasis={"100%"}>
<Image src={"/logo.svg"} h={"3rem"} objectFit={"contain"} />
</Box>
<Flex gap={5} justify={"center"} flexBasis={"100%"}> <MiddleSection/>
{status === "authenticated" ? ( <RightSection/>
<>
<Link href={"/dashboard"} color={"purple.500"}>
Tableau de bord
</Link>
<Link href={"/profile"} color={"purple.500"}>
Profile
</Link>
<Link href={"/map"} color={"purple.500"}>
Carte
</Link>
</>
) : (
<>
<Text>A propos</Text>
<Text>Contact</Text>
<Text>Aide</Text>
</>
)}
</Flex> </Flex>
</Box>
<RightSection />
</Flex>
</Box>
); );
} }
+2 -2
View File
@@ -32,8 +32,8 @@ export default function FormMessage(props: Props) {
return ( return (
<Flex gap={5} mt={5}> <Flex gap={5} mt={5}>
<Input type={text} colorScheme={'purple'} onChange={(evt) => setText(evt.target.value) } value={text} /> <Input type={text} onChange={evt => setText(evt.target.value) } value={text}/>
<Button colorScheme={'purple'} onClick={handleSubmit} >Envoyer</Button> <Button onClick={handleSubmit}>Envoyer</Button>
</Flex> </Flex>
) )
} }
+73 -81
View File
@@ -8,13 +8,13 @@ import {
Input, Input,
Container, Container,
FormErrorMessage, FormErrorMessage,
} from "@chakra-ui/react"; } from '@chakra-ui/react';
import { useState } from "react"; import {useState} from 'react';
import { useRouter } from "next/router"; import {useRouter} from 'next/router';
import { signIn, SignInResponse } from "next-auth/react"; 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"; import {useForm} from 'react-hook-form';
export default function LoginForm() { export default function LoginForm() {
const router = useRouter(); const router = useRouter();
@@ -24,92 +24,84 @@ export default function LoginForm() {
const { const {
handleSubmit, handleSubmit,
register, register,
formState: { errors, isSubmitting }, formState: {errors, isSubmitting},
} = useForm(); } = useForm();
const buttonWidth = { base: "100%", md: "unset" }; const buttonWidth = {base: '100%', md: 'unset'};
const errorPassword = "Mot de passe incorrect"; const errorPassword = 'Mot de passe incorrect';
const loginUser = async (value: LoginData) => { const loginUser = async (value: LoginData) => {
setIsLoading(true); setIsLoading(true);
signIn("credentials", { ...value, redirect: false }).then( signIn('credentials', {...value, redirect: false}).then(
(res: unknown) => { (res: unknown) => {
const { ok: connexionSuccess } = res as SignInResponse; const {ok: connexionSuccess} = res as SignInResponse;
setIsLoading(false); setIsLoading(false);
if (connexionSuccess) router.push("/dashboard"); if (connexionSuccess) router.push('/dashboard');
else setWrongPasswordError(true); else setWrongPasswordError(true);
} },
); );
}; };
return ( return (
<Box flexBasis={"100%"}> <Box flexBasis={'100%'}>
<Container> <Container>
<form id="login_form" onSubmit={handleSubmit(loginUser)}> <form id="login_form" onSubmit={handleSubmit(loginUser)}>
<Heading textAlign={"center"} size={"2xl"}> <Heading textAlign={'center'} size={'2xl'}>
Connexion
</Heading>
{/*Email*/}
<FormControl mb={"1rem"} id={"email"} isInvalid={errors.email}>
<FormLabel>Adresse email</FormLabel>
<Input
type="text"
{...register("email", {
required: { value: true, message: "Adresse email requise" },
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "Adresse email invalide",
},
})}
placeholder="adresse@email.com"
/>
<FormErrorMessage>{errors.email?.message}</FormErrorMessage>
</FormControl>
{/*Mot de passe*/}
<FormControl
mb={"1rem"}
id="password"
isInvalid={errors.password || wrongPasswordError}
>
<FormLabel>Mot de passe</FormLabel>
<Input
type="password"
placeholder="Mot de passe"
{...register("password", {
required: { value: true, message: "Mot de passe requis" },
})}
/>
<FormErrorMessage>{errors.password?.message}</FormErrorMessage>
<FormErrorMessage>
{wrongPasswordError && errorPassword}
</FormErrorMessage>
</FormControl>
{/*Boutons*/}
<Flex
justify={"center"}
mt={"50px"}
gap={5}
justifyContent={"space-between"}
>
<Button onClick={() => router.push("/")} w={buttonWidth}>
Retour
</Button>
<Button
isLoading={isLoading}
w={buttonWidth}
colorScheme="purple"
type="submit"
>
Connexion Connexion
</Button> </Heading>
</Flex>
</form> {/*Email*/}
</Container> <FormControl mb={'1rem'} id={'email'}
</Box> isInvalid={errors.email as boolean | undefined}>
<FormLabel>Adresse email</FormLabel>
<Input
type="text"
{...register('email', {
required: {value: true, message: 'Adresse email requise'},
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Adresse email invalide',
},
})}
placeholder="adresse@email.com"
/>
<FormErrorMessage>{errors.email?.message as string}</FormErrorMessage>
</FormControl>
{/*Mot de passe*/}
<FormControl
mb={'1rem'}
id="password"
isInvalid={errors.password as boolean | undefined ||
wrongPasswordError}
>
<FormLabel>Mot de passe</FormLabel>
<Input
type="password"
placeholder="Mot de passe"
{...register('password', {
required: {value: true, message: 'Mot de passe requis'},
})}
/>
<FormErrorMessage>{errors.password?.message as string}</FormErrorMessage>
<FormErrorMessage>{wrongPasswordError &&
errorPassword}</FormErrorMessage>
</FormControl>
{/*Boutons*/}
<Flex justify={'center'} mt={'50px'} gap={5}
justifyContent={'space-between'}>
<Button onClick={() => router.push('/')} w={buttonWidth}>
Retour
</Button>
<Button isLoading={isLoading} w={buttonWidth} type="submit">
Connexion
</Button>
</Flex>
</form>
</Container>
</Box>
); );
} }
+66 -91
View File
@@ -10,7 +10,6 @@ import {
FormErrorMessage, FormErrorMessage,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import {useRouter} from 'next/router'; import {useRouter} from 'next/router';
import {useState} from 'react';
import {signIn, SignInResponse} from 'next-auth/react'; import {signIn, SignInResponse} from 'next-auth/react';
import {RegisterData} from '@/models/form/RegisterData'; import {RegisterData} from '@/models/form/RegisterData';
import {SubmitHandler, useForm} from 'react-hook-form'; import {SubmitHandler, useForm} from 'react-hook-form';
@@ -48,137 +47,113 @@ export default function RegisterForm() {
}), }),
}; };
fetch('/api/users', options).then(() => { fetch('/api/users', options)
signIn('credentials', {email, password, redirect: false}).then( .then(() => signIn('credentials', {email, password, redirect: false}))
(res: unknown) => { .then((res: unknown) => {
const {ok: connexionSuccess} = res as SignInResponse; const {ok: connexionSuccess} = res as SignInResponse;
router.push(connexionSuccess ? '/getting-start' : '/');
// TODO If success -> goto interactive form else login },
router.push(connexionSuccess ? '/dashboard' : '/'); )
}, .catch(err => console.error(err));
);
}).catch((err) => console.error(err));
}; };
return ( return (
<Box flexBasis={'100%'}> <Box flexBasis={'100%'}>
<Container> <Container>
<form id="register_form" onSubmit={handleSubmit(submitHandler)}> <form id="register_form" onSubmit={handleSubmit(submitHandler)}>
<Heading textAlign={'center'} size={'2xl'}> <Heading textAlign={'center'} size={'2xl'}>Inscription</Heading>
Inscription
</Heading>
<Flex mt={'100px'} mb={'1rem'} gap={5}> <Flex mt={'100px'} mb={'1rem'} gap={5}>
{/*Prénom*/} {/*Prénom*/}
<FormControl id="firstName" isInvalid={errors.firstName as boolean|undefined}> <FormControl id="firstName"
isInvalid={errors.firstName as boolean | undefined}>
<FormLabel>Prénom</FormLabel> <FormLabel>Prénom</FormLabel>
<Input <Input type="text" placeholder="Prénom"
type="text" {...register('firstName', {
placeholder="Prénom" required: {value: true, message: 'Prénom requis'},
{...register('firstName', { })}/>
required: {value: true, message: 'Prénom requis'},
})}
/>
<FormErrorMessage>{errors.firstName?.message as string}</FormErrorMessage> <FormErrorMessage>{errors.firstName?.message as string}</FormErrorMessage>
</FormControl> </FormControl>
{/*Nom*/} {/*Nom*/}
<FormControl id="lastName" isInvalid={errors.lastName as boolean|undefined}> <FormControl id="lastName"
isInvalid={errors.lastName as boolean | undefined}>
<FormLabel>Nom</FormLabel> <FormLabel>Nom</FormLabel>
<Input <Input type="text" placeholder="Nom"
type="text" {...register('lastName', {
placeholder="Nom" required: {value: true, message: 'Nom requis'},
{...register('lastName', { })}/>
required: {value: true, message: 'Nom requis'},
})}
/>
<FormErrorMessage>{errors.lastName?.message as string}</FormErrorMessage> <FormErrorMessage>{errors.lastName?.message as string}</FormErrorMessage>
</FormControl> </FormControl>
</Flex> </Flex>
{/*Email*/} {/*Email*/}
<FormControl mb={'1rem'} id={'email'} isInvalid={errors.email as boolean|undefined}> <FormControl mb={'1rem'} id={'email'}
isInvalid={errors.email as boolean | undefined}>
<FormLabel>Adresse email</FormLabel> <FormLabel>Adresse email</FormLabel>
<Input <Input type="text" placeholder="adresse@email.com"
type="text" {...register('email', {
{...register('email', { required: {
required: {value: true, message: 'Adresse email requise'}, value: true,
pattern: { message: 'Adresse email requise',
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, },
message: 'Adresse email invalide', pattern: {
}, value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
})} message: 'Adresse email invalide',
placeholder="adresse@email.com" },
/> })}/>
<FormErrorMessage>{errors.email?.message as string}</FormErrorMessage> <FormErrorMessage>{errors.email?.message as string}</FormErrorMessage>
</FormControl> </FormControl>
{/*Date de naissance*/} {/*Date de naissance*/}
<FormControl <FormControl mb={'1rem'} id={'birthdate'}
mb={'1rem'} isInvalid={errors.birthdate as boolean | undefined}>
id={'birthdate'}
isInvalid={errors.birthdate as boolean|undefined}
>
<FormLabel>Date de naissance</FormLabel> <FormLabel>Date de naissance</FormLabel>
<Input <Input type="date"
type="date" {...register('birthdate', {
{...register('birthdate', { required: {
required: { value: true,
value: true, message: 'Date de naissance requise',
message: 'Date de naissance requise', },
}, validate: validateDate,
validate: validateDate, })}/>
})} {(errors.birthdate) &&
/> <FormErrorMessage>{errors.birthdate.message as string}</FormErrorMessage>}
{ (errors.birthdate) ? <FormErrorMessage>{errors.birthdate.message as string}</FormErrorMessage> : "" }
</FormControl> </FormControl>
{/*Mot de passe*/} {/*Mot de passe*/}
<FormControl mb={'1rem'} id="password" isInvalid={errors.password as boolean|undefined}> <FormControl mb={'1rem'} id="password"
isInvalid={errors.password as boolean | undefined}>
<FormLabel>Mot de passe</FormLabel> <FormLabel>Mot de passe</FormLabel>
<Input <Input type="password" placeholder="Mot de passe"
type="password" {...register('password', {
placeholder="Mot de passe" required: {value: true, message: 'Mot de passe requis'},
{...register('password', { })}/>
required: {value: true, message: 'Mot de passe requis'},
})}
/>
<FormErrorMessage>{errors.password?.message as string}</FormErrorMessage> <FormErrorMessage>{errors.password?.message as string}</FormErrorMessage>
</FormControl> </FormControl>
{/*Mot de passe (2)*/} {/*Mot de passe (2)*/}
<FormControl <FormControl mb={'1rem'} id="confirmPassword"
mb={'1rem'} isInvalid={errors.confirmPassword as boolean | undefined}>
id="confirmPassword"
isInvalid={errors.confirmPassword as boolean|undefined}
>
<FormLabel>Confirmation du mot de passe</FormLabel> <FormLabel>Confirmation du mot de passe</FormLabel>
<Input <Input type="password" placeholder="Mot de passe"
type="password" {...register('confirmPassword', {
placeholder="Mot de passe" required: {value: true, message: 'Confirmation requise'},
{...register('confirmPassword', { validate: (value: string) => (
required: {value: true, message: 'Confirmation requise'}, watch('password') === value ||
validate: (value: string) => { 'Les mots de passe ne correspondent pas'
return ( ),
watch('password') === value || })}/>
'Les mots de passe ne correspondent pas'
);
},
})}
/>
<FormErrorMessage>{errors.confirmPassword?.message as string}</FormErrorMessage> <FormErrorMessage>{errors.confirmPassword?.message as string}</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={{base: '100%', md: 'unset'}}> <Button onClick={() => router.push('/')}
w={{base: '100%', md: 'unset'}}>
Retour Retour
</Button> </Button>
<Button <Button isLoading={isSubmitting} w={{base: '100%', md: 'unset'}}
isLoading={isSubmitting} type="submit">
w={{base: '100%', md: 'unset'}}
colorScheme="purple"
type="submit"
>
Je m&apos;inscris Je m&apos;inscris
</Button> </Button>
</Flex> </Flex>
@@ -8,19 +8,20 @@ import {
Box, Box,
CardHeader, CardHeader,
useToast, useToast,
} from "@chakra-ui/react"; } from '@chakra-ui/react';
import Carousel from "../../../Carousel"; import Carousel from '../../../Carousel';
import { BiHeart } from "react-icons/bi"; import {BiHeart} from 'react-icons/bi';
import { RxCross1 } from "react-icons/rx"; import {RxCross1} from 'react-icons/rx';
import { useMutation, useQuery } from "@tanstack/react-query"; import {useMutation, useQuery} from '@tanstack/react-query';
import PassionTagList from "@/components/layout/dashboard/card_user/PassionTagList"; import PassionTagList
import { useState } from "react"; from '@/components/layout/dashboard/card_user/PassionTagList';
import SearchFailCard from "./SearchFailCard"; import {useState} from 'react';
import LoadingPage from "@/components/LoadingPage"; import SearchFailCard from './SearchFailCard';
import LoadingPage from '@/components/LoadingPage';
export default function CardUser({ users, loggedUser, setMatch }) { export default function CardUser({users, loggedUser, setMatch}) {
const toast = useToast({ const toast = useToast({
position: "top", position: 'top',
duration: 2000, duration: 2000,
isClosable: true, isClosable: true,
}); });
@@ -29,8 +30,8 @@ export default function CardUser({ users, loggedUser, setMatch }) {
const likeMutation = useMutation({ const likeMutation = useMutation({
mutationFn: async (id) => { mutationFn: async (id) => {
const likePostOptions = { const likePostOptions = {
method: "POST", method: 'POST',
headers: { "Content-Type": "application/json" }, headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ body: JSON.stringify({
idUser: loggedUser.id, idUser: loggedUser.id,
idUserLiked: id, idUserLiked: id,
@@ -48,9 +49,9 @@ export default function CardUser({ users, loggedUser, setMatch }) {
onSuccess: (data) => { onSuccess: (data) => {
if (data.error) { if (data.error) {
toast({ toast({
title: "Erreur", title: 'Erreur',
description: "Une erreur est survenue", description: 'Une erreur est survenue',
status: "error", status: 'error',
}); });
return; return;
} }
@@ -59,9 +60,9 @@ export default function CardUser({ users, loggedUser, setMatch }) {
} }
setListUsers(listUsers.slice(1)); setListUsers(listUsers.slice(1));
toast({ toast({
title: "J'aime", title: 'J\'aime',
description: "Votre action a bien été prise en compte", description: 'Votre action a bien été prise en compte',
status: "success", status: 'success',
}); });
//tester si match et afficher un truc //tester si match et afficher un truc
@@ -71,8 +72,8 @@ export default function CardUser({ users, loggedUser, setMatch }) {
const dislikeMutation = useMutation({ const dislikeMutation = useMutation({
mutationFn: async (id) => { mutationFn: async (id) => {
const dislikePostOptions = { const dislikePostOptions = {
method: "POST", method: 'POST',
headers: { "Content-Type": "application/json" }, headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ body: JSON.stringify({
idUser: loggedUser.id, idUser: loggedUser.id,
idUserDisliked: id, idUserDisliked: id,
@@ -90,18 +91,18 @@ export default function CardUser({ users, loggedUser, setMatch }) {
onSuccess: (data) => { onSuccess: (data) => {
if (data.error) { if (data.error) {
toast({ toast({
title: "Erreur", title: 'Erreur',
description: "Une erreur est survenue", description: 'Une erreur est survenue',
status: "error", status: 'error',
duration: 2000, duration: 2000,
}); });
return; return;
} }
setListUsers(listUsers.slice(1)); setListUsers(listUsers.slice(1));
toast({ toast({
title: "J'aime pas", title: 'J\'aime pas',
description: "Votre action a bien été prise en compte", description: 'Votre action a bien été prise en compte',
status: "success", status: 'success',
duration: 2000, duration: 2000,
}); });
}, },
@@ -113,93 +114,72 @@ export default function CardUser({ users, loggedUser, setMatch }) {
data: listPassions, data: listPassions,
error: passionError, error: passionError,
} = useQuery({ } = useQuery({
queryKey: ["passions"], queryKey: ['passions'],
queryFn: async () => { queryFn: async () => {
return fetch(`/api/passions/`) return fetch(`/api/passions/`)
.then((res) => res.json()) .then(res => res.json())
.catch((err) => { .catch(err => err);
return err;
});
}, },
}); });
const formateDateToAge = (birthdate: Date) => { const formateDateToAge = (birthdate: Date) => {
const date = new Date(birthdate); const date = new Date(birthdate);
var ageDifMs = Date.now() - date.getTime(); const ageDifMs = Date.now() - date.getTime();
var ageDate = new Date(ageDifMs); // miliseconds from epoch const ageDate = new Date(ageDifMs); // miliseconds from epoch
return Math.abs(ageDate.getUTCFullYear() - 1970); return Math.abs(ageDate.getUTCFullYear() - 1970);
}; };
if (listUsers.length === 0) { if (listUsers.length === 0) return <SearchFailCard/>;
return <SearchFailCard />; if (likeMutation.isLoading ||
} dislikeMutation.isLoading) return <LoadingPage/>;
if (likeMutation.isLoading || dislikeMutation.isLoading) {
return <LoadingPage />;
}
return ( return (
<Card w={"100%"} h={"100%"} borderRadius={"1rem"} overflow={"hidden"}> <Card w={'100%'} h={'100%'} borderRadius={'1rem'} overflow={'hidden'}>
<CardHeader> <CardHeader>
<Carousel borderRadius={"1rem"} images={listUsers?.[0].images} /> <Carousel borderRadius={'1rem'} images={listUsers?.[0].images}/>
</CardHeader> </CardHeader>
<CardBody> <CardBody>
<Flex justify={"space-between"} mb={"20px"}> <Flex justify={'space-between'} mb={'20px'}>
<Heading fontSize={"1.5rem"} fontWeight={"bold"} flexBasis={"70%"}> <Heading fontSize={'1.5rem'} fontWeight={'bold'} flexBasis={'70%'}>
{listUsers[0].firstName} {listUsers[0].lastName},{" "} {listUsers[0].firstName} {listUsers[0].lastName},{' '}
{formateDateToAge(listUsers[0].birthdate)} ans {formateDateToAge(listUsers[0].birthdate)} ans
</Heading> </Heading>
<Flex gap={1}> <Flex gap={1}>
<IconButton <IconButton icon={<BiHeart/>} aria-label="like"
aria-label="like" borderRadius={'1rem'}
borderRadius={"1rem"} onClick={() => likeMutation.mutate(listUsers[0].id)
colorScheme={"purple"} }/>
onClick={() => { <IconButton icon={<RxCross1/>} aria-label="dislike"
likeMutation.mutate(listUsers[0].id); borderRadius={'1rem'} variant={'outline'}
}} onClick={() => dislikeMutation.mutate(
> listUsers[0].id)}/>
<BiHeart /> </Flex>
</IconButton>
<IconButton
aria-label="dislike"
borderRadius={"1rem"}
colorScheme={"purple"}
variant={"outline"}
onClick={() => {
dislikeMutation.mutate(listUsers[0].id);
}}
>
<RxCross1 />
</IconButton>
</Flex> </Flex>
</Flex>
<Box mb={"20px"}> <Box mb={'20px'}>
<Heading size={"sm"} fontWeight={"bold"} mb="0.5rem"> <Heading size={'sm'} fontWeight={'bold'} mb="0.5rem">
A propos : A propos :
</Heading> </Heading>
<Text as="i">&quot;{listUsers[0].bio}&quot;</Text> <Text as="i">&quot;{listUsers[0].bio}&quot;</Text>
</Box> </Box>
<Box> <Box>
<Heading size={"sm"} fontWeight={"bold"}> <Heading size={'sm'} fontWeight={'bold'}>
Passions : Passions :
</Heading> </Heading>
{passionLoading ? ( {passionLoading
<Text>Chargement des passions...</Text> ? <Text>Chargement des passions...</Text>
) : passionIsError ? ( : passionIsError
<Text>Erreur lors du chargement des passions</Text> ? <Text>Erreur lors du chargement des passions</Text>
) : ( :
<PassionTagList <PassionTagList passions={listUsers[0].PassionID}
passions={listUsers[0].PassionID} userPassions={loggedUser.PassionID}
userPassions={loggedUser.PassionID} listPassions={listPassions}/>
listPassions={listPassions} }
/> </Box>
)} </CardBody>
</Box> </Card>
</CardBody>
</Card>
); );
} }
@@ -1,4 +1,4 @@
import { Badge, Flex, Tag } from "@chakra-ui/react"; import {Flex, Tag} from '@chakra-ui/react';
type Props = { type Props = {
passions: string[]; passions: string[];
@@ -6,22 +6,15 @@ type Props = {
listPassions?: Object[]; listPassions?: Object[];
}; };
export default function PassionTagList({ const PassionTagList = ({passions, userPassions = [], listPassions}: Props) => (
passions, <Flex gap={'0.5rem'} mt={'1vh'} flexWrap="wrap">
userPassions = [],
listPassions,
}: Props) {
return (
<Flex gap={"0.5rem"} mt={"1vh"} flexWrap="wrap">
{passions.map((passionID, index) => ( {passions.map((passionID, index) => (
<Tag <Tag variant={userPassions.includes(passionID) ? 'subtle' : 'outline'}
key={index} key={index}>
variant={userPassions.includes(passionID) ? "subtle" : "outline"} {listPassions?.find((passion) => passion.id === passionID)?.name}
colorScheme={"purple"} </Tag>
>
{listPassions?.find((passion) => passion.id === passionID)?.name}
</Tag>
))} ))}
</Flex> </Flex>
); );
}
export default PassionTagList;
@@ -1,8 +1,7 @@
import { Card, Text, CardBody, CardHeader } from "@chakra-ui/react"; import {Card, Text, CardBody, CardHeader} from '@chakra-ui/react';
export default function SearchFailCard(props) { const SearchFailCard = () => (
return ( <Card w={'100%'} h={'100%'} borderRadius={'1rem'} overflow={'hidden'}>
<Card w={"100%"} h={"100%"} borderRadius={"1rem"} overflow={"hidden"}>
<CardHeader> Aucun profil correspondant à vos critères </CardHeader> <CardHeader> Aucun profil correspondant à vos critères </CardHeader>
<CardBody> <CardBody>
<Text as="b"> <Text as="b">
@@ -10,5 +9,6 @@ export default function SearchFailCard(props) {
</Text> </Text>
</CardBody> </CardBody>
</Card> </Card>
); );
}
export default SearchFailCard;
@@ -1,93 +1,67 @@
import {Card, Flex, Box, Image, Text, Spacer, Divider} from "@chakra-ui/react"; import {Card, Flex, Box, Image, Text, Spacer, Divider} from '@chakra-ui/react';
import { useRouter } from "next/router"; import {useRouter} from 'next/router';
import { AiFillMessage } from "react-icons/ai"; import {AiFillMessage} from 'react-icons/ai';
import { BsFillPersonFill } from "react-icons/bs"; import {BsFillPersonFill} from 'react-icons/bs';
import { BiLogOut } from "react-icons/bi"; import {BiLogOut} from 'react-icons/bi';
import { FaMapMarkedAlt } from "react-icons/fa"; import {FaMapMarkedAlt} from 'react-icons/fa';
import LeftPanelButton from "@/components/layout/dashboard/left_panel/LeftPanelButton"; import LeftPanelButton
import { signOut } from "next-auth/react"; from '@/components/layout/dashboard/left_panel/LeftPanelButton';
import {signOut} from 'next-auth/react';
export default function LeftPanel(props) { export default function LeftPanel(props) {
const router = useRouter(); const router = useRouter();
const { user } = props; const {user} = props;
const formateDate = (dateString) => { const formateDate = (dateString) => {
var options = { year: "numeric", month: "long", day: "numeric" }; const options = {year: 'numeric', month: 'long', day: 'numeric'};
return new Date(dateString).toLocaleDateString([], options); return new Date(dateString).toLocaleDateString([], options);
}; };
return ( return (
<Card <Card width={'20vw'} height={'100%'} borderRadius={0} padding={'0px'}
width={"20vw"} bg={'#faf9ff'}>
height={"100%"} <Flex direction={'column'} height={'100%'} margin={'10%'}>
borderRadius={0} <Box>
padding={"0px"} <Image src={user.images[0] ?? '/blank_profile_picture.webp'}
bg={"#faf9ff"} objectFit={'contain'} borderRadius={'1rem'}/>
> <Box mt={'2rem'}>
<Flex direction={"column"} height={"100%"} margin={"10%"}> <Flex align={'center'} justifyContent="space-between" mb={'1rem'}>
<Box> <Text fontSize={'1.5rem'} fontWeight={'bold'}>
{user.images.length === 0 ? ( {user.firstName} {user.lastName}
<Image src={"/blank_profile_picture.webp"} borderRadius={"1rem"} /> </Text>
) : ( <Text fontSize={'1rem'} fontWeight={'bold'}>
<Image {formateDate(user.birthdate)}
src={user.images[0]} </Text>
borderRadius={"1rem"} </Flex>
objectFit={"contain"} <Divider mb={'1rem'}/>
width={"100%"} <Text as="i" fontWeight={'bold'}>&quot;{user.bio}&quot;</Text>
/> </Box>
)}
<Box mt={"2rem"}>
<Flex align={"center"} justifyContent="space-between" mb={"1rem"}>
<Text fontSize={"1.5rem"} fontWeight={"bold"}>
{user.firstName} {user.lastName}
</Text>
<Text fontSize={"1rem"} fontWeight={"bold"}>
{formateDate(user.birthdate)}
</Text>
</Flex>
<Divider colorScheme={"purple"} mb={"1rem"} />
<Text as="i" fontWeight={"bold"}>
&quot;{user.bio}&quot;
</Text>
</Box> </Box>
</Box> <Spacer/>
<Spacer /> <Flex gap={2} direction={'column'} mt={'1vh'}
<Flex spacing={3.5} alignContent={'bottom'}>
gap={2} <LeftPanelButton leftIcon={<FaMapMarkedAlt/>}
direction={"column"} onClickHandler={() => router.push('/map')}>
mt={"1vh"} Carte
spacing={3.5} </LeftPanelButton>
alignContent={"bottom"}
> <LeftPanelButton leftIcon={<AiFillMessage/>}
<LeftPanelButton onClickHandler={() => router.push('/dashboard')}>
leftIcon={<FaMapMarkedAlt />} Messages
onClickHandler={() => router.push("/map")} </LeftPanelButton>
>
Carte <LeftPanelButton leftIcon={<BsFillPersonFill/>}
</LeftPanelButton> onClickHandler={() => router.push('/profile')}>
<LeftPanelButton Profil
leftIcon={<AiFillMessage />} </LeftPanelButton>
onClickHandler={() => router.push("/dashboard")} <LeftPanelButton variant={'outline'} leftIcon={<BiLogOut/>}
> onClickHandler={() => signOut({callbackUrl: '/'})}>
Messages Déconnexion
</LeftPanelButton> </LeftPanelButton>
<LeftPanelButton </Flex>
leftIcon={<BsFillPersonFill />}
onClickHandler={() => router.push("/profile")}
>
Profile
</LeftPanelButton>
<LeftPanelButton
variant={"outline"}
leftIcon={<BiLogOut />}
onClickHandler={() => signOut({ callbackUrl: "/" })}
>
Deconnexion
</LeftPanelButton>
</Flex> </Flex>
</Flex> </Card>
</Card>
); );
} }
@@ -2,25 +2,20 @@ import {Button, ResponsiveValue} from '@chakra-ui/react';
import {ReactJSXElement} from '@emotion/react/types/jsx-namespace'; import {ReactJSXElement} from '@emotion/react/types/jsx-namespace';
type Props = { type Props = {
children?: ReactJSXElement children?: ReactJSXElement | string
onClickHandler: () => void onClickHandler: () => void
leftIcon?: ReactJSXElement, leftIcon?: ReactJSXElement,
variant?: ResponsiveValue<'link' | 'outline' | string | 'ghost' | 'solid' | 'unstyled'> variant?: ResponsiveValue<'link' | 'outline' | string | 'ghost' | 'solid' | 'unstyled'>
} }
export default function LeftPanelButton(props: Props) { export default function LeftPanelButton(props: Props) {
const {children, onClickHandler, leftIcon: icon, variant = "ghost"} = props; const {children, onClickHandler, leftIcon: icon, variant = 'ghost'} = props;
return ( return (
<Button <Button variant={variant} width={'100%'} justifyContent={'left'}
colorScheme={'purple'} onClick={onClickHandler} leftIcon={icon} fontWeight={'bold'}
variant={variant} fontSize={'1.2rem'}>
width={'100%'} {children}
justifyContent={'left'} </Button>
onClick={onClickHandler}
leftIcon={icon}
fontWeight={'bold'}
fontSize={'1.2rem'}
>{children}</Button>
); );
} }
+9 -17
View File
@@ -13,37 +13,29 @@ export default function HeroBanner() {
const router = useRouter(); const router = useRouter();
return ( return (
<Flex <Flex bg={'#FAF9FF'} p={150} minH={{base: 'unset', md: '100vh'}}
bg={'#FAF9FF'} align={'center'} justify={'center'}>
p={150}
minH={{base: 'unset', md: '100vh'}}
align={'center'}
justify={'center'}
>
<Flex gap={10}> <Flex gap={10}>
<Flex justify={'center'} direction={'column'} flexBasis={'100%'}> <Flex justify={'center'} direction={'column'} flexBasis={'100%'}>
<Heading mb={'2.5rem'}>Prêt(e) à trouver votre âme sœur ?</Heading> <Heading mb={'2.5rem'}>Prêt(e) à trouver votre âme sœur ?</Heading>
<Text mb={'2rem'} maxW={{md: "500px"}}> <Text mb={'2rem'} maxW={{md: '500px'}}>
Notre site de rencontre vous offre la possibilité de rencontrer Notre site de rencontre vous offre la possibilité de rencontrer
des personnes intéressantes et de trouver l&apos;amour. Inscrivez-vous des personnes intéressantes et de trouver l&apos;amour.
Inscrivez-vous
dès maintenant pour découvrir toutes nos fonctionnalités ! dès maintenant pour découvrir toutes nos fonctionnalités !
</Text> </Text>
<ButtonGroup> <ButtonGroup>
<Button colorScheme={'purple'} <Button onClick={() => router.push('/register')}>
onClick={() => router.push('/register')}>
S&apos;inscrire S&apos;inscrire
</Button> </Button>
</ButtonGroup> </ButtonGroup>
</Flex> </Flex>
<Box flexBasis={'100%'} display={{base: "none", md: "block"}}> <Box flexBasis={'100%'} display={{base: 'none', md: 'block'}}>
<Image borderRadius={20} <Image borderRadius={20} boxShadow={'lg'} src={'/couple_img.jpg'}
boxShadow={'lg'} alt="happy couple"/>
src={'/couple_img.jpg'}
alt="happy couple"
/>
</Box> </Box>
</Flex> </Flex>
</Flex> </Flex>
@@ -4,8 +4,7 @@ export default function PresentationSection() {
return ( return (
<Flex justify={{base: 'center', md: 'right'}} alignItems={'center'} <Flex justify={{base: 'center', md: 'right'}} alignItems={'center'}
bg={{base: '#805AD5'}} bgImage={'/couple_funny.png'} bg={{base: '#805AD5'}} bgImage={'/couple_funny.png'}
bgSize={'cover'} bgSize={'cover'} bgPos={'center'}
bgPos={'center'}
bgAttachment={'fixed'} minH={{base: 'initial', md: '80vh'}}> bgAttachment={'fixed'} minH={{base: 'initial', md: '80vh'}}>
<Box p={50} width={{base: '100%', md: '50%'}} maxW={{md: "500px"}} bg={'#805AD5'}> <Box p={50} width={{base: '100%', md: '50%'}} maxW={{md: "500px"}} bg={'#805AD5'}>
+45 -54
View File
@@ -1,69 +1,60 @@
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet"; import {MapContainer, TileLayer, Marker, Popup} from 'react-leaflet';
import { Flex, Text, useToast } from "@chakra-ui/react"; import {Flex, Text, useToast} from '@chakra-ui/react';
import MarkerClusterGroup from "@christopherpickering/react-leaflet-markercluster"; import MarkerClusterGroup
from '@christopherpickering/react-leaflet-markercluster';
import "@christopherpickering/react-leaflet-markercluster/dist/styles.min.css"; import '@christopherpickering/react-leaflet-markercluster/dist/styles.min.css';
import MarkerBar from "./MarkerBar"; import MarkerBar from './MarkerBar';
import "leaflet/dist/leaflet.css"; import 'leaflet/dist/leaflet.css';
export default function MapComponent(props: any) { export default function MapComponent(props: any) {
const { location, listBars } = props; const {location, listBars} = props;
const toast = useToast({ position: "bottom" }); const toast = useToast({position: 'bottom'});
const idToastError = "error_location"; const idToastError = 'error_location';
const userIcon = new L.Icon({ const userIcon = new L.Icon({
iconUrl: "logo.svg", iconUrl: 'logo.svg',
iconSize: [35, 35], iconSize: [35, 35],
}); });
return ( return (
<> <>
{location[0] === null || location[1] === null ? ( {location[0] === null || location[1] === null ? (
<> <>
{!toast.isActive(idToastError) {!toast.isActive(idToastError) &&
? toast({ toast({
title: "Erreur", title: 'Erreur',
id: idToastError, id: idToastError,
description: "Veuillez autoriser la localisation", description: 'Veuillez autoriser la localisation',
status: "error", status: 'error',
isClosable: false, isClosable: false,
duration: 100000, duration: 100000,
}) })}
: null} <Text color="purple.500" fontSize="2xl" fontWeight="bold">
<Text color="purple.500" fontSize="2xl" fontWeight="bold"> Veuillez autoriser la localisation
Veuillez autoriser la localisation </Text>
</Text> </>
</> ) : (
) : ( <Flex>
<Flex> <MapContainer center={location} zoom={13} minZoom={8}
<MapContainer zoomControl={false}
center={location} style={{width: '100vw', height: '100vw'}}>
zoom={13} <TileLayer
minZoom={8} url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
zoomControl={false} attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
style={{ />
width: "100vw", {/* Pour le user */}
height: "100vw", <Marker icon={userIcon} position={location}></Marker>
}}
>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
/>
{/* Pour le user */}
<Marker icon={userIcon} position={location}></Marker>
<MarkerClusterGroup chunkedLoading showCoverageOnHover={false}> <MarkerClusterGroup chunkedLoading showCoverageOnHover={false}>
{listBars.map((bar, index) => { {listBars.map((bar: any, index: number) => <MarkerBar key={index} bar={bar}/>)}
return <MarkerBar key={index} bar={bar} />; </MarkerClusterGroup>
})} </MapContainer>
</MarkerClusterGroup> </Flex>
</MapContainer> )}
</Flex> </>
)}
</>
); );
} }
+2 -4
View File
@@ -1,6 +1,6 @@
import { Marker, Popup } from "react-leaflet"; import { Marker, Popup } from "react-leaflet";
import "leaflet/dist/leaflet.css"; import "leaflet/dist/leaflet.css";
import { Box, Button, Card, CardBody, Text } from "@chakra-ui/react"; import { Box, Button, Text } from "@chakra-ui/react";
export default function MarkerBar(props) { export default function MarkerBar(props) {
const { bar } = props; const { bar } = props;
@@ -23,9 +23,7 @@ export default function MarkerBar(props) {
<Text fontSize={"1rem"} fontWeight={"bold"} align={"center"}> <Text fontSize={"1rem"} fontWeight={"bold"} align={"center"}>
{bar.name || '"Nom du bar"'} {bar.name || '"Nom du bar"'}
</Text> </Text>
<Button colorScheme={"purple"} variant={"outline"}> <Button variant={"outline"}>Inviter un match</Button>
Inviter un match
</Button>
</Box> </Box>
</Popup> </Popup>
</Marker> </Marker>
@@ -4,43 +4,27 @@ import {
Tag, Tag,
TagLeftIcon, TagLeftIcon,
TagLabel, TagLabel,
} from "@chakra-ui/react"; } from '@chakra-ui/react';
import { IoAdd, IoRemove } from "react-icons/io5"; import {IoAdd, IoRemove} from 'react-icons/io5';
export default function CustomCheckbox(props) { export default function CustomCheckbox(props) {
const { text } = props; const {text} = props;
const { state, getInputProps, getCheckboxProps, getLabelProps, htmlProps } = const {state, getInputProps, getCheckboxProps, getLabelProps, htmlProps} =
useCheckbox(props); useCheckbox(props);
return ( return (
<chakra.label <chakra.label variant={state.isChecked ? 'solid' : 'outline'}
// colorScheme={"purple"} cursor="pointer" {...htmlProps}>
variant={state.isChecked ? "solid" : "outline"} <input {...getInputProps()} hidden/>
cursor="pointer"
{...htmlProps} <Tag {...getCheckboxProps()} variant={state.isChecked ? "solid" : "outline"} {...getLabelProps()}>
> {state.isChecked
<input {...getInputProps()} hidden /> ? <TagLeftIcon as={IoRemove}/>
{state.isChecked ? ( : <TagLeftIcon as={IoAdd}/>
<Tag }
{...getCheckboxProps()}
colorScheme={"purple"}
{...getLabelProps()}
>
<TagLeftIcon as={IoRemove} />
<TagLabel>{text}</TagLabel> <TagLabel>{text}</TagLabel>
</Tag> </Tag>
) : ( </chakra.label>
<Tag
{...getCheckboxProps()}
colorScheme={"purple"}
variant={"outline"}
{...getLabelProps()}
>
<TagLeftIcon as={IoAdd} />
<TagLabel>{text}</TagLabel>
</Tag>
)}
</chakra.label>
); );
} }
@@ -6,52 +6,40 @@ import {
FormErrorMessage, FormErrorMessage,
FormHelperText, FormHelperText,
FormLabel, FormLabel,
} from "@chakra-ui/react"; } from '@chakra-ui/react';
import { Controller, ControllerProps } from "react-hook-form"; import {Controller, ControllerProps} from 'react-hook-form';
type CustomEditableProps = { type CustomEditableProps = {
label: string; label: string;
isDisabled?: boolean; isDisabled?: boolean;
helperText?: string; helperText?: string;
} & Omit<ControllerProps, "render">; } & Omit<ControllerProps, 'render'>;
export default function CustomEditable(props: CustomEditableProps) { export default function CustomEditable(props: CustomEditableProps) {
const { const {
defaultValue, defaultValue,
label, label,
name, name,
helperText = "", helperText = '',
isDisabled = false, isDisabled = false,
...controllerProps ...controllerProps
} = props; } = props;
return ( return (
<Controller <Controller{...controllerProps} name={name} defaultValue={defaultValue}
{...controllerProps} render={({field, fieldState: {invalid, error}}) => (
name={name} <FormControl id={name} isInvalid={invalid}>
defaultValue={defaultValue} <FormLabel as="legend" htmlFor={name}>{label}</FormLabel>
render={({ field, fieldState: { invalid, error } }) => { <Editable{...field} id={name} fontWeight="bold"
return ( isDisabled={isDisabled}
<FormControl id={name} isInvalid={invalid}> placeholder={'Non renseigné'}
<FormLabel as="legend" htmlFor={name}> color={isDisabled ? 'gray.500' : undefined}>
{label} <EditablePreview/>
</FormLabel> <EditableInput/>
<Editable </Editable>
{...field} <FormHelperText>{helperText}</FormHelperText>
id={name} <FormErrorMessage as="b">{error?.message}</FormErrorMessage>
fontWeight="bold" </FormControl>
isDisabled={isDisabled} )}/>
placeholder={"Non renseigné"}
color={isDisabled ? "gray.500" : undefined}
>
<EditablePreview />
<EditableInput />
</Editable>
<FormHelperText>{helperText}</FormHelperText>
<FormErrorMessage as="b">{error?.message}</FormErrorMessage>
</FormControl>
);
}}
/>
); );
} }
@@ -5,13 +5,13 @@ import {
FormControl, FormControl,
FormErrorMessage, FormErrorMessage,
FormLabel, FormLabel,
} from "@chakra-ui/react"; } from '@chakra-ui/react';
import { Controller, ControllerProps } from "react-hook-form"; import {Controller, ControllerProps} from 'react-hook-form';
type CustomEditableProps = { type CustomEditableProps = {
label: string; label: string;
isDisabled?: boolean; isDisabled?: boolean;
} & Omit<ControllerProps, "render">; } & Omit<ControllerProps, 'render'>;
export default function CustomEditableArea(props: CustomEditableProps) { export default function CustomEditableArea(props: CustomEditableProps) {
const { const {
@@ -23,32 +23,19 @@ export default function CustomEditableArea(props: CustomEditableProps) {
} = props; } = props;
return ( return (
<Controller <Controller{...controllerProps} name={name} defaultValue={defaultValue}
{...controllerProps} render={({field, fieldState: {invalid, error}}) => (
name={name} <FormControl id={name} isInvalid={invalid}>
defaultValue={defaultValue} <FormLabel as="legend" htmlFor={name}>{label}</FormLabel>
render={({ field, fieldState: { invalid, error } }) => { <Editable{...field} id={name} fontWeight="bold"
return ( width={'100%'} isDisabled={isDisabled}
<FormControl id={name} isInvalid={invalid}> placeholder={'Non renseigné'}
<FormLabel as="legend" htmlFor={name}> color={isDisabled ? 'gray.500' : undefined}>
{label} <EditablePreview/>
</FormLabel> <EditableTextarea/>
<Editable </Editable>
{...field} <FormErrorMessage as="b">{error?.message}</FormErrorMessage>
id={name} </FormControl>
fontWeight="bold" )}/>
width={"100%"}
isDisabled={isDisabled}
placeholder={"Non renseigné"}
color={isDisabled ? "gray.500" : undefined}
>
<EditablePreview />
<EditableTextarea />
</Editable>
<FormErrorMessage as="b">{error?.message}</FormErrorMessage>
</FormControl>
);
}}
/>
); );
} }
@@ -5,7 +5,7 @@ import {
FormHelperText, FormHelperText,
FormLabel, FormLabel,
UseEditableProps, UseEditableProps,
} from "@chakra-ui/react"; } from '@chakra-ui/react';
type CustomEditableProps = { type CustomEditableProps = {
id: string; id: string;
@@ -14,23 +14,16 @@ type CustomEditableProps = {
} & UseEditableProps; } & UseEditableProps;
export default function CustomEditable(props: CustomEditableProps) { export default function CustomEditable(props: CustomEditableProps) {
const { label, id, helperText = "", ...UseEditableProps } = props; const {label, id, helperText = '', ...UseEditableProps} = props;
return ( return (
<FormControl> <FormControl>
<FormLabel as="legend" htmlFor={id}> <FormLabel as="legend" htmlFor={id}>{label}</FormLabel>
{label} <Editable{...UseEditableProps} fontWeight="bold" isDisabled={true}
</FormLabel> placeholder={'Non renseigné'} color={'gray.500'}>
<Editable <EditablePreview/>
{...UseEditableProps} </Editable>
fontWeight="bold" <FormHelperText>{helperText}</FormHelperText>
isDisabled={true} </FormControl>
placeholder={"Non renseigné"}
color={"gray.500"}
>
<EditablePreview />
</Editable>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
); );
} }
@@ -1,60 +1,35 @@
import { FormLabel, HStack, Radio, RadioGroup } from "@chakra-ui/react"; import {FormLabel, HStack, Radio, RadioGroup} from '@chakra-ui/react';
import { Gender } from "@prisma/client"; import {Gender} from '@prisma/client';
import { Controller, ControllerProps } from "react-hook-form"; import {Controller, ControllerProps} from 'react-hook-form';
type CustomRadioGenderProps = { type CustomRadioGenderProps = {
label: string; label: string;
} & Omit<ControllerProps, "render">; } & Omit<ControllerProps, 'render'>;
export default function CustomRadioGender(props: CustomRadioGenderProps) { export default function CustomRadioGender(props: CustomRadioGenderProps) {
const { defaultValue, label, name, ...controllerProps } = props; const {defaultValue, label, name, ...controllerProps} = props;
const getTextGender = (gender: string) => { const genders = [
switch (gender) { {label: 'Homme', gender: Gender.MALE},
case Gender.MALE: {label: 'Femme', gender: Gender.FEMALE},
return "Homme"; {label: 'Autre', gender: Gender.OTHER},
case Gender.FEMALE: {label: 'Non renseigné', gender: Gender.UNKNOWN},
return "Femme"; ];
case Gender.OTHER:
return "Autre";
case Gender.UNKNOWN:
return "Non renseigné";
}
};
return ( return (
<> <>
<FormLabel as={"legend"} htmlFor={name}> <FormLabel as={'legend'} htmlFor={name}>{label}</FormLabel>
{label} <Controller{...controllerProps} name={name}
</FormLabel> render={({field}) => (
<Controller <RadioGroup{...field} id={name} as="b"
{...controllerProps} defaultValue={defaultValue}>
name={name} <HStack spacing={'0.5rem'}>
render={({ field }) => { {genders.map(({label, gender}, index) => (
return ( <Radio key={index} value={gender}>{label}</Radio>
<RadioGroup ))}
{...field} </HStack>
colorScheme={"purple"} </RadioGroup>
id={name} )}/>
as="b" </>
defaultValue={defaultValue}
>
<HStack spacing={"0.5rem"}>
<Radio value={Gender.MALE}>{getTextGender(Gender.MALE)}</Radio>
<Radio value={Gender.FEMALE}>
{getTextGender(Gender.FEMALE)}
</Radio>
<Radio value={Gender.OTHER}>
{getTextGender(Gender.OTHER)}
</Radio>
<Radio value={Gender.UNKNOWN}>
{getTextGender(Gender.UNKNOWN)}
</Radio>
</HStack>
</RadioGroup>
);
}}
/>
</>
); );
} }
@@ -6,10 +6,10 @@ import {
RangeSliderThumb, RangeSliderThumb,
RangeSliderTrack, RangeSliderTrack,
Tooltip, Tooltip,
} from "@chakra-ui/react"; } from '@chakra-ui/react';
import { Controller, ControllerProps } from "react-hook-form"; import {Controller, ControllerProps} from 'react-hook-form';
import { FaGreaterThan, FaLessThan } from "react-icons/fa"; import {FaGreaterThan, FaLessThan} from 'react-icons/fa';
type CustomRangeSliderProps = { type CustomRangeSliderProps = {
label: string; label: string;
@@ -19,7 +19,7 @@ type CustomRangeSliderProps = {
showTooltipAge: boolean; showTooltipAge: boolean;
setShowTooltipAge: React.Dispatch<React.SetStateAction<boolean>>; setShowTooltipAge: React.Dispatch<React.SetStateAction<boolean>>;
name: string; name: string;
} & Omit<ControllerProps, "render">; } & Omit<ControllerProps, 'render'>;
export default function CustomRangeSlider(props: CustomRangeSliderProps) { export default function CustomRangeSlider(props: CustomRangeSliderProps) {
const { const {
@@ -34,60 +34,40 @@ export default function CustomRangeSlider(props: CustomRangeSliderProps) {
} = props; } = props;
return ( return (
<> <>
<FormLabel as={"legend"} htmlFor={name}> <FormLabel as={'legend'} htmlFor={name}>{label}</FormLabel>
{label} <Controller{...controllerProps} name={name}
</FormLabel> render={({field: {onChange}}) => (
<Controller <RangeSlider aria-label={['min', 'max']} min={18}
{...controllerProps} max={99}
name={name} id={name} color={'pink.500'}
render={({ field: { onChange } }) => ( defaultValue={defaultValue}
<RangeSlider onChange={(v: [number, number]) => {
aria-label={["min", "max"]} setSliderAgeValue(v);
colorScheme={"purple"} onChange(v);
min={18} }}
max={99} onMouseEnter={() => setShowTooltipAge(true)}
id={name} onMouseLeave={() => setShowTooltipAge(false)}>
color={"pink.500"} <RangeSliderTrack>
defaultValue={defaultValue} <RangeSliderFilledTrack bgColor={'purple.500'}/>
onChange={(v: [number, number]) => { </RangeSliderTrack>
setSliderAgeValue(v); <Tooltip hasArrow bg="purple.500" color="white"
onChange(v); placement="top" isOpen={showTooltipAge}
}} label={`${sliderAgeValue[0]}`}>
onMouseEnter={() => setShowTooltipAge(true)} <RangeSliderThumb boxSize={6} index={0}>
onMouseLeave={() => setShowTooltipAge(false)} <Box color={'purple.500'} as={FaLessThan}/>
> </RangeSliderThumb>
<RangeSliderTrack> </Tooltip>
<RangeSliderFilledTrack bgColor={"purple.500"} />
</RangeSliderTrack>
<Tooltip
hasArrow
bg="purple.500"
color="white"
placement="top"
isOpen={showTooltipAge}
label={`${sliderAgeValue[0]}`}
>
<RangeSliderThumb boxSize={6} index={0}>
<Box color={"purple.500"} as={FaLessThan} />
</RangeSliderThumb>
</Tooltip>
<Tooltip <Tooltip hasArrow bg="purple.500" color="white"
hasArrow placement="top" isOpen={showTooltipAge}
bg="purple.500" label={`${sliderAgeValue[1]}`}>
color="white" <RangeSliderThumb boxSize={6} index={1}>
placement="top" <Box color={'purple.500'} as={FaGreaterThan}/>
isOpen={showTooltipAge} </RangeSliderThumb>
label={`${sliderAgeValue[1]}`} </Tooltip>
> </RangeSlider>
<RangeSliderThumb boxSize={6} index={1}> )}/>
<Box color={"purple.500"} as={FaGreaterThan} /> </>
</RangeSliderThumb>
</Tooltip>
</RangeSlider>
)}
/>
</>
); );
} }
+30 -46
View File
@@ -6,10 +6,10 @@ import {
SliderThumb, SliderThumb,
SliderTrack, SliderTrack,
Tooltip, Tooltip,
} from "@chakra-ui/react"; } from '@chakra-ui/react';
import { Controller, ControllerProps } from "react-hook-form"; import {Controller, ControllerProps} from 'react-hook-form';
import { FaWalking } from "react-icons/fa"; import {FaWalking} from 'react-icons/fa';
type CustomSliderProps = { type CustomSliderProps = {
label: string; label: string;
@@ -19,7 +19,7 @@ type CustomSliderProps = {
showTooltipDistance: boolean; showTooltipDistance: boolean;
setShowTooltipDistance: React.Dispatch<React.SetStateAction<boolean>>; setShowTooltipDistance: React.Dispatch<React.SetStateAction<boolean>>;
name: string; name: string;
} & Omit<ControllerProps, "render">; } & Omit<ControllerProps, 'render'>;
export default function CustomSlider(props: CustomSliderProps) { export default function CustomSlider(props: CustomSliderProps) {
const { const {
@@ -34,47 +34,31 @@ export default function CustomSlider(props: CustomSliderProps) {
} = props; } = props;
return ( return (
<> <>
<FormLabel as={"legend"} htmlFor={name}> <FormLabel as={'legend'} htmlFor={name}>{label}</FormLabel>
{label} <Controller{...controllerProps} name={name}
</FormLabel> render={({field: {onChange}}) => (
<Controller <Slider aria-label={'distance'} min={20} max={250}
{...controllerProps} id={'distance'} color={'pink.500'}
name={name} defaultValue={defaultValue}
render={({ field: { onChange } }) => ( onChange={v => {
<Slider setSliderDistanceValue(v);
aria-label={"distance"} onChange(v);
colorScheme={"purple"} }}
min={20} onMouseEnter={() => setShowTooltipDistance(true)}
max={250} onMouseLeave={() => setShowTooltipDistance(false)}>
id={"distance"} <SliderTrack>
color={"pink.500"} <SliderFilledTrack bgColor={'purple.500'}/>
defaultValue={defaultValue} </SliderTrack>
onChange={(v) => { <Tooltip hasArrow bg="purple.500" color="white"
setSliderDistanceValue(v); placement="top" isOpen={showTooltipDistance}
onChange(v); label={`${sliderDistanceValue} km`}>
}} <SliderThumb boxSize={6}>
onMouseEnter={() => setShowTooltipDistance(true)} <Box color={'purple.500'} as={FaWalking}/>
onMouseLeave={() => setShowTooltipDistance(false)} </SliderThumb>
> </Tooltip>
<SliderTrack> </Slider>
<SliderFilledTrack bgColor={"purple.500"} /> )}/>
</SliderTrack> </>
<Tooltip
hasArrow
bg="purple.500"
color="white"
placement="top"
isOpen={showTooltipDistance}
label={`${sliderDistanceValue} km`}
>
<SliderThumb boxSize={6}>
<Box color={"purple.500"} as={FaWalking} />
</SliderThumb>
</Tooltip>
</Slider>
)}
/>
</>
); );
} }
@@ -13,104 +13,80 @@ import {
useCheckboxGroup, useCheckboxGroup,
useDisclosure, useDisclosure,
useToast, useToast,
} from "@chakra-ui/react"; } from '@chakra-ui/react';
import { useQueryClient } from "@tanstack/react-query"; import {useQueryClient} from '@tanstack/react-query';
import { RiEditBoxLine } from "react-icons/ri"; import {RiEditBoxLine} from 'react-icons/ri';
import CustomCheckbox from "./CustomCheckbox"; import CustomCheckbox from './CustomCheckbox';
import {Passion} from '@prisma/client';
export default function ModalChoosePassion(props) { export default function ModalChoosePassion(props) {
const { isOpen, onOpen, onClose } = useDisclosure(); const {isOpen, onOpen, onClose} = useDisclosure();
const { user, passions } = props; const {user, passions} = props;
const toast = useToast({ position: "top", isClosable: true }); const toast = useToast({position: 'top', isClosable: true});
const client = useQueryClient(); const client = useQueryClient();
const { value, getCheckboxProps } = useCheckboxGroup({ const {value, getCheckboxProps} = useCheckboxGroup({
defaultValue: user.PassionID, defaultValue: user.PassionID,
}); });
const savePassions = (idPassionList) => { const savePassions = (idPassionList) => {
let jsonPassions = { Passion: { connect: [] } }; let jsonPassions = {Passion: {connect: []}};
idPassionList.forEach((id) => { idPassionList.forEach((id) => {
jsonPassions.Passion.connect.push({ id: id }); jsonPassions.Passion.connect.push({id: id});
}); });
const options = { const options = {
method: "PATCH", method: 'PATCH',
headers: { headers: {'Content-Type': 'application/json'},
"Content-Type": "application/json",
},
body: JSON.stringify(jsonPassions), body: JSON.stringify(jsonPassions),
}; };
fetch(`/api/users/${user.id}`, options) fetch(`/api/users/${user.id}`, options)
.then((res) => res.json()) .then(res => res.json())
.then((data) => { .then(() => {
toast({ toast({
title: "Centres d'intérêts mis à jour", title: 'Centres d\'intérêts mis à jour',
status: "success", status: 'success',
duration: 9000, duration: 9000,
}); });
client.invalidateQueries("user"); client.invalidateQueries('user');
onClose(); onClose();
}) })
.catch((err) => { .catch(err => console.log(err));
console.log(err);
});
}; };
return ( return (
<> <>
<Button <Button onClick={onOpen} leftIcon={<RiEditBoxLine/>}>
colorScheme={"purple"} Choisir les centres d'intérêts
onClick={onOpen} </Button>
leftIcon={<RiEditBoxLine />}
>
Choisir les centres d'intérèts
</Button>
<Modal <Modal blockScrollOnMount={false} size={'4xl'} isOpen={isOpen}
blockScrollOnMount={false} onClose={onClose} scrollBehavior={'inside'}>
size={"4xl"} <ModalOverlay/>
isOpen={isOpen} <ModalContent>
onClose={onClose} <ModalHeader>Choix des centres d'intérêts</ModalHeader>
scrollBehavior={"inside"} <ModalCloseButton/>
> <ModalBody>
<ModalOverlay /> <Flex gap={'1rem'} flexWrap="wrap">
<ModalContent> {passions && (
<ModalHeader>Choix des centres d'intérèts</ModalHeader> passions.map((passion: Passion) => (
<ModalCloseButton /> <CustomCheckbox key={passion.id} text={passion.name}
<ModalBody> {...getCheckboxProps({value: passion.id})}/>
<Flex gap={"1rem"} flexWrap="wrap"> )
{passions !== null ? ( )
passions.map((passion, index) => { )}
return ( </Flex>
<CustomCheckbox </ModalBody>
{...getCheckboxProps({ value: passion.id })}
key={passion.id}
text={passion.name}
/>
);
})
) : (
<></>
)}
</Flex>
</ModalBody>
<ModalFooter> <ModalFooter>
<Button <Button mr={3} onClick={() => savePassions(value)}>
colorScheme="purple" Save
mr={3} </Button>
onClick={() => { </ModalFooter>
savePassions(value); </ModalContent>
}} </Modal>
> </>
Save
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
); );
} }
@@ -13,66 +13,65 @@ import {
ModalHeader, ModalHeader,
ModalOverlay, ModalOverlay,
useDisclosure, useDisclosure,
useQuery,
useToast, useToast,
} from "@chakra-ui/react"; } from '@chakra-ui/react';
import { useQueryClient } from "@tanstack/react-query"; import {useQueryClient} from '@tanstack/react-query';
import { useState } from "react"; import {useState} from 'react';
import { RiEditBoxLine } from "react-icons/ri"; import {RiEditBoxLine} from 'react-icons/ri';
export default function ModalModifyImages(props) { export default function ModalModifyImages(props) {
const { isOpen, onOpen, onClose } = useDisclosure(); const {isOpen, onOpen, onClose} = useDisclosure();
const { images, user, userData, setUserData } = props; const {images, user, userData, setUserData} = props;
const [listImage, setlistImage] = useState(images); const [listImage, setlistImage] = useState(images);
const toast = useToast({ position: "top", isClosable: true }); const toast = useToast({position: 'top', isClosable: true});
const client = useQueryClient(); const client = useQueryClient();
const uploadImage = async (file) => { const uploadImage = async (file) => {
const body = new FormData(); const body = new FormData();
body.append("file", file); body.append('file', file);
const imagePostOptions = { const imagePostOptions = {
method: "POST", method: 'POST',
body, body,
}; };
const imagePatchOptions = { const imagePatchOptions = {
method: "PUT", method: 'PUT',
headers: { "Content-Type": "application/json" }, headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ body: JSON.stringify({
images: [...listImage, `imageUsers/${file.name}`], images: [...listImage, `imageUsers/${file.name}`],
}), }),
}; };
fetch(`/api/file/uploadFile`, imagePostOptions) fetch(`/api/file/uploadFile`, imagePostOptions)
.then((res) => { .then(res => {
fetch(`/api/users/${user.id}`, imagePatchOptions) fetch(`/api/users/${user.id}`, imagePatchOptions)
.then((res) => { .then(res => {
toast({ toast({
title: `Ajout d'image effectué`, title: `Ajout d'image effectué`,
status: "success", status: 'success',
isClosable: true, isClosable: true,
}); });
setlistImage([...listImage, `imageUsers/${file.name}`]); setlistImage([...listImage, `imageUsers/${file.name}`]);
// router.reload(); // router.reload();
}) })
.catch(() => { .catch(() => {
setIsLoading(false); setIsLoading(false);
toast({ toast({
title: `Erreur lors de l'ajout des images`, title: `Erreur lors de l'ajout des images`,
status: "error", status: 'error',
isClosable: true, isClosable: true,
}); });
});
})
.catch(err => {
toast({
title: `Erreur lors de l'ajout des images`,
status: 'error',
isClosable: true,
}); });
}) console.log(err);
.catch((err) => {
toast({
title: `Erreur lors de l'ajout des images`,
status: "error",
isClosable: true,
}); });
console.log(err);
});
}; };
const deleteImage = async (fileName) => { const deleteImage = async (fileName) => {
@@ -85,121 +84,97 @@ export default function ModalModifyImages(props) {
} }
const imageDeleteOptions = { const imageDeleteOptions = {
method: "DELETE", method: 'DELETE',
body: JSON.stringify({ fileName: fileName.split("/").pop() }), body: JSON.stringify({fileName: fileName.split('/').pop()}),
}; };
fetch(`/api/file/deleteFile`, imageDeleteOptions).then((res) => { fetch(`/api/file/deleteFile`, imageDeleteOptions).then((res) => {
const imagePatchOptions = { const imagePatchOptions = {
method: "PUT", method: 'PUT',
headers: { "Content-Type": "application/json" }, headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ body: JSON.stringify({
images: [...listImage], images: [...listImage],
}), }),
}; };
fetch(`/api/users/${user.id}`, imagePatchOptions) fetch(`/api/users/${user.id}`, imagePatchOptions)
.then((res) => { .then((res) => {
toast({ toast({
title: `Suppression d'image effectué`, title: `Suppression d'image effectué`,
status: "success", status: 'success',
isClosable: true, isClosable: true,
});
})
.catch(() => {
toast({
title: `Erreur lors de la suppression des images`,
status: 'error',
isClosable: true,
});
}); });
})
.catch(() => {
toast({
title: `Erreur lors de la suppression des images`,
status: "error",
isClosable: true,
});
});
}); });
}; };
return ( return (
<> <>
<Button <Button onClick={onOpen} leftIcon={<RiEditBoxLine/>}>
colorScheme={"purple"} Modifier les images
onClick={onOpen} </Button>
leftIcon={<RiEditBoxLine />}
>
Modifier les images
</Button>
<Modal <Modal blockScrollOnMount={false} size={'4xl'} isOpen={isOpen}
blockScrollOnMount={false} onClose={onClose} scrollBehavior={'inside'}>
size={"4xl"} <ModalOverlay/>
isOpen={isOpen} <ModalContent>
onClose={onClose} <ModalHeader>Modification des images</ModalHeader>
scrollBehavior={"inside"} <ModalCloseButton/>
> <ModalBody>
<ModalOverlay /> <Grid templateColumns={`repeat(${listImage.length + 1}, 1fr)`}
<ModalContent> gap={5}>
<ModalHeader>Modification des images</ModalHeader> {listImage.map((image, index) => (
<ModalCloseButton /> <GridItem key={index}>
<ModalBody> <Flex direction={'column'} gap={'1rem'}>
<Grid <Image src={image}/>
templateColumns={`repeat(${listImage.length + 1}, 1fr)`} <Button id={index.toString()} colorScheme={'red'}
gap={5} onClick={() =>
> deleteImage(`${listImage[index]}`)}>
{listImage.map((image, index) => ( Supprimer l'image
<GridItem key={index}> </Button>
<Flex direction={"column"} gap={"1rem"}> </Flex>
<Image src={image} /> </GridItem>
<Button ))}
id={"" + index} {listImage.length < 5 && (
colorScheme={"red"} <GridItem width={'100%'}>
onClick={(e) => { <Input type={'file'} height={'100%'}
deleteImage(`${listImage[index]}`); accept={'image/png, image/jpeg, image/webp'}
}} onInput={({target}) => {
> const date = new Date();
Supprimer l'image const time = date.getTime();
</Button>
</Flex>
</GridItem>
))}
{listImage.length < 5 ? (
<GridItem width={"100%"}>
<Input
type={"file"}
height={"100%"}
accept={"image/png, image/jpeg, image/webp"}
onInput={({ target }) => {
const date = new Date();
const time = date.getTime();
const file = target.files[0]; const file = target.files[0];
const extension = file.name.split(".").pop(); const extension = file.name.split('.').pop();
const newFile = new File( const newFile = new File(
[file], [file],
`${user.id}_${time}.${extension}`, `${user.id}_${time}.${extension}`,
{ {
type: file.type, type: file.type,
} },
); );
uploadImage(newFile); uploadImage(newFile);
}} }}/>
></Input> </GridItem>
</GridItem> )}
) : ( </Grid>
<></> </ModalBody>
)}
</Grid>
</ModalBody>
<ModalFooter> <ModalFooter>
<Button <Button mr={3} onClick={() => {
colorScheme="purple" client.invalidateQueries('user');
mr={3}
onClick={(e) => {
client.invalidateQueries("user");
onClose(); onClose();
}} }}>
> Save
Save </Button>
</Button> </ModalFooter>
</ModalFooter> </ModalContent>
</ModalContent> </Modal>
</Modal> </>
</>
); );
} }
@@ -5,7 +5,7 @@ export default function ProfileBadgeList(props) {
return ( return (
<Flex gap={"0.5rem"} mt={"1vh"} flexWrap="wrap"> <Flex gap={"0.5rem"} mt={"1vh"} flexWrap="wrap">
{userPassions.map((idPassion, index) => ( {userPassions.map((idPassion, index) => (
<Tag key={index} colorScheme={"purple"}> <Tag key={index}>
{passions !== null && passions !== undefined && passions.length > 0 {passions !== null && passions !== undefined && passions.length > 0
? passions.find((element) => element.id === idPassion).name ? passions.find((element) => element.id === idPassion).name
: ""} : ""}
+2 -1
View File
@@ -3,6 +3,7 @@ import type { AppProps } from "next/app";
import { ChakraProvider } from "@chakra-ui/react"; import { ChakraProvider } from "@chakra-ui/react";
import { SessionProvider } from "next-auth/react"; import { SessionProvider } from "next-auth/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import theme from '@/styles/theme';
const queryClient = new QueryClient(); const queryClient = new QueryClient();
@@ -12,7 +13,7 @@ export default function App({
}: AppProps) { }: AppProps) {
return ( return (
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<ChakraProvider> <ChakraProvider theme={theme}>
<SessionProvider session={session}> <SessionProvider session={session}>
<Component {...pageProps} /> <Component {...pageProps} />
</SessionProvider> </SessionProvider>
+47 -59
View File
@@ -1,95 +1,83 @@
import { import {
Box, Box,
Button, Button,
Flex,
FormControl, FormControl,
FormErrorMessage, FormErrorMessage,
FormLabel, FormLabel,
Input, Input,
useToast, useToast,
} from "@chakra-ui/react"; } 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 { useState } from "react"; import {useState} from 'react';
import { useForm } from "react-hook-form"; import {useForm} from 'react-hook-form';
import LoadingPage from '@/components/LoadingPage';
import {Session} from '@/models/auth/Session';
export default function settings() { export default function settings() {
const router = useRouter(); const router = useRouter();
const toast = useToast({ position: "top", isClosable: true }); const toast = useToast({position: 'top', isClosable: true});
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { const {
handleSubmit, handleSubmit,
register, register,
formState: { errors, isSubmitting }, formState: {errors, isSubmitting},
} = useForm(); } = useForm();
const [userData, setUserData] = useState({}); const [userData, setUserData] = useState({});
const {data: session, status} = useSession({required: true});
const { data: session, status } = useSession(); if (status === "loading") return <LoadingPage/>
if (status === "unauthenticated") router.push("/login"); if (status === 'authenticated') {
const {user} = session as unknown as Session;
if (status === "authenticated") { if (user.role !== 'ADMIN') router.push('/login');
const { user } = session as unknown as Session;
if (user.role !== "ADMIN") router.push("/login");
const savePassion = (passion: any) => { const savePassion = (passion: any) => {
const options = { const options = {
method: "POST", method: 'POST',
headers: { "Content-Type": "application/json" }, headers: {'Content-Type': 'application/json'},
body: JSON.stringify(passion), body: JSON.stringify(passion),
}; };
fetch(`/api/passions`, options) fetch(`/api/passions`, options)
.then((res) => { .then(res => {
setIsLoading(false); setIsLoading(false);
toast({ toast({
title: `Passion ajoutée`, title: `Passion ajoutée`,
status: "success", status: 'success',
});
})
.catch(err => {
setIsLoading(false);
toast({
title: `Erreur lors de l'ajout`,
status: 'error',
});
}); });
})
.catch((err) => {
setIsLoading(false);
toast({
title: `Erreur lors de l'ajout`,
status: "error",
});
});
}; };
return ( return (
<> <>
<Box <Box as="form" id="form_passion" width={'80%'}
as="form" onSubmit={handleSubmit(savePassion)}>
id="form_passion" <FormControl isInvalid={errors.name as boolean | undefined}>
width={"80%"} <FormLabel htmlFor="passion">Passion</FormLabel>
onSubmit={handleSubmit(savePassion)} <Input id="passion" placeholder="passion"
> {...register('name', {
<FormControl isInvalid={errors.name}> required: 'This is required',
<FormLabel htmlFor="passion">Passion</FormLabel> })}/>
<Input <FormErrorMessage>
id="passion" {errors.name && errors.name.message as string}
placeholder="passion" </FormErrorMessage>
{...register("name", { </FormControl>
required: "This is required", <Button mt={4} isLoading={isSubmitting} type="submit">
})} Submit
/> </Button>
<FormErrorMessage> </Box>
{errors.name && errors.name.message} </>
</FormErrorMessage>
</FormControl>
<Button
mt={4}
colorScheme="purple"
isLoading={isSubmitting}
type="submit"
>
Submit
</Button>
</Box>
</>
); );
} }
} }
+1 -1
View File
@@ -55,7 +55,7 @@ export default function ChatId() {
<MessageList user={session.user as User} messages={messages}/> <MessageList user={session.user as User} messages={messages}/>
<Flex gap={5} mt={5}> <Flex gap={5} mt={5}>
<Input type={"text"} colorScheme={'purple'} onChange={(evt) => setText(evt.target.value) } value={text} /> <Input type={"text"} colorScheme={'purple'} onChange={evt => setText(evt.target.value)} value={text} />
<Button colorScheme={'purple'} onClick={handleSubmit}>Envoyer</Button> <Button colorScheme={'purple'} onClick={handleSubmit}>Envoyer</Button>
</Flex> </Flex>
</Container> </Container>
+9
View File
@@ -0,0 +1,9 @@
import {extendTheme, withDefaultColorScheme} from '@chakra-ui/react';
const theme = extendTheme(
{},
withDefaultColorScheme({ colorScheme: 'purple' })
);
export default theme;