mirror of
https://github.com/LucasVbr/meeting-app.git
synced 2026-05-13 17:21:53 +00:00
refactor: Set default colorScheme to purple
Took 1 hour 12 minutes
This commit is contained in:
@@ -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
@@ -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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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'inscris
|
Je m'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">"{listUsers[0].bio}"</Text>
|
<Text as="i">"{listUsers[0].bio}"</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'}>"{user.bio}"</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"}>
|
|
||||||
"{user.bio}"
|
|
||||||
</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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -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'amour. Inscrivez-vous
|
des personnes intéressantes et de trouver l'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'inscrire
|
S'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'}>
|
||||||
|
|||||||
@@ -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='© <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='© <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>
|
</>
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {extendTheme, withDefaultColorScheme} from '@chakra-ui/react';
|
||||||
|
|
||||||
|
|
||||||
|
const theme = extendTheme(
|
||||||
|
{},
|
||||||
|
withDefaultColorScheme({ colorScheme: 'purple' })
|
||||||
|
);
|
||||||
|
|
||||||
|
export default theme;
|
||||||
Reference in New Issue
Block a user