Files
meeting-app/src/pages/profile.tsx
T

371 lines
12 KiB
TypeScript

import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import type { Session } from "@/models/auth/Session";
import Carousel from "@/components/Carousel";
import { Gender } from "@prisma/client";
import {
Box,
Button,
Center,
Container,
Divider,
Flex,
FormLabel,
Text,
useToast,
VStack,
} from "@chakra-ui/react";
import { formateDate } from "@/lib/formateDate";
import ModalModifyImages from "@/components/layout/profile/ModalModifyImages";
import ModalChoosePassion from "@/components/layout/profile/ModalChoosePassion";
import ProfileTagList from "@/components/layout/profile/ProfileTagList";
import LoadingPage from "@/components/LoadingPage";
import CustomEditable from "@/components/layout/profile/CustomEditable";
import CustomFalseEditable from "@/components/layout/profile/CustomFalseEditable";
import CustomEditableArea from "@/components/layout/profile/CustomEditableArea";
import CustomRadioGender from "@/components/layout/profile/CustomRadioGender";
import CustomRangeSlider from "@/components/layout/profile/CustomRangeSlider";
import CustomSlider from "@/components/layout/profile/CustomSlider";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { useQuery } from "@tanstack/react-query";
export default function UserProfile() {
const router = useRouter();
const toast = useToast({ position: "top", isClosable: true });
const [currentlyLoading, setCurrentlyLoading] = useState(false);
const [passions, setPassions] = useState([] as any[]);
const [address, setAddress] = useState({} as any);
const [showTooltipAge, setShowTooltipAge] = useState(false);
const [sliderAgeValue, setSliderAgeValue] = useState([[] as number[]]);
const [showTooltipDistance, setShowTooltipDistance] = useState(false);
const [sliderDistanceValue, setSliderDistanceValue] = useState<number>(0);
const {
handleSubmit,
control,
formState: { errors },
} = useForm({ mode: "onBlur" });
useEffect(() => {
fetch(`/api/passions`)
.then((res) => res.json())
.then((data) => {
setPassions([...data]);
})
.catch((err) => {
console.log(err);
});
}, []);
const { data: session, status } = useSession();
const {
isLoading,
isError,
data: userData,
error,
} = useQuery({
queryKey: ["user"],
enabled: status === "authenticated",
queryFn: async () => {
const { user } = session as unknown as Session;
setSliderAgeValue([user.ageMin, user.ageMax]);
setSliderDistanceValue(user.distance);
return fetch(`/api/users/${user.id}`)
.then((res) => res.json())
.catch((err) => {
return err;
});
},
});
const { data: addressData, isSuccess: addressIsSuccess } = useQuery({
queryKey: ["address"],
enabled: Boolean(userData?.location),
queryFn: async () => {
const [lat, long] = userData.location.split(",");
return fetch(
`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${long}`
)
.then((res) => res.json())
.catch((err) => {
return err;
});
},
});
if (isLoading) {
if (status === "unauthenticated") router.push("/login");
return <LoadingPage />;
}
if (isError) {
toast({
title: `Erreur lors de la récupération des données du profil`,
status: "error",
position: "top",
});
if (status === "unauthenticated") router.push("/");
}
const saveData = (values: any) => {
let trueValues = {};
for (const [key, value] of Object.entries(values)) {
if (value !== "" && value !== undefined) {
if (key === "age") {
trueValues["ageMin"] = value[0];
trueValues["ageMax"] = value[1];
} else {
trueValues[key] = value;
}
}
}
const options = {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(trueValues),
};
if (Object.keys(trueValues).length > 0) {
setCurrentlyLoading(true);
fetch(`/api/users/${userData.id}`, options)
.then((res) => {
setCurrentlyLoading(false);
toast({
position: "top",
title: `Modifications effectuées`,
status: "success",
isClosable: true,
});
})
.catch((err) => {
setCurrentlyLoading(false);
toast({
title: `Erreur lors de l'envoi des modifications`,
position: "top",
status: "error",
isClosable: true,
});
console.log(err);
});
}
};
return (
<Box height={"100%"}>
<Container justifyContent={"center"} maxW={"70rem"} mt={"1rem"}>
<Flex flexDirection={"column"} alignItems={"center"} gap={"1rem"}>
<Box width={"50%"}>
<Carousel
images={
userData.images
? userData.images
: ["blank_profile_picture.webp"]
}
borderRadius={"1rem"}
></Carousel>
</Box>
<ModalModifyImages
userData={userData}
user={userData}
images={userData.images}
/>
<Divider colorScheme={"purple"} />
<Text align={"center"} as="i" color={"grey"}>
Modifiez les champs en les selectionnants
</Text>
<Box
position={"relative"}
as="form"
onSubmit={handleSubmit(saveData)}
width={"80%"}
>
<Flex width={"100%"} justify={"space-between"} mb={"1rem"}>
<Box>
<CustomEditable
name="firstName"
defaultValue={
userData.firstName === null ? "" : userData.firstName
}
control={control}
rules={{
required: { value: true, message: "Prénom requis" },
}}
label={"Prénom :"}
/>
</Box>
<Box>
<CustomEditable
name="lastName"
defaultValue={
userData.lastName === null ? "" : userData.lastName
}
control={control}
rules={{
required: { value: true, message: "Nom requis" },
}}
label={"Nom :"}
/>
</Box>
<Box>
<CustomFalseEditable
id="birthdate"
value={
userData.birthdate === undefined ||
userData.birthdate === null
? "Non renseigné"
: formateDate(userData.birthdate.toString())
}
label={"Date de naissance :"}
/>
</Box>
</Flex>
<Divider colorScheme={"purple"} />
<Flex justify={"space-between"} my={"1rem"}>
<Box>
<CustomFalseEditable
id="location"
value={
!addressIsSuccess || addressData === undefined
? "Rendez vous sur la carte !"
: `${addressData.address.town}, ${addressData.address.county}, ${addressData.address.state}`
}
label={"Adresse :"}
helperText={
"Ce champ est automatiquement modifié depuis la carte"
}
/>
</Box>
<Box>
<CustomFalseEditable
id="email"
value={userData.email}
label={"Adresse mail :"}
/>
</Box>
</Flex>
<Divider colorScheme={"purple"} />
<Box my={"1rem"}>
<CustomEditableArea
name="bio"
defaultValue={userData.bio === null ? "" : userData.bio}
control={control}
rules={{
maxLength: {
value: 240,
message: "240 caractères maximum",
},
}}
label={"À propos :"}
/>
</Box>
<Divider colorScheme={"purple"} />
<Box my={"1rem"}>
<Box>
<CustomRadioGender
name={"gender"}
label={"Genre :"}
control={control}
defaultValue={
userData.gender === null ? Gender.UNKNOWN : userData.gender
}
/>
</Box>
</Box>
<Divider colorScheme={"purple"} />
<Box my={"1rem"}>
<Box>
<FormLabel as={"legend"} htmlFor={"passion"}>
Centre d'intéret :
</FormLabel>
<VStack gap={"1rem"} align="start">
<ProfileTagList
userPassions={
userData.PassionID !== undefined ? userData.PassionID : []
}
passions={passions}
/>
<ModalChoosePassion user={userData} passions={passions} />
</VStack>
</Box>
</Box>
<Divider colorScheme={"purple"} />
<Box my={"1rem"}>
<Center>
<Text as={"b"} fontSize={"1.5rem"} my={"1rem"}>
Préférences
</Text>
</Center>
<Box my={"1rem"}>
<CustomRadioGender
name={"prefGender"}
label={"Genre :"}
control={control}
defaultValue={
userData.prefGender === null
? Gender.UNKNOWN
: userData.prefGender
}
/>
</Box>
<Box>
<CustomRangeSlider
control={control}
label={"Age :"}
name={"age"}
defaultValue={[userData.ageMin, userData.ageMax]}
setSliderAgeValue={setSliderAgeValue}
setShowTooltipAge={setShowTooltipAge}
showTooltipAge={showTooltipAge}
sliderAgeValue={sliderAgeValue}
/>
</Box>
<CustomSlider
control={control}
label={"Distance des bars :"}
name={"distance"}
defaultValue={userData.distance}
setSliderDistanceValue={setSliderDistanceValue}
setShowTooltipDistance={setShowTooltipDistance}
showTooltipDistance={showTooltipDistance}
sliderDistanceValue={sliderDistanceValue}
/>
</Box>
<Divider colorScheme={"purple"} />
<Center position={"sticky"} bottom={0} gap={"1rem"} my={"1rem"}>
<Button
colorScheme={"purple"}
isLoading={currentlyLoading}
type="submit"
>
Sauvegarder les modifications
</Button>
<Button
colorScheme={"purple"}
variant="outline"
onClick={() => {
router.push("/dashboard");
}}
>
Retour
</Button>
</Center>
</Box>
</Flex>
</Container>
</Box>
);
}