Skip to content

Commit

Permalink
Merge pull request #72 from OZ-Coding-School/dev/detail
Browse files Browse the repository at this point in the history
회원 탈퇴 추가, 댓글 수정
  • Loading branch information
srnnnn authored Sep 9, 2024
2 parents 8f6eaa3 + 707e99c commit eab5520
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,7 @@ export const passwordResetConfirm = (uidb64: string, token: string, password: st
};
return axiosInstance.post(`/auth/password/reset/`, requestForm);
};

export const deleteAccount = () => {
return axiosInstance.delete("/auth/delete/");
};
8 changes: 7 additions & 1 deletion src/common/comment/CommentInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ const CommentInput = ({ onClose = () => {}, articleId, onCommentSubmit, toast }:
}, [setFocus]);

const onSubmit = async (data: CommentFormData) => {
if (!data.content.trim()) {
toast("내용을 입력해주세요.");
return;
}
const formData = new FormData();
formData.append("content", data.content);

Expand All @@ -53,9 +57,11 @@ const CommentInput = ({ onClose = () => {}, articleId, onCommentSubmit, toast }:
onCommentSubmit(newComment);
} catch (error) {
if (error instanceof AxiosError && error.response) {
console.log("훈수작성 실패", error);
console.error("훈수작성 실패", error);
if (error.response.status === 401) {
toast("로그인 후 사용가능합니다.");
} else if (error.response.status === 400) {
toast("잘못된 요청입니다. 다시 시도해주세요.");
}
}
}
Expand Down
36 changes: 32 additions & 4 deletions src/common/info/InfoMyPageLeft.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import ProfileImage from "../profile/ProfileImage";
import { useUserStore } from "../../config/store";
import { userInfoImageUpdate, userInfoUpdate } from "../../api/account";
import { AxiosError } from "axios";
import { ModalPortalModal } from "../../config/ModalPortalModal";
import ModalUserDelete from "../modal/ModalUserDelete";

type InfoMyPageLeftProps = {
isUserMypage: boolean;
Expand All @@ -17,6 +19,7 @@ const InfoMyPageLeft = ({ isUserMypage }: InfoMyPageLeftProps) => {
const [profileImage, setProfileImage] = useState<File | null>(null);
const { user, updateUser, otherUser } = useUserStore();
const [error, setError] = useState<string | null>(null);
const [deleteUserModal, setDeleteUserModal] = useState(false);

useEffect(() => {
if (user.nickname) setNicknameText(user.nickname);
Expand Down Expand Up @@ -55,11 +58,19 @@ const InfoMyPageLeft = ({ isUserMypage }: InfoMyPageLeftProps) => {
}
};

const handleDeleteUserModalClose = () => {
setDeleteUserModal(false);
};

const handleDeleteUserModalOpen = () => {
setDeleteUserModal(true);
};

return (
<form
className={tw(
"bg-white w-full min-w-[300px] rounded-2xl px-5 py-6 flex flex-col sticky top-20 dark:bg-gray-800 ",
isUserMypage ? "min-h-[300px]" : "min-h-[200px]"
isUserMypage ? "min-h-[325px]" : "min-h-[200px]"
)}
>
<div className="flex flex-col flex-grow gap-8">
Expand Down Expand Up @@ -146,10 +157,27 @@ const InfoMyPageLeft = ({ isUserMypage }: InfoMyPageLeftProps) => {
</div>
<p className="mb-1 text-xs font-medium text-center text-literal-highlight">{error}</p>
{isUserMypage && (
<Button color="primary" onClick={() => (isEdit ? handleUpdate() : setIsEdit(true))}>
{isEdit ? "프로필 수정 완료" : "프로필 수정"}
</Button>
<>
<Button color="primary" onClick={() => (isEdit ? handleUpdate() : setIsEdit(true))}>
{isEdit ? "프로필 수정 완료" : "프로필 수정"}
</Button>
<Button
onClick={handleDeleteUserModalOpen}
className="mt-3 bg-transparent text-gray-500 text-xs py-0 px-0 w-fit ml-auto hover:bg-transparent hover:text-gray-500 hover:underline"
>
회원탈퇴
</Button>
</>
)}
<ModalPortalModal>
{deleteUserModal && (
<ModalUserDelete
isOpen={deleteUserModal}
onClose={handleDeleteUserModalClose}
parent="home-parent"
/>
)}
</ModalPortalModal>
</form>
);
};
Expand Down
164 changes: 164 additions & 0 deletions src/common/modal/ModalUserDelete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { AnimatePresence, motion } from "framer-motion";
import { useEffect, useRef, useState } from "react";
import { IoClose } from "react-icons/io5";
import Button from "../button/Button";
import { authApi } from "../../api";
import { useNavigate } from "react-router-dom";
import { IoWarning } from "react-icons/io5";

interface ModalUserDeleteProps {
onClose: () => void;
isOpen: boolean;
parent: string;
}

const ModalUserDelete = ({ onClose, isOpen, parent }: ModalUserDeleteProps) => {
const modalRef = useRef<HTMLTextAreaElement>(null);
const [isAlert, setIsAlert] = useState(false);
const [alertMsg, setAlertMsg] = useState<null | string>(null);
useEffect(() => {
if (isOpen) {
modalRef.current?.focus();
}
}, [isOpen]);

const handleClose = async () => {
onClose();
};

const navigate = useNavigate();

useEffect(() => {
const parentElement = document.querySelector("." + parent);
const headerElement = document.querySelector(".header");

let scrollY = 0;

if (isOpen) {
//? 현재 스크롤 위치 저장
scrollY = window.scrollY;

//? 스크롤을 0으로 설정 (모달이 열릴 때)
window.scrollTo(0, 0);

//? 스크롤을 고정하고 화면을 고정
document.body.style.overflowY = "hidden";
document.body.style.top = `-${scrollY}px`;
document.body.style.width = "100%";

//? 부모 및 헤더에 blur 효과 추가
parentElement?.classList.add("blur-[2px]");
headerElement?.classList.add("blur-[2px]");
}

return () => {
//? 모달이 닫힐 때 블러 제거
parentElement?.classList.remove("blur-[2px]");
headerElement?.classList.remove("blur-[2px]");

//? 저장된 스크롤 위치로 복원
const storedScrollY = parseInt(document.body.style.top || "0") * -1;

//? 스크롤 및 위치 복원
document.body.style.overflowY = "scroll";
document.body.style.top = "";
document.body.style.width = "";

//? 저장된 스크롤 위치로 이동
window.scrollTo(0, storedScrollY);
};
}, [isOpen, parent]);

const handleUserAccountDelete = async () => {
try {
await authApi.deleteAccount();
navigate("/");
} catch (error) {
console.error("회원 탈퇴 실패:", error);
alertHandler("문제가 발생했습니다. 다시 시도해주세요.");
}
};

const alertHandler = (text: string) => {
setIsAlert(true);
setAlertMsg(text);
};

useEffect(() => {
if (isAlert) {
const timer = setTimeout(() => {
setIsAlert(false);
setAlertMsg(null);
}, 2000);

return () => clearTimeout(timer);
}
}, [isAlert]);

return (
<>
<motion.nav
initial={{ opacity: 0 }}
animate={{ opacity: 0.5 }}
exit={{ opacity: 0 }}
className="fixed flex justify-center items-center inset-0 z-50 bg-black w-full h-full"
></motion.nav>
<div
onClick={onClose}
className="text-literal-normal inset-0 font-default z-[60] fixed flex items-center justify-center"
>
<motion.nav
tabIndex={-1}
ref={modalRef}
onKeyDown={(e) => {
if (e.key === "Escape") onClose();
}}
initial={{ opacity: 0, translateY: 20 }}
animate={{ opacity: [1], translateY: 0 }}
exit={{ opacity: 0 }}
onClick={(e) => e.stopPropagation()}
className="dark:bg-gray-900 outline-none w-full h-full md:w-[570px] md:h-[240px] md:rounded-3xl bg-white relative flex justify-center items-center"
>
<div className="px-12 md:px-[110px] flex flex-col w-full h-full justify-center items-center">
<div className="w-full font-bold text-lg font-point text-center dark:text-gray-100">
회원 탈퇴
</div>
<div className="text-literal-error mt-5 w-full text-center font-semibold">
정말 탈퇴 하시겠습니까?
</div>
<div className="w-full flex gap-5 mt-10">
<Button onClick={handleUserAccountDelete} color="danger" className="w-full">
탈퇴
</Button>
<Button onClick={handleClose} className="w-full">
취소
</Button>
</div>
</div>

<IoClose
onClick={onClose}
title="닫기"
className="absolute text-gray-400 dark:hover:text-gray-100 hover:text-gray-800 transition cursor-pointer w-[28px] h-[28px] top-2 right-2"
/>
</motion.nav>
<AnimatePresence>
{isAlert && (
<motion.div
initial={{ translateY: -100 }}
animate={{ translateY: 0 }}
exit={{ translateY: -100 }}
transition={{ type: "spring", duration: 1 }}
className="fixed flex items-center gap-2 p-2 bg-orange-600 bg-opacity-75 rounded-lg top-14 text-background"
>
<IoWarning />
<div>{alertMsg}</div>
</motion.div>
)}
</AnimatePresence>
</div>
</>
);
};

export default ModalUserDelete;
4 changes: 2 additions & 2 deletions src/pages/MyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { useNavigate, useParams } from "react-router-dom";
import { AxiosError } from "axios";

const MyPage = () => {
const [isUserMypage, setIsUserMypage] = useState<boolean>(false); //마이페이지에 들어온 유저가 본인인지 아닌지 확인할거
const [isUserMypage, setIsUserMypage] = useState<boolean>(false);
const { user, updateUser, initOtherUser } = useUserStore();
const { userId } = useParams(); // 유저 클릭해서 마이페이지 보는 경우 볼려는 유저의 아이디
const { userId } = useParams();
const navigate = useNavigate();

useEffect(() => {
Expand Down

0 comments on commit eab5520

Please sign in to comment.