feat(Choose passions): Can now choose passion

Todo : fetch userData in useEffect and using userData for userProfile
This commit is contained in:
Laurian-Dufrechou
2023-04-03 19:17:01 +02:00
parent 7b7ded3648
commit 018af6890c
7 changed files with 411 additions and 24 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1,50 @@
import {
Badge,
useCheckbox,
Text,
Flex,
chakra,
Box,
Tag,
TagLeftIcon,
TagLabel,
} from "@chakra-ui/react";
import { IoAdd, IoRemove } from "react-icons/io5";
export default function CustomCheckbox(props) {
const { text, checked, value } = props;
const { state, getInputProps, getCheckboxProps, getLabelProps, htmlProps } =
useCheckbox(props);
return (
<chakra.label
// colorScheme={"purple"}
variant={state.isChecked ? "solid" : "outline"}
cursor="pointer"
{...htmlProps}
>
<input {...getInputProps()} hidden />
{state.isChecked ? (
<Tag
{...getCheckboxProps()}
colorScheme={"purple"}
{...getLabelProps()}
>
<TagLeftIcon as={IoRemove} />
<TagLabel>{text}</TagLabel>
</Tag>
) : (
<Tag
{...getCheckboxProps()}
colorScheme={"purple"}
variant={"outline"}
{...getLabelProps()}
>
<TagLeftIcon as={IoAdd} />
<TagLabel>{text}</TagLabel>
</Tag>
)}
</chakra.label>
);
}
@@ -0,0 +1,110 @@
import {
Badge,
Box,
Button,
Flex,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
useCheckbox,
useCheckboxGroup,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import { useEffect } from "react";
import { useState } from "react";
import { RiEditBoxLine } from "react-icons/ri";
import CustomCheckbox from "./CustomCheckbox";
export default function ModalChoosePassion(props) {
const { isOpen, onOpen, onClose } = useDisclosure();
const { user, passions } = props;
const toast = useToast();
const { value, getCheckboxProps } = useCheckboxGroup({
defaultValue: user.PassionID,
});
const savePassions = (PassionID) => {
const options = {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ PassionID }),
};
fetch(`/api/users/${user.id}`, options)
.then((res) => res.json())
.then((data) => {
toast({
title: "Centres d'intérêts mis à jour",
status: "success",
duration: 9000,
isClosable: true,
});
onClose();
})
.catch((err) => {
console.log(err);
});
};
return (
<>
<Button
colorScheme={"purple"}
onClick={onOpen}
leftIcon={<RiEditBoxLine />}
>
Choisir les centres d'intérèts
</Button>
<Modal
blockScrollOnMount={false}
size={"4xl"}
isOpen={isOpen}
onClose={onClose}
scrollBehavior={"inside"}
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Choix des centres d'intérèts</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Flex gap={"1rem"} flexWrap="wrap">
{passions !== null ? (
passions.map((passion, index) => {
return (
<CustomCheckbox
{...getCheckboxProps({ value: passion.id })}
key={passion.id}
text={passion.name}
/>
);
})
) : (
<></>
)}
</Flex>
</ModalBody>
<ModalFooter>
<Button
colorScheme="purple"
mr={3}
onClick={() => savePassions(value)}
>
Save
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
}
@@ -0,0 +1,204 @@
import {
Button,
Flex,
Grid,
GridItem,
Image,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import { useState } from "react";
import { RiEditBoxLine } from "react-icons/ri";
export default function ModalModifyImages(props) {
const { isOpen, onOpen, onClose } = useDisclosure();
const { images, user, userData, setUserData } = props;
const [listImage, setlistImage] = useState(images);
const toast = useToast();
const uploadImage = async (file) => {
const body = new FormData();
body.append("file", file);
const imagePostOptions = {
method: "POST",
body,
};
const imagePatchOptions = {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
images: [...listImage, `imageUsers/${file.name}`],
}),
};
fetch(`/api/file/uploadFile`, imagePostOptions)
.then((res) => {
fetch(`/api/users/${user.id}`, imagePatchOptions)
.then((res) => {
toast({
title: `Ajout d'image effectué`,
status: "success",
isClosable: true,
});
setlistImage([...listImage, `imageUsers/${file.name}`]);
// router.reload();
})
.catch(() => {
setIsLoading(false);
toast({
title: `Erreur lors de l'ajout des images`,
status: "error",
isClosable: true,
});
});
})
.catch((err) => {
toast({
title: `Erreur lors de l'ajout des images`,
status: "error",
isClosable: true,
});
console.log(err);
});
};
const deleteImage = async (fileName) => {
let newListImage = listImage;
const index = newListImage.indexOf(fileName);
if (index > -1) {
newListImage.splice(index, 1);
setlistImage([...newListImage]);
}
const imageDeleteOptions = {
method: "DELETE",
body: JSON.stringify({ fileName: fileName.split("/").pop() }),
};
fetch(`/api/file/deleteFile`, imageDeleteOptions).then((res) => {
const imagePatchOptions = {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
images: [...listImage],
}),
};
fetch(`/api/users/${user.id}`, imagePatchOptions)
.then((res) => {
toast({
title: `Suppression d'image effectué`,
status: "success",
isClosable: true,
});
})
.catch(() => {
toast({
title: `Erreur lors de la suppression des images`,
status: "error",
isClosable: true,
});
});
});
};
return (
<>
<Button
colorScheme={"purple"}
onClick={onOpen}
leftIcon={<RiEditBoxLine />}
>
Modifier les images
</Button>
<Modal
blockScrollOnMount={false}
size={"4xl"}
isOpen={isOpen}
onClose={onClose}
scrollBehavior={"inside"}
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Modification des images</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Grid
templateColumns={`repeat(${listImage.length + 1}, 1fr)`}
gap={5}
>
{listImage.map((image, index) => (
<GridItem key={index}>
<Flex direction={"column"} gap={"1rem"}>
<Image src={image} />
<Button
id={"" + index}
colorScheme={"red"}
onClick={(e) => {
deleteImage(`${listImage[index]}`);
}}
>
Supprimer l'image
</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 extension = file.name.split(".").pop();
const newFile = new File(
[file],
`${user.id}_${time}.${extension}`,
{
type: file.type,
}
);
uploadImage(newFile);
}}
></Input>
</GridItem>
) : (
<></>
)}
</Grid>
</ModalBody>
<ModalFooter>
<Button
colorScheme="purple"
mr={3}
onClick={(e) => {
setUserData({
...userData,
images: [...listImage],
});
onClose();
}}
>
Save
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
}
@@ -0,0 +1,16 @@
import { Flex, Tag } from "@chakra-ui/react";
export default function ProfileBadgeList(props) {
const { passions, userPassions } = props;
return (
<Flex gap={"0.5rem"} mt={"1vh"} flexWrap="wrap">
{userPassions.map((idPassion, index) => (
<Tag key={index} colorScheme={"purple"}>
{passions !== null && passions !== undefined
? passions.find((element) => element.id === idPassion).name
: ""}
</Tag>
))}
</Flex>
);
}
+28 -16
View File
@@ -23,21 +23,22 @@ import {
RadioGroup,
Text,
useToast,
VStack,
} from "@chakra-ui/react";
import ModalModifyImages from "@/components/layout/user_profile/ModalModifyImages";
import ModalChoosePassion from "@/components/layout/user_profile/ModalChoosePassion";
import ProfileBadgeList from "@/components/layout/user_profile/ProfileBadgeList";
import ProfileTagList from "@/components/layout/user_profile/ProfileTagList";
import { useState } from "react";
import { useEffect, useState } from "react";
import { useForm, Controller } from "react-hook-form";
export default function UserProfile() {
const router = useRouter();
const toast = useToast();
const [isLoading, setIsLoading] = useState(false);
const [passions, setPassions] = useState(null);
const {
handleSubmit,
@@ -45,6 +46,18 @@ export default function UserProfile() {
formState: { errors },
} = useForm();
useEffect(() => {
fetch(`/api/passions`)
.then((res) => res.json())
.then((data) => {
setPassions([...data]);
})
.catch((err) => {
console.log(err);
});
}, []);
// faire un useEffect ou je fetch user
const [userData, setUserData] = useState({});
const { data: session, status } = useSession();
@@ -86,7 +99,7 @@ export default function UserProfile() {
.then((res) => {
setIsLoading(false);
toast({
position:'top',
position: "top",
title: `Modifications effectuées`,
status: "success",
isClosable: true,
@@ -97,7 +110,7 @@ export default function UserProfile() {
setIsLoading(false);
toast({
title: `Erreur lors de l'envoi des modifications`,
position :'top',
position: "top",
status: "error",
isClosable: true,
});
@@ -310,16 +323,15 @@ export default function UserProfile() {
<FormLabel as={"legend"} htmlFor={"passion"}>
Centre d'intéret :
</FormLabel>
<Controller
name={"passion"}
control={control}
render={({ field }) => (
<>
<ProfileBadgeList passions={user.passion !== undefined ? user.passion : []}/>
<ModalChoosePassion user={user}/>
</>
)}
/>
<VStack gap={"1rem"} align="start">
<ProfileTagList
userPassions={
user.PassionID !== undefined ? user.PassionID : []
}
passions={passions}
/>
<ModalChoosePassion user={user} passions={passions} />
</VStack>
</Box>
</Box>
<Divider colorScheme={"purple"} />
@@ -403,7 +415,7 @@ export default function UserProfile() {
</Button>
<Button
colorScheme={"purple"}
variant='outline'
variant="outline"
onClick={() => router.push("/dashboard")}
>
Retour
+3 -8
View File
@@ -1050,14 +1050,9 @@
dependencies:
"glob" "7.1.7"
"@next/swc-linux-x64-gnu@13.2.3":
"integrity" "sha512-w5MyxPknVvC9LVnMenAYMXMx4KxPwXuJRMQFvY71uXg68n7cvcas85U5zkdrbmuZ+JvsO5SIG8k36/6X3nUhmQ=="
"resolved" "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.3.tgz"
"version" "13.2.3"
"@next/swc-linux-x64-musl@13.2.3":
"integrity" "sha512-CTeelh8OzSOVqpzMFMFnVRJIFAFQoTsI9RmVJWW/92S4xfECGcOzgsX37CZ8K982WHRzKU7exeh7vYdG/Eh4CA=="
"resolved" "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.3.tgz"
"@next/swc-win32-x64-msvc@13.2.3":
"integrity" "sha512-aLG2MaFs4y7IwaMTosz2r4mVbqRyCnMoFqOcmfTi7/mAS+G4IMH0vJp4oLdbshqiVoiVuKrAfqtXj55/m7Qu1Q=="
"resolved" "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.3.tgz"
"version" "13.2.3"
"@nodelib/fs.scandir@2.1.5":