mirror of
https://github.com/LucasVbr/meeting-app.git
synced 2026-05-13 17:21:53 +00:00
feat(Choose passions): Can now choose passion
Todo : fetch userData in useEffect and using userData for userProfile
This commit is contained in:
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
@@ -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
|
||||
|
||||
@@ -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":
|
||||
|
||||
Reference in New Issue
Block a user