From a1a01432987fb0e9127bfec00dd49902c935acb6 Mon Sep 17 00:00:00 2001 From: yoonyesol Date: Fri, 16 Aug 2024 19:35:46 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20#69=20=EC=9C=A0=EC=A0=80=20=EA=B0=9C?= =?UTF-8?q?=EC=9D=B8=EC=A0=95=EB=B3=B4=20=EC=84=A4=EC=A0=95=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/ValidationInput.tsx | 8 +- src/pages/setting/UserSettingPage.tsx | 262 +++++++++++++++++++++- 2 files changed, 267 insertions(+), 3 deletions(-) diff --git a/src/components/common/ValidationInput.tsx b/src/components/common/ValidationInput.tsx index 3a4f9ba3..8e81ba72 100644 --- a/src/components/common/ValidationInput.tsx +++ b/src/components/common/ValidationInput.tsx @@ -6,7 +6,8 @@ import { RiEyeFill, RiEyeOffFill } from 'react-icons/ri'; * ValidationInput 컴포넌트 params, 모든 params는 optional * * @params {string} [label] - 입력 필드의 label 텍스트 - * @params {boolean} [required] - 입력 필드 필수 여부 + * @params {boolean} [disabled] - 입력 필드 disabled 여부. 기본값은 'false' + * @params {boolean} [required] - 입력 필드 필수 여부. 기본값은 'true' * @params {string} [errors] - 유효성 검증 후 오류 발생 시 표시할 오류 메시지 * @params {UseFormRegisterReturn} [register] - react-hook-form의 resister 함수. * register('password', {required: ...})부분을 그대로 파라미터에 넣으면 됩니다. @@ -30,6 +31,7 @@ import { RiEyeFill, RiEyeOffFill } from 'react-icons/ri'; type ValidationInputProps = { label?: string; + disabled?: boolean; required?: boolean; errors?: string; register?: UseFormRegisterReturn; @@ -42,6 +44,7 @@ type ValidationInputProps = { export default function ValidationInput({ label, + disabled = false, required = true, errors, register, @@ -70,10 +73,11 @@ export default function ValidationInput({
UserSettingPage
; + const [imageUrl, setImageUrl] = useState(''); + const [isFocused, setIsFocused] = useState(false); + const [link, setLink] = useState(''); + const [linksList, setLinksList] = useState(USER_INFO_DUMMY.links); + const { toastSuccess, toastError, toastWarn } = useToast(); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + setValue, + } = useForm({ + mode: 'onChange', + defaultValues: { + id: USER_INFO_DUMMY.id, + email: USER_INFO_DUMMY.email, + code: '', + nickname: USER_INFO_DUMMY.nickname, + bio: USER_INFO_DUMMY.bio, + links: USER_INFO_DUMMY.links, + }, + }); + + // 이미지 관련 코드 + const handleChangeImg = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + + if (file.size > USER_SETTINGS.MAX_IMAGE_SIZE) { + toastWarn(`최대 ${convertBytesToString(USER_SETTINGS.MAX_IMAGE_SIZE)} 이하의 이미지 파일만 업로드 가능합니다.`); + e.target.value = ''; + return; + } + + setImageUrl(URL.createObjectURL(file)); + }; + + const handleRemoveImg = () => { + setValue('profileUrl', ''); + setImageUrl(''); + }; + + // 웹사이트 링크 관련 코드 + const handleFocus = () => { + setIsFocused(true); + }; + + const handleBlur = () => { + setIsFocused(false); + }; + + const handleLinkChange = (e: ChangeEvent) => { + setLink(e.target.value); + }; + + const handleAddLink = (newLink: string) => { + if (newLink.trim() === '') return; + if (linksList.length === USER_SETTINGS.MAX_LINK_COUNT) + return toastWarn(`링크는 최대 ${USER_SETTINGS.MAX_LINK_COUNT}개까지 등록할 수 있습니다.`); + + setLinksList([...linksList, newLink.trim()]); + setValue('links', [...linksList, newLink.trim()]); + setLink(''); + }; + + const handleRemoveLink = (removeLink: string) => { + const filteredData = linksList.filter((item) => item !== removeLink); + setLinksList(filteredData); + setValue('links', filteredData); + }; + + // form 전송 함수 + const onSubmit = async (data: UserSignUpForm) => { + const { id, ...filteredData } = data; + + // TODO: 폼 제출 로직 수정 필요 + try { + // 프로필 수정 폼 + const registrationResponse = await axios.post(`http://localhost:8080/api/v1/user/${id}`, filteredData); + if (registrationResponse.status !== 200) return toastError('프로필 수정에 실패했습니다. 다시 시도해 주세요.'); + + // 이미지 폼 + if (!imageUrl) return toastSuccess('프로필 수정이 완료되었습니다.'); + const imgFormData = new FormData(); + try { + const jpeg = await reduceImageSize(imageUrl); + const file = new File([jpeg], new Date().toISOString(), { type: 'image/jpeg' }); + imgFormData.append('profile', file); + imgFormData.append('id', id); + + const imageResponse = await axios.post(`http://localhost:8080/api/v1/users/file`, imgFormData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }); + + if (imageResponse.status !== 200) return toastError('이미지 업로드에 실패했습니다. 다시 시도해 주세요.'); + + toastSuccess('프로필 수정이 완료되었습니다.'); + } catch (err) { + toastError(`이미지 업로드에 실패했습니다: ${err}`); + } + } catch (error) { + toastError(`프로필 수정 진행 중 오류가 발생했습니다: ${error}`); + } + }; + + return ( +
+
+ {/* 프로필 이미지 */} +
+
+ {imageUrl ? ( + <> + profileImage +
+

+ +

+
+ + ) : ( + + )} +
+
+ + {/* 아이디 */} + + + {/* 이메일 */} + + + {/* 닉네임, 중복 확인 */} + + + {/* 자기소개 */} +
+ +