From 2b7c0860f65a9e1a65c35cf5c81712a3aeda4b09 Mon Sep 17 00:00:00 2001 From: choi Date: Fri, 20 Dec 2024 17:20:19 +0900 Subject: [PATCH] =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 10 ++ package.json | 1 + src/components/bar/setting-menu.tsx | 3 + src/components/modal/profileEdit-modal.tsx | 112 ++++++++++++--------- src/services/profileService.ts | 6 ++ 5 files changed, 87 insertions(+), 45 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0a83bb6..2715028 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "clsx": "^2.1.1", "cookie": "^1.0.1", "framer-motion": "^11.9.0", + "js-cookie": "^3.0.5", "lucide-react": "^0.446.0", "next": "14.2.13", "next-themes": "^0.3.0", @@ -4611,6 +4612,15 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index e64e41b..bc99a5d 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "clsx": "^2.1.1", "cookie": "^1.0.1", "framer-motion": "^11.9.0", + "js-cookie": "^3.0.5", "lucide-react": "^0.446.0", "next": "14.2.13", "next-themes": "^0.3.0", diff --git a/src/components/bar/setting-menu.tsx b/src/components/bar/setting-menu.tsx index d739ea9..27bdf31 100644 --- a/src/components/bar/setting-menu.tsx +++ b/src/components/bar/setting-menu.tsx @@ -15,8 +15,10 @@ import { IconLogout, IconSettings, } from "@tabler/icons-react"; +import { useRouter } from "next/navigation"; export function SettingMenu() { + const router = useRouter(); const { logout } = useUser(); const { isLoggedIn } = useAuth(); const signInModal = useSigninModal(); @@ -28,6 +30,7 @@ export function SettingMenu() { const handleLogout = async () => { logout(); + router.refresh(); } const toggleTheme = useCallback(() => { diff --git a/src/components/modal/profileEdit-modal.tsx b/src/components/modal/profileEdit-modal.tsx index 3a26ee8..1a2176b 100644 --- a/src/components/modal/profileEdit-modal.tsx +++ b/src/components/modal/profileEdit-modal.tsx @@ -15,11 +15,13 @@ import ModalTitle from "./modal-title"; import { Textarea } from "../ui/textarea"; import { CustomModal } from "./custom-modal"; import { api } from "@/lib/axios"; +import { getProfileUUID } from "@/services/profileService"; const ProfileEditModal = () => { const [file, setFile] = useState(null); const [isLoading, setIsloading] = useState(false); const profileEditModal = useProfileEditModal(); + const uuid = getProfileUUID(); const onChange = (open: boolean) => { if (!open) { @@ -45,15 +47,11 @@ const ProfileEditModal = () => { const formData = new FormData(); formData.append("file", file); - const response = await api.post( - `/upload/images`, - formData, - { - headers: { - "Content-Type": "multipart/form-data", - }, - } - ); + const response = await api.post(`/upload/images`, formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); if (response.status === 201) { return response.data; @@ -63,17 +61,24 @@ const ProfileEditModal = () => { }; const FormSchema = z.object({ - name: z.string().min(1, "이름은 필수입니다.").max(20, "20자 이하로 입력하세요"), - description: z.string().max(500, "소개는 500자 이내로 작성하세요").optional(), - link1: z - .string() - .regex(/^https?:\/\//, "http:// 또는 https:// 형식의 URL을 입력하세요") - .optional(), - link2: z - .string() - .regex(/^https?:\/\//, "http:// 또는 https:// 형식의 URL을 입력하세요") - .optional(), -}); + name: z + .string() + .min(1, "1자 이상으로 작성하세요.") + .max(20, "20자 이하로 입력하세요") + .optional(), + description: z + .string() + .max(500, "소개는 500자 이내로 작성하세요") + .optional(), + link1: z + .string() + .regex(/^https?:\/\//, "http:// 또는 https:// 형식의 URL을 입력하세요") + .optional(), + link2: z + .string() + .regex(/^https?:\/\//, "http:// 또는 https:// 형식의 URL을 입력하세요") + .optional(), + }); const { register, @@ -94,44 +99,57 @@ const ProfileEditModal = () => { const onSubmit: SubmitHandler = async (values) => { try { - setIsloading(true); + const hasChanges = + Object.values(values).some( + (value) => value !== null && value !== "" && value !== undefined + ) || file !== null; - const profileImage = file; - if (!profileImage) { - toast.error("프로필 사진이 필요합니다."); + if (!hasChanges) { + toast.error("변경된 내용이 없습니다."); return; } - const profileImageUrl = await uploadToS3(profileImage); + setIsloading(true); - const requestData = { - ...values, - profileImage: profileImageUrl, - }; + let requestData = { ...values }; - const response = await api.patch( - `/profile`, - requestData, - { - withCredentials: true, + if (file) { + try { + const profileImageUrl = await uploadToS3(file); + requestData.profileImage = profileImageUrl; + } catch (error) { + toast.error("이미지 업로드에 실패했습니다."); + return; } + } + + const filteredRequestData = Object.fromEntries( + Object.entries(requestData).filter( + ([_, value]) => value !== null && value !== "" && value !== undefined + ) ); - if (response.status !== 200) { - throw new Error("프로필 수정에 실패했습니다."); + if (!uuid) { + toast.error("프로필 정보를 찾을 수 없습니다."); + return; } + const response = await api.patch( + `/profile/${uuid}`, + filteredRequestData, + { withCredentials: true } + ); + toast.success("프로필이 수정되었습니다."); reset(); profileEditModal.onClose(); } catch (error) { - if (axios.isAxiosError(error) && error.response?.data) { - const errorData = error.response.data; - toast.error( - errorData.detail || "프로필 수정 중 오류가 발생했습니다." - ); + if (axios.isAxiosError(error)) { + const errorMessage = + error.response?.data?.detail || "프로필 수정 중 오류가 발생했습니다."; + toast.error(errorMessage); } else { - toast.error("프로필 수정 과정에서 오류가 발생했습니다."); + toast.error("프로필 수정 중 오류가 발생했습니다."); } } finally { setIsloading(false); @@ -179,8 +197,8 @@ const ProfileEditModal = () => {

@@ -218,7 +236,11 @@ const ProfileEditModal = () => { 취소 -