From 35e8b59ee0e84e5d307c20c675c7ae48414a67d7 Mon Sep 17 00:00:00 2001 From: Soo Date: Fri, 13 Dec 2024 16:02:02 +0900 Subject: [PATCH 01/11] =?UTF-8?q?Fix=20:=203=EC=B0=A8=20QA=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/_components/DaedLineGather/index.tsx | 3 +-- .../_components/MainNav/MainNav.module.scss | 4 ++-- src/app/mypage/_components/Info/Info.tsx | 18 ++++++++++-------- .../_components/deleteModal/index.tsx | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/app/main/_components/DaedLineGather/index.tsx b/src/app/main/_components/DaedLineGather/index.tsx index ca720c4a..80d6847d 100644 --- a/src/app/main/_components/DaedLineGather/index.tsx +++ b/src/app/main/_components/DaedLineGather/index.tsx @@ -56,8 +56,7 @@ export default function DeadLineGather({ meetingList }: DeadLineGatherProps) { if (timeDiff < 60 * 1000) return `1분 후 마감`; // 1분 미만 if (timeDiff < 60 * 60 * 1000) return `${minutesLeft}분 후 마감`; // 1시간 미만 - if (timeDiff < 24 * 60 * 60 * 1000) - return `${hoursLeft}시간 ${minutesLeft > 0 ? `${minutesLeft}분` : ''} 후 마감`; // 하루 미만 + if (timeDiff < 24 * 60 * 60 * 1000) return `${hoursLeft}시간 후 마감`; // 하루 미만 return `${daysLeft}일 ${hoursLeft > 0 ? `${hoursLeft}시간` : ''} 후 마감`; // 하루 이상 }; diff --git a/src/app/main/_components/MainNav/MainNav.module.scss b/src/app/main/_components/MainNav/MainNav.module.scss index eeeb836c..633a976c 100644 --- a/src/app/main/_components/MainNav/MainNav.module.scss +++ b/src/app/main/_components/MainNav/MainNav.module.scss @@ -45,11 +45,11 @@ } // 430px 이하 화면에 대한 반응형 스타일 추가 -@media (max-width: 430px) { +@media (max-width: 540px) { .mainNav { flex-direction: column; width: 90%; - margin-top: 20px; + margin-top: 50px; padding: 0px; .mainNavContent { diff --git a/src/app/mypage/_components/Info/Info.tsx b/src/app/mypage/_components/Info/Info.tsx index 98e31124..2d600afb 100644 --- a/src/app/mypage/_components/Info/Info.tsx +++ b/src/app/mypage/_components/Info/Info.tsx @@ -79,14 +79,16 @@ export default function Info({

내 프로필

- + {loggedIn && ( + + )}
diff --git a/src/app/mypage/myGatherings/_components/deleteModal/index.tsx b/src/app/mypage/myGatherings/_components/deleteModal/index.tsx index 9a3fd54e..69b68759 100644 --- a/src/app/mypage/myGatherings/_components/deleteModal/index.tsx +++ b/src/app/mypage/myGatherings/_components/deleteModal/index.tsx @@ -28,7 +28,7 @@ export default function DeleteModal({ }, 500); } catch (error) { if (axios.isAxiosError(error) && error.response) { - if (error.response.status === 400) { + if (error.response.status === 400 || error.response.status === 404) { addToast('참가인원이 존재하여 삭제할수 없습니다', 'error'); } else { alert( From 4752c3a94e661e23dcf17795ec5bfe6f9604bce6 Mon Sep 17 00:00:00 2001 From: Soo Date: Tue, 17 Dec 2024 15:49:51 +0900 Subject: [PATCH 02/11] =?UTF-8?q?Fix=20:=203=EC=B0=A8=20QA=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=20=EC=A4=91=EA=B0=84=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_components/Header/Header.module.scss | 2 +- .../mypage/_components/Info/info.module.scss | 7 +++++ .../_components/infoEdit/infoEdit.module.scss | 6 ++-- .../mypage/_components/infoEdit/infoEdit.tsx | 4 +-- .../profileImageEdit.module.scss | 19 ++++++------ .../myGatherings/myGatherings.module.scss | 31 +++++++++++++++++-- src/app/mypage/mypage.module.scss | 17 ++++++++++ src/app/mypage/page.tsx | 26 ++++++++++------ 8 files changed, 85 insertions(+), 27 deletions(-) diff --git a/src/app/main/_components/Header/Header.module.scss b/src/app/main/_components/Header/Header.module.scss index 640712a1..8aaf1027 100644 --- a/src/app/main/_components/Header/Header.module.scss +++ b/src/app/main/_components/Header/Header.module.scss @@ -168,7 +168,7 @@ } .right { - width: 100%; + // width: 100%; justify-content: end; .headerMyapgeButton { width: 28px; diff --git a/src/app/mypage/_components/Info/info.module.scss b/src/app/mypage/_components/Info/info.module.scss index 0ac35d0f..59bbf3f6 100644 --- a/src/app/mypage/_components/Info/info.module.scss +++ b/src/app/mypage/_components/Info/info.module.scss @@ -205,6 +205,13 @@ // 반응형 스타일 추가 @media (max-width: 430px) { + .noneResi { + font-size: 13px; + display: block; + margin-top: 30px; + text-align: center; + line-height: 140%; + } .card { .top { padding: 15px; diff --git a/src/app/mypage/_components/infoEdit/infoEdit.module.scss b/src/app/mypage/_components/infoEdit/infoEdit.module.scss index c9db8977..ac959d8d 100644 --- a/src/app/mypage/_components/infoEdit/infoEdit.module.scss +++ b/src/app/mypage/_components/infoEdit/infoEdit.module.scss @@ -14,7 +14,7 @@ .title { display: block; margin-top: 15px; - font-size: 26px; + font-size: 24px; font-weight: bold; } .nameInput { @@ -157,7 +157,7 @@ .infoEditModal { .logoTitle { display: block; - font-size: 32px; + font-size: 24px; font-weight: bold; margin-top: 30px; color: #007aff; @@ -170,7 +170,7 @@ .title { display: block; margin-top: 15px; - font-size: 26px; + font-size: 20px; font-weight: bold; } .nameInput { diff --git a/src/app/mypage/_components/infoEdit/infoEdit.tsx b/src/app/mypage/_components/infoEdit/infoEdit.tsx index efe7152f..522dfdce 100644 --- a/src/app/mypage/_components/infoEdit/infoEdit.tsx +++ b/src/app/mypage/_components/infoEdit/infoEdit.tsx @@ -308,12 +308,12 @@ export default function InfoEdit({ }}> 수정하기 - + */}
); diff --git a/src/app/mypage/_components/profileImageEdit/profileImageEdit.module.scss b/src/app/mypage/_components/profileImageEdit/profileImageEdit.module.scss index 0891072f..dc5da002 100644 --- a/src/app/mypage/_components/profileImageEdit/profileImageEdit.module.scss +++ b/src/app/mypage/_components/profileImageEdit/profileImageEdit.module.scss @@ -99,14 +99,14 @@ } .preview { - width: 200px; - height: 200px; + width: 100px; + height: 100px; margin: 0 auto; position: relative; .previewImgWrap { - width: 200px; - height: 200px; + width: 100px; + height: 100px; display: block; overflow: hidden; margin: 0 auto; @@ -122,18 +122,19 @@ .inputWrap { display: block; margin-top: 20px; - margin-bottom: 50px; + margin-bottom: 0px; position: absolute; - bottom: -20%; - right: 0; + bottom: 0%; + right: -25%; input { display: none; } input + label { + width: auto; img { display: block; - width: 100%; - height: 100%; + width: 50%; + height: 50%; background: #fff; border-radius: 100%; cursor: pointer; diff --git a/src/app/mypage/myGatherings/myGatherings.module.scss b/src/app/mypage/myGatherings/myGatherings.module.scss index 2cbcf007..4fba1c62 100644 --- a/src/app/mypage/myGatherings/myGatherings.module.scss +++ b/src/app/mypage/myGatherings/myGatherings.module.scss @@ -72,9 +72,10 @@ font-weight: bold; } - @media (max-width: 430px) { + @media (max-width: 530px) { font-size: 16px; gap: 30px; + word-break: keep-all; } } } @@ -183,6 +184,7 @@ border: 1px solid #007aff; border-radius: 15px; width: 35%; + word-break: keep-all; @media (max-width: 430px) { padding: 10px 15px; @@ -193,7 +195,7 @@ } } - @media (max-width: 430px) { + @media (max-width: 480px) { padding: 0 16px; padding-top: 60px; padding-bottom: 100px; @@ -213,21 +215,44 @@ h1 { font-size: 16px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: block; + width: 100%; } b { font-size: 14px; padding: 10px 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: block; + width: 100%; } p { flex-direction: column; align-items: flex-start; gap: 5px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: block; + width: 100%; - .time, + .time { + font-size: 12px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: block; + width: 100%; + } .person { font-size: 12px; + margin-top: 10px; } } diff --git a/src/app/mypage/mypage.module.scss b/src/app/mypage/mypage.module.scss index c7221867..2343592e 100644 --- a/src/app/mypage/mypage.module.scss +++ b/src/app/mypage/mypage.module.scss @@ -6,6 +6,23 @@ font-weight: 900; font-family: 'LaundryGothicOTF-Bold'; } +.infoSkeleton { + width: 100%; + background: #333; + padding: 100px 0; + border-radius: 15px; + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: skeleton-animation 1.5s infinite linear; +} +@keyframes skeleton-animation { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} .relative { position: relative; diff --git a/src/app/mypage/page.tsx b/src/app/mypage/page.tsx index 097923b4..265d78f6 100644 --- a/src/app/mypage/page.tsx +++ b/src/app/mypage/page.tsx @@ -22,7 +22,7 @@ export default function MyPage() { const [prOpen, setPrOpen] = useState(false); const [animatedRating, setAnimatedRating] = useState(0); // Initial rating state const [checkedLogin, setCheckedLogin] = useState(null); // state to hold checkedLogin - // const [loading, setLoading] = useState(true); // 로딩 상태 추가 + const [loading, setLoading] = useState(true); // 로딩 상태 추가 // 클라이언트 사이드에서만 localStorage를 접근 useEffect(() => { const token = localStorage.getItem('accessToken'); @@ -36,6 +36,7 @@ export default function MyPage() { // 사용자 정보 가져오기 const fetchPersonalInfo = async () => { + setLoading(true); try { const response = await getPersonalInfo(); const targetRating = response.data.averageRating; @@ -64,6 +65,8 @@ export default function MyPage() { setInfo(response.data); } catch (err) { // console.error('Failed to fetch personal info:', err); + } finally { + setLoading(false); } }; @@ -93,14 +96,19 @@ export default function MyPage() { )}

마이페이지

-
- -
+ {loading === true ? ( +
+ ) : ( +
+ +
+ )} +
매너능력치
From 207c53cdcc5f9e3f46550b8dcf3b2be4f630269a Mon Sep 17 00:00:00 2001 From: KIMSOOHWAN Date: Thu, 19 Dec 2024 16:12:02 +0900 Subject: [PATCH 03/11] =?UTF-8?q?Fix=20:=20=EA=B0=9C=EC=9D=B8=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EB=B3=80=EA=B2=BD=20=EB=B6=80=EB=B6=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mypage/_components/infoEdit/infoEdit.tsx | 136 +++++++++++++----- 1 file changed, 102 insertions(+), 34 deletions(-) diff --git a/src/app/mypage/_components/infoEdit/infoEdit.tsx b/src/app/mypage/_components/infoEdit/infoEdit.tsx index 522dfdce..d922714c 100644 --- a/src/app/mypage/_components/infoEdit/infoEdit.tsx +++ b/src/app/mypage/_components/infoEdit/infoEdit.tsx @@ -13,8 +13,8 @@ interface IInfoEditProps { handleEditOpen: () => void; updateInfo: () => void; mypageInfo: { - profileImage: string | null; - nickName: string; + profileImage: string | any; + nickName: string | any; }; } @@ -52,6 +52,8 @@ export default function InfoEdit({ const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [isPasswordMatched, setIsPasswordMatched] = useState(true); + // 비밀번호 유효성 검사 + const passwordRegex = /^[a-zA-Z0-9!@#$%^&*()_+=[\]{};':"\\|,.<>/?~-]{8,50}$/; const togglePasswordVisibility = () => setShowPassword(prev => !prev); const toggleConfirmPasswordVisibility = () => @@ -65,7 +67,41 @@ export default function InfoEdit({ }; // 비밀번호와 비밀번호 확인 비교 함수 + // const validatePasswords = () => { + // if (passwordValue !== confirmPasswordValue) { + // setIsPasswordMatched(false); + // setError('confirmPassword', { + // type: 'manual', + // message: '비밀번호가 일치하지 않습니다.', + // }); + // } else { + // setIsPasswordMatched(true); + // clearErrors('confirmPassword'); + // } + // }; + const validatePassword = () => { + if (!passwordValue) { + clearErrors('password'); // 비밀번호가 없으면 에러 제거 + return; + } + if (!passwordRegex.test(passwordValue)) { + setError('password', { + type: 'manual', + message: + '비밀번호는 8자 이상 50자 이하, 영어, 숫자, 특수문자만 사용 가능합니다.', + }); + setIsPasswordMatched(false); // 비밀번호 확인도 일치하지 않게 처리 + } else { + clearErrors('password'); + validatePasswords(); // 비밀번호 확인과의 일치 여부 확인 + } + }; const validatePasswords = () => { + if (!passwordValue || !confirmPasswordValue) { + setIsPasswordMatched(false); // 입력이 없을 때 기본적으로 불일치로 간주 + return; + } + if (passwordValue !== confirmPasswordValue) { setIsPasswordMatched(false); setError('confirmPassword', { @@ -73,14 +109,18 @@ export default function InfoEdit({ message: '비밀번호가 일치하지 않습니다.', }); } else { - setIsPasswordMatched(true); clearErrors('confirmPassword'); + setIsPasswordMatched(true); } }; useEffect(() => { - validatePasswords(); - }, [passwordValue, confirmPasswordValue]); + validatePassword(); // 비밀번호 유효성 검사 + }, [passwordValue]); + + useEffect(() => { + validatePasswords(); // 비밀번호 확인 검사 + }, [confirmPasswordValue]); // 닉네임 유효성 검사 함수 const validateNickname = (nickName: string): boolean => { @@ -113,16 +153,17 @@ export default function InfoEdit({ clearErrors('nickName'); } catch (error: any) { if (error.response?.status === 400) { + setIsNameChecked(false); // 닉네임 체크 실패 시 상태 초기화 setIsNameDuplicate(true); setError('nickName', { type: 'manual', message: '이미 사용 중인 닉네임입니다.', }); setErrorMessage(''); - addToast('이미 사용 중인 닉네임입니다.', 'error'); + // addToast('이미 사용 중인 닉네임입니다.', 'error'); } else { setErrorMessage('닉네임 확인 중 오류가 발생했습니다.'); - addToast('닉네임 확인 중 오류가 발생했습니다.', 'error'); + // addToast('닉네임 확인 중 오류가 발생했습니다.', 'error'); } setIsNameChecked(true); } @@ -130,46 +171,62 @@ export default function InfoEdit({ // 개인정보 수정 제출 함수 const onSubmit: SubmitHandler = async _data => { - if (!isPasswordMatched) { - addToast( - '비밀번호가 일치하지 않습니다. 다시 한번 확인 해주세요.', - 'error' - ); - return; - } - try { - const updateData: { nickName?: string; password?: string } = {}; + const updateData: { nickName?: any; password?: any } = {}; if (nameValue && isNameChecked && !isNameDuplicate) { updateData.nickName = nameValue; } - if (passwordValue.trim()) { + if (passwordValue && passwordValue.trim()) { updateData.password = passwordValue; } - if (Object.keys(updateData).length === 0) { - addToast('수정할 항목이 없습니다.', 'error'); - return; - } - - // `updatePersonalInfo` 호출 시, `undefined`인 매개변수를 처리 - await updatePersonalInfo( - updateData.nickName || '', // `nickName`이 `undefined`일 경우 빈 문자열로 처리 - updateData.password || '' // `password`가 `undefined`일 경우 빈 문자열로 처리 - ); + // if (Object.keys(updateData).length === 0) { + // addToast('수정할 항목이 없습니다.', 'error'); + // return; + // } + console.log(updateData); + await updatePersonalInfo(updateData?.nickName, updateData?.password); + addToast('개인정보가 수정되었습니다.', 'success'); updateInfo(); reset(); handleEditOpen(); - addToast('개인정보가 수정되었습니다.', 'success'); + + // 성공 후 새로고침이 필요하다면 여기서 처리 + setTimeout(() => { + window.location.reload(); + }, 1000); } catch (error) { - addToast('정보 수정 중 오류가 발생했습니다.', 'error'); + console.log('정보 수정 중 오류가 발생했습니다.', 'error'); + } finally { + console.log('파이널리'); } }; - const isFormValid = isPasswordMatched && (isNameChecked || !!passwordValue); + const isFormValid = + (passwordValue && isPasswordMatched && !errors.password) || + (nameValue.trim() && !isNameDuplicate && isNameChecked); + useEffect(() => { + console.log({ + nameValue, + passwordValue, + confirmPasswordValue, + isNameChecked, + isNameDuplicate, + isPasswordMatched, + }); + }, [ + nameValue, + passwordValue, + confirmPasswordValue, + isNameChecked, + isNameDuplicate, + isPasswordMatched, + ]); + console.log('isFormValid 상태:', isFormValid); + console.log('버튼 활성화 상태:', !isFormValid ? '비활성화' : '활성화'); return (
@@ -201,7 +258,7 @@ export default function InfoEdit({ type="text" placeholder="변경하고 싶은 닉네임을 입력해주세요." {...register('nickName', { - required: '닉네임을 입력해주세요.', + // required: '닉네임을 입력해주세요.', validate: validateNickname, })} onChange={e => { @@ -261,7 +318,11 @@ export default function InfoEdit({ )}
+ {errors.password && ( +
{errors.password.message}
+ )}
+
비밀번호 확인 (선택)
@@ -293,13 +354,14 @@ export default function InfoEdit({ )}
- {!isPasswordMatched && ( + {errors.confirmPassword && (
- 비밀번호가 일치하지 않습니다. + {errors.confirmPassword.message}
)}
- */} + {/*
diff --git a/src/app/main/_components/newGather/page.tsx b/src/app/main/_components/newGather/page.tsx index 778910f0..19c86e87 100644 --- a/src/app/main/_components/newGather/page.tsx +++ b/src/app/main/_components/newGather/page.tsx @@ -103,6 +103,10 @@ export default function NewGather({ meetingList }: NewGatherProps) { width={224} height={224} unoptimized={true} + onError={e => { + e.currentTarget.src = + '/assets/images/emptyThumbnail.png'; + }} /> diff --git a/src/app/mypage/_components/infoEdit/infoEdit.tsx b/src/app/mypage/_components/infoEdit/infoEdit.tsx index d922714c..8cfa7ee3 100644 --- a/src/app/mypage/_components/infoEdit/infoEdit.tsx +++ b/src/app/mypage/_components/infoEdit/infoEdit.tsx @@ -13,8 +13,8 @@ interface IInfoEditProps { handleEditOpen: () => void; updateInfo: () => void; mypageInfo: { - profileImage: string | any; - nickName: string | any; + profileImage: string; + nickName: string; }; } @@ -171,62 +171,64 @@ export default function InfoEdit({ // 개인정보 수정 제출 함수 const onSubmit: SubmitHandler = async _data => { + console.log('onSubmit 호출됨'); // 로그 확인용 try { - const updateData: { nickName?: any; password?: any } = {}; + // 업데이트 데이터 생성 + const updateData: { nickName?: string; password?: string } = {}; - if (nameValue && isNameChecked && !isNameDuplicate) { - updateData.nickName = nameValue; + // 닉네임 수정 조건 + if (nameValue && (isNameChecked || !isNameDuplicate)) { + updateData.nickName = nameValue; // 닉네임이 비어있어도 isNameChecked가 true이면 업데이트 } + // 비밀번호 수정 조건 if (passwordValue && passwordValue.trim()) { updateData.password = passwordValue; } - // if (Object.keys(updateData).length === 0) { - // addToast('수정할 항목이 없습니다.', 'error'); - // return; - // } - console.log(updateData); - await updatePersonalInfo(updateData?.nickName, updateData?.password); + console.log('updateData:', updateData); // 값 확인 + // 수정 항목이 없을 경우 처리 + if (!updateData.nickName && !updateData.password) { + addToast('수정할 항목이 없습니다.', 'error'); + return; + } + + // `updatePersonalInfo` 호출 + await updatePersonalInfo( + updateData.nickName || '', // 닉네임: 빈 문자열로 기본값 설정 + updateData.password || '' // 비밀번호: 빈 문자열로 기본값 설정 + ); + + // 성공 메시지 및 UI 업데이트 addToast('개인정보가 수정되었습니다.', 'success'); updateInfo(); reset(); handleEditOpen(); - // 성공 후 새로고침이 필요하다면 여기서 처리 + // 성공 후 새로고침 (선택적) setTimeout(() => { window.location.reload(); }, 1000); } catch (error) { - console.log('정보 수정 중 오류가 발생했습니다.', 'error'); + console.error('정보 수정 중 오류가 발생했습니다.', error); } finally { console.log('파이널리'); } }; const isFormValid = - (passwordValue && isPasswordMatched && !errors.password) || - (nameValue.trim() && !isNameDuplicate && isNameChecked); - useEffect(() => { - console.log({ - nameValue, - passwordValue, - confirmPasswordValue, - isNameChecked, - isNameDuplicate, - isPasswordMatched, - }); - }, [ - nameValue, - passwordValue, - confirmPasswordValue, - isNameChecked, - isNameDuplicate, - isPasswordMatched, - ]); - console.log('isFormValid 상태:', isFormValid); - console.log('버튼 활성화 상태:', !isFormValid ? '비활성화' : '활성화'); + (passwordValue && isPasswordMatched && !errors.password) || // 비밀번호 관련 조건 + (nameValue.trim() && !isNameDuplicate && isNameChecked); // 닉네임 관련 조건 + + console.log('isFormValid:', isFormValid); + console.log('passwordValue:', passwordValue); + console.log('isPasswordMatched:', isPasswordMatched); + console.log('errors.password:', errors.password); + console.log('nameValue:', nameValue); + console.log('isNameDuplicate:', isNameDuplicate); + console.log('isNameChecked:', isNameChecked); + console.log(mypageInfo); return (
@@ -261,6 +263,7 @@ export default function InfoEdit({ // required: '닉네임을 입력해주세요.', validate: validateNickname, })} + defaultValue={mypageInfo.nickName} onChange={e => { setNameValue(e.currentTarget.value); setIsNameChecked(false); @@ -324,7 +327,7 @@ export default function InfoEdit({
- 비밀번호 확인 (선택) + 비밀번호 확인
{ + e.currentTarget.onerror = null; // 추가 에러 핸들링 방지 + e.currentTarget.src = '/assets/images/emptyThumbnail.png'; + }} />
diff --git a/src/app/mypage/myGatherings/finish/page.tsx b/src/app/mypage/myGatherings/finish/page.tsx index 4be47f7e..4ef3574f 100644 --- a/src/app/mypage/myGatherings/finish/page.tsx +++ b/src/app/mypage/myGatherings/finish/page.tsx @@ -99,7 +99,7 @@ export default function Finish() { 참여 중 모임 썸네일 Date: Sun, 22 Dec 2024 17:05:21 +0900 Subject: [PATCH 05/11] =?UTF-8?q?Fix=20:=20console=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mypage/_components/infoEdit/infoEdit.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/mypage/_components/infoEdit/infoEdit.tsx b/src/app/mypage/_components/infoEdit/infoEdit.tsx index 8cfa7ee3..11be8b80 100644 --- a/src/app/mypage/_components/infoEdit/infoEdit.tsx +++ b/src/app/mypage/_components/infoEdit/infoEdit.tsx @@ -186,7 +186,7 @@ export default function InfoEdit({ updateData.password = passwordValue; } - console.log('updateData:', updateData); // 값 확인 + // console.log('updateData:', updateData); // 값 확인 // 수정 항목이 없을 경우 처리 if (!updateData.nickName && !updateData.password) { @@ -221,14 +221,14 @@ export default function InfoEdit({ (passwordValue && isPasswordMatched && !errors.password) || // 비밀번호 관련 조건 (nameValue.trim() && !isNameDuplicate && isNameChecked); // 닉네임 관련 조건 - console.log('isFormValid:', isFormValid); - console.log('passwordValue:', passwordValue); - console.log('isPasswordMatched:', isPasswordMatched); - console.log('errors.password:', errors.password); - console.log('nameValue:', nameValue); - console.log('isNameDuplicate:', isNameDuplicate); - console.log('isNameChecked:', isNameChecked); - console.log(mypageInfo); + // console.log('isFormValid:', isFormValid); + // console.log('passwordValue:', passwordValue); + // console.log('isPasswordMatched:', isPasswordMatched); + // console.log('errors.password:', errors.password); + // console.log('nameValue:', nameValue); + // console.log('isNameDuplicate:', isNameDuplicate); + // console.log('isNameChecked:', isNameChecked); + // console.log(mypageInfo); return (
From d37fc065e2d09325f0a8ccde3041bed35dec595a Mon Sep 17 00:00:00 2001 From: KIMSOOHWAN Date: Mon, 23 Dec 2024 16:14:06 +0900 Subject: [PATCH 06/11] =?UTF-8?q?Fix=20:=203=EC=B0=A8=20QA=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=A4=91,=20=EC=BA=90=EC=8B=B1=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=EB=B6=80=EB=B6=84=20=EB=B0=8F=20=EC=B0=9C=EC=88=AB?= =?UTF-8?q?=EC=9E=90=20=EB=A6=AC=EC=95=A1=ED=8A=B8=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=EC=AA=BD=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/main/_components/Header/Header.tsx | 6 + .../_components/gameRank/gameRank.module.scss | 7 +- src/app/main/_components/gameRank/index.tsx | 2 +- .../deleteModal/deleteModal.module.scss | 9 ++ .../_components/outModal/index.tsx | 1 + .../_components/outModal/outModal.module.scss | 21 +++ src/app/mypage/myGatherings/create/page.tsx | 6 +- src/app/mypage/myGatherings/finish/page.tsx | 6 +- .../myGatherings/myGatherings.module.scss | 2 + .../mypage/myGatherings/participant/page.tsx | 5 +- src/app/mypage/page.tsx | 133 ++++++------------ .../settingAlarm/settingAlarm.module.scss | 1 + 12 files changed, 100 insertions(+), 99 deletions(-) diff --git a/src/app/main/_components/Header/Header.tsx b/src/app/main/_components/Header/Header.tsx index 6be2b971..a3eb4506 100644 --- a/src/app/main/_components/Header/Header.tsx +++ b/src/app/main/_components/Header/Header.tsx @@ -4,6 +4,7 @@ import React, { useEffect, useState } from 'react'; import styles from './Header.module.scss'; import Image from 'next/image'; import Link from 'next/link'; +// import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { usePathname, useRouter } from 'next/navigation'; import { getLikeList, getPersonalInfo } from '@/api/apis/mypageApis'; import { getAlrmList } from '@/api/apis/headerApis'; @@ -25,6 +26,7 @@ interface INotification { } export default function Header() { + // const queryClient = useQueryClient(); const [info, setInfo] = useState(null); const [loggedIn, setLoggedIn] = useState(false); const [loading, setLoading] = useState(true); @@ -70,6 +72,10 @@ export default function Header() { fetchGetLikeList(); }, []); // Empty dependency array ensures this runs only once when the component mounts + // // 찜 리스트를 가져오는 Query + // const { data: likeList, isLoading } = useQuery(['likeList'], getLikeList, { + // staleTime: 1000 * 60 * 5, // 5분 동안 데이터 유지 + // }); useEffect(() => { const token = localStorage.getItem('accessToken'); diff --git a/src/app/main/_components/gameRank/gameRank.module.scss b/src/app/main/_components/gameRank/gameRank.module.scss index 8f612ab6..35253cb1 100644 --- a/src/app/main/_components/gameRank/gameRank.module.scss +++ b/src/app/main/_components/gameRank/gameRank.module.scss @@ -43,15 +43,16 @@ width: 20%; height: 125px; display: block; - padding: 10px; + padding: 5px; box-sizing: border-box; border: 1px solid #ddd; - border-radius: 5px; + border-radius: 3px; img { width: 100%; height: 100%; display: block; + background-size: contain; } } @@ -71,7 +72,7 @@ .name { display: block; width: 70%; - font-size: 22px; + font-size: 20px; font-weight: bold; line-clamp: 2; -webkit-line-clamp: 2; diff --git a/src/app/main/_components/gameRank/index.tsx b/src/app/main/_components/gameRank/index.tsx index 27b93ccf..ba565456 100644 --- a/src/app/main/_components/gameRank/index.tsx +++ b/src/app/main/_components/gameRank/index.tsx @@ -62,7 +62,7 @@ export default function GameRank() { // objectFit="cover" width={125} height={125} - src={`https://${cloud}/${e.thumbnail}`} + src={`https://${cloud}/${e?.thumbnail}`} alt="게임랭크 이미지" unoptimized={true} onError={e => { diff --git a/src/app/mypage/myGatherings/_components/deleteModal/deleteModal.module.scss b/src/app/mypage/myGatherings/_components/deleteModal/deleteModal.module.scss index 9c7d60fd..cd90002c 100644 --- a/src/app/mypage/myGatherings/_components/deleteModal/deleteModal.module.scss +++ b/src/app/mypage/myGatherings/_components/deleteModal/deleteModal.module.scss @@ -27,6 +27,11 @@ padding: 60px 15px; box-sizing: border-box; font-size: 18px; + @media (max-width: 430px) { + font-size: 16px; + padding: 30px 15px; + text-align: center; + } span { color: #007aff; font-weight: bold; @@ -44,6 +49,10 @@ padding: 25px 15px; box-sizing: border-box; font-size: 18px; + @media (max-width: 430px) { + font-size: 14px; + padding: 15px; + } &.ok { background: #fff; color: #333; diff --git a/src/app/mypage/myGatherings/_components/outModal/index.tsx b/src/app/mypage/myGatherings/_components/outModal/index.tsx index e8f2c67d..13c080b9 100644 --- a/src/app/mypage/myGatherings/_components/outModal/index.tsx +++ b/src/app/mypage/myGatherings/_components/outModal/index.tsx @@ -34,6 +34,7 @@ export default function OutModal({

{meetingTitle}에서 나가시겠습니까? +

모임에서 나가시면 더이상 같은 모임에는 참가 하실수 없습니다.

diff --git a/src/app/mypage/myGatherings/myGatherings.module.scss b/src/app/mypage/myGatherings/myGatherings.module.scss index 4fba1c62..287817be 100644 --- a/src/app/mypage/myGatherings/myGatherings.module.scss +++ b/src/app/mypage/myGatherings/myGatherings.module.scss @@ -103,6 +103,7 @@ border-bottom: 3px dashed #e5e7eb; @media (max-width: 430px) { width: 47%; + border-bottom: 0; } .img { @@ -274,6 +275,7 @@ justify-content: center; flex-direction: column; padding: 100px 0; + margin: 0 auto; h1 { display: block; diff --git a/src/app/mypage/myGatherings/participant/page.tsx b/src/app/mypage/myGatherings/participant/page.tsx index 5c59415c..df66ab9a 100644 --- a/src/app/mypage/myGatherings/participant/page.tsx +++ b/src/app/mypage/myGatherings/participant/page.tsx @@ -169,12 +169,15 @@ export default function Finish() { 참여 중 모임 썸네일 { + e.currentTarget.src = '/assets/images/emptyThumbnail.png'; + }} />
diff --git a/src/app/mypage/page.tsx b/src/app/mypage/page.tsx index 265d78f6..61b0251e 100644 --- a/src/app/mypage/page.tsx +++ b/src/app/mypage/page.tsx @@ -1,11 +1,13 @@ 'use client'; + import styles from './mypage.module.scss'; import Info from './_components/Info/Info'; import Link from 'next/link'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { getPersonalInfo } from '@/api/apis/mypageApis'; import InfoEdit from './_components/infoEdit/infoEdit'; import Image from 'next/image'; +import { useQuery } from '@tanstack/react-query'; // UserProfile 인터페이스의 이름을 I로 시작하도록 수정 interface IUserProfile { @@ -17,96 +19,56 @@ interface IUserProfile { } export default function MyPage() { - const [info, setInfo] = useState(null); // IUserProfile 타입 사용 - const [editOpen, setEditOpen] = useState(false); - const [prOpen, setPrOpen] = useState(false); - const [animatedRating, setAnimatedRating] = useState(0); // Initial rating state - const [checkedLogin, setCheckedLogin] = useState(null); // state to hold checkedLogin - const [loading, setLoading] = useState(true); // 로딩 상태 추가 - // 클라이언트 사이드에서만 localStorage를 접근 - useEffect(() => { - const token = localStorage.getItem('accessToken'); - setCheckedLogin(token); - // setLoading(false); // 로딩 완료 - }, []); + const [editOpen, setEditOpen] = useState(false); // 정보 수정 모달 상태 + const [prOpen, setPrOpen] = useState(false); // PR 태그 표시 상태 + + // React Query를 사용하여 사용자 정보 가져오기 + const { + data: userInfo, // 사용자 정보 + isLoading, // 로딩 상태 + refetch, // 정보 갱신 함수 + } = useQuery({ + queryKey: ['personalInfo'], // Query Key + queryFn: getPersonalInfo, // 데이터를 가져오는 함수 + staleTime: 5 * 60 * 1000, // 데이터가 5분 동안 신선하게 유지 + cacheTime: 10 * 60 * 1000, // 데이터가 10분 동안 캐시 + refetchOnWindowFocus: false, // 창 전환 시 자동 갱신 비활성화 + }); + // 정보 수정 모달 열기/닫기 핸들러 const handleEditOpen = () => { setEditOpen(prev => !prev); }; - // 사용자 정보 가져오기 - const fetchPersonalInfo = async () => { - setLoading(true); - try { - const response = await getPersonalInfo(); - const targetRating = response.data.averageRating; - - // Animate rating - let currentRating = 0; - const increment = 0.1; // Increment step - const animationDuration = 1000; // Duration in milliseconds - const interval = 16; // Interval in milliseconds (roughly 60fps) - - const step = (animationDuration / interval) * increment; // Calculate increment per frame - - const animate = () => { - if (currentRating < targetRating) { - currentRating += step; - if (currentRating > targetRating) currentRating = targetRating; // Cap at targetRating - setAnimatedRating(currentRating); - requestAnimationFrame(animate); - } else { - setAnimatedRating(targetRating); // Ensure final value is set - } - }; - - animate(); // Start animation - - setInfo(response.data); - } catch (err) { - // console.error('Failed to fetch personal info:', err); - } finally { - setLoading(false); - } - }; - - // 컴포넌트가 처음 렌더링될 때 사용자 정보를 가져옵니다. - useEffect(() => { - fetchPersonalInfo(); - }, []); - - // if (loading) { - // return
Loading...
; // 로딩 중 상태를 표시합니다. - // } - + // 평점 애니메이션 계산 + const animatedRating = userInfo?.data?.averageRating || 0; const ratingPercentage = (animatedRating / 5) * 100; return (
- {/* Conditionally render InfoEdit only if info is not null */} - {info && ( + {/* 정보 수정 모달 */} + {userInfo && (
)}

마이페이지

- {loading === true ? ( + + {/* 로딩 상태 및 사용자 정보 표시 */} + {isLoading ? (
) : ( -
- -
+ )}
@@ -115,22 +77,18 @@ export default function MyPage() {
+ width: `${ + ratingPercentage <= 20 ? 15 : Math.min(ratingPercentage, 100) + }%`, + }}>

{animatedRating.toFixed(1)}

+

@@ -144,13 +102,13 @@ export default function MyPage() {

    - {info?.prTags.map((item, i) =>
  • {item}
  • )} + {userInfo?.data?.prTags?.map((tag, index) => ( +
  • {tag}
  • + ))}
+
    - {/*
  • - PR 태그 수정 -
  • */}
  • 내 모임 @@ -199,8 +157,3 @@ export default function MyPage() {
); } -{ - /*
  • - 친구 목록 -
  • */ -} diff --git a/src/app/mypage/settingAlarm/settingAlarm.module.scss b/src/app/mypage/settingAlarm/settingAlarm.module.scss index eb4704e3..eab1803b 100644 --- a/src/app/mypage/settingAlarm/settingAlarm.module.scss +++ b/src/app/mypage/settingAlarm/settingAlarm.module.scss @@ -3,6 +3,7 @@ padding: 0 24px; box-sizing: border-box; padding-top: 60px; + padding-bottom: 150px; .popup { position: fixed; From 2dcbd325e891fc9431a7042651b01e6b9a765bfe Mon Sep 17 00:00:00 2001 From: KIMSOOHWAN Date: Mon, 23 Dec 2024 16:15:51 +0900 Subject: [PATCH 07/11] =?UTF-8?q?Fix=20:=203=EC=B0=A8=20QA=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=A4=91,=20=EC=BA=90=EC=8B=B1=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=EB=B6=80=EB=B6=84=20=EB=B0=8F=20=EC=B0=9C=EC=88=AB?= =?UTF-8?q?=EC=9E=90=20=EB=A6=AC=EC=95=A1=ED=8A=B8=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=EC=AA=BD=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=EC=A4=91=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/mypage/page.tsx | 133 +++++++++++++++++++++++++++------------- 1 file changed, 90 insertions(+), 43 deletions(-) diff --git a/src/app/mypage/page.tsx b/src/app/mypage/page.tsx index 61b0251e..265d78f6 100644 --- a/src/app/mypage/page.tsx +++ b/src/app/mypage/page.tsx @@ -1,13 +1,11 @@ 'use client'; - import styles from './mypage.module.scss'; import Info from './_components/Info/Info'; import Link from 'next/link'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { getPersonalInfo } from '@/api/apis/mypageApis'; import InfoEdit from './_components/infoEdit/infoEdit'; import Image from 'next/image'; -import { useQuery } from '@tanstack/react-query'; // UserProfile 인터페이스의 이름을 I로 시작하도록 수정 interface IUserProfile { @@ -19,56 +17,96 @@ interface IUserProfile { } export default function MyPage() { - const [editOpen, setEditOpen] = useState(false); // 정보 수정 모달 상태 - const [prOpen, setPrOpen] = useState(false); // PR 태그 표시 상태 - - // React Query를 사용하여 사용자 정보 가져오기 - const { - data: userInfo, // 사용자 정보 - isLoading, // 로딩 상태 - refetch, // 정보 갱신 함수 - } = useQuery({ - queryKey: ['personalInfo'], // Query Key - queryFn: getPersonalInfo, // 데이터를 가져오는 함수 - staleTime: 5 * 60 * 1000, // 데이터가 5분 동안 신선하게 유지 - cacheTime: 10 * 60 * 1000, // 데이터가 10분 동안 캐시 - refetchOnWindowFocus: false, // 창 전환 시 자동 갱신 비활성화 - }); - + const [info, setInfo] = useState(null); // IUserProfile 타입 사용 + const [editOpen, setEditOpen] = useState(false); + const [prOpen, setPrOpen] = useState(false); + const [animatedRating, setAnimatedRating] = useState(0); // Initial rating state + const [checkedLogin, setCheckedLogin] = useState(null); // state to hold checkedLogin + const [loading, setLoading] = useState(true); // 로딩 상태 추가 + // 클라이언트 사이드에서만 localStorage를 접근 + useEffect(() => { + const token = localStorage.getItem('accessToken'); + setCheckedLogin(token); + // setLoading(false); // 로딩 완료 + }, []); // 정보 수정 모달 열기/닫기 핸들러 const handleEditOpen = () => { setEditOpen(prev => !prev); }; - // 평점 애니메이션 계산 - const animatedRating = userInfo?.data?.averageRating || 0; + // 사용자 정보 가져오기 + const fetchPersonalInfo = async () => { + setLoading(true); + try { + const response = await getPersonalInfo(); + const targetRating = response.data.averageRating; + + // Animate rating + let currentRating = 0; + const increment = 0.1; // Increment step + const animationDuration = 1000; // Duration in milliseconds + const interval = 16; // Interval in milliseconds (roughly 60fps) + + const step = (animationDuration / interval) * increment; // Calculate increment per frame + + const animate = () => { + if (currentRating < targetRating) { + currentRating += step; + if (currentRating > targetRating) currentRating = targetRating; // Cap at targetRating + setAnimatedRating(currentRating); + requestAnimationFrame(animate); + } else { + setAnimatedRating(targetRating); // Ensure final value is set + } + }; + + animate(); // Start animation + + setInfo(response.data); + } catch (err) { + // console.error('Failed to fetch personal info:', err); + } finally { + setLoading(false); + } + }; + + // 컴포넌트가 처음 렌더링될 때 사용자 정보를 가져옵니다. + useEffect(() => { + fetchPersonalInfo(); + }, []); + + // if (loading) { + // return
    Loading...
    ; // 로딩 중 상태를 표시합니다. + // } + const ratingPercentage = (animatedRating / 5) * 100; return (
    - {/* 정보 수정 모달 */} - {userInfo && ( + {/* Conditionally render InfoEdit only if info is not null */} + {info && (
    )}

    마이페이지

    - - {/* 로딩 상태 및 사용자 정보 표시 */} - {isLoading ? ( + {loading === true ? (
    ) : ( - +
    + +
    )}
    @@ -77,18 +115,22 @@ export default function MyPage() {
    + width: `${ratingPercentage <= 20 ? 15 : Math.min(ratingPercentage, 100)}%`, + }} // Animate width based on animatedRating + >

    {animatedRating.toFixed(1)}

    -

    @@ -102,13 +144,13 @@ export default function MyPage() {

      - {userInfo?.data?.prTags?.map((tag, index) => ( -
    • {tag}
    • - ))} + {info?.prTags.map((item, i) =>
    • {item}
    • )}
    -
      + {/*
    • + PR 태그 수정 +
    • */}
    • 내 모임 @@ -157,3 +199,8 @@ export default function MyPage() {
    ); } +{ + /*
  • + 친구 목록 +
  • */ +} From 9275597ba581d1a2802555b2001b0017f2f04dc2 Mon Sep 17 00:00:00 2001 From: KIMSOOHWAN Date: Tue, 24 Dec 2024 16:13:21 +0900 Subject: [PATCH 08/11] =?UTF-8?q?Fix=20:=203=EC=B0=A8=20QA=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=EC=A4=91=20=EC=BF=BC=EB=A6=AC,=EC=BA=90=EC=8B=B1=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EB=B6=80=EB=B6=84=20=EC=A7=84=ED=96=89?= =?UTF-8?q?=EC=A4=91,=20prEdit=ED=8C=8C=EC=9D=BC=20=EB=93=A4=EC=97=AC?= =?UTF-8?q?=EC=93=B0=EA=B8=B0=20=EB=AC=B4=EC=8B=9C=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_components/deleteModal/index.tsx | 14 ++--- .../_components/outModal/index.tsx | 23 +++----- .../mypage/myGatherings/participant/page.tsx | 8 +++ src/app/mypage/prEdit/page.tsx | 59 +++++++++++-------- src/app/mypage/prEdit/prEdit.module.scss | 16 +++++ 5 files changed, 74 insertions(+), 46 deletions(-) diff --git a/src/app/mypage/myGatherings/_components/deleteModal/index.tsx b/src/app/mypage/myGatherings/_components/deleteModal/index.tsx index 69b68759..7a20d57f 100644 --- a/src/app/mypage/myGatherings/_components/deleteModal/index.tsx +++ b/src/app/mypage/myGatherings/_components/deleteModal/index.tsx @@ -16,16 +16,15 @@ export default function DeleteModal({ handleModalClose, meetingId, meetingTitle, -}: IOutModalProps) { + removeMeeting, +}: IOutModalProps & { removeMeeting: (_id: string) => void }) { const { addToast } = useToast(); const HandleDeleteMeeting = async (id: string) => { try { await deleteMeeting(id); // API 호출 addToast('모임을 삭제하였습니다.', 'success'); - setTimeout(() => { - window.location.reload(); - }, 500); + removeMeeting(id); // 부모 상태 업데이트 호출 } catch (error) { if (axios.isAxiosError(error) && error.response) { if (error.response.status === 400 || error.response.status === 404) { @@ -39,6 +38,8 @@ export default function DeleteModal({ alert('오류가 발생했습니다. 다시 시도해주세요.'); } // console.error('모임 삭제 중 오류 발생:', error); + } finally { + handleModalClose(); } }; @@ -51,10 +52,7 @@ export default function DeleteModal({
    -
    diff --git a/src/app/mypage/myGatherings/participant/page.tsx b/src/app/mypage/myGatherings/participant/page.tsx index df66ab9a..c7de761c 100644 --- a/src/app/mypage/myGatherings/participant/page.tsx +++ b/src/app/mypage/myGatherings/participant/page.tsx @@ -53,6 +53,12 @@ export default function Finish() { // 포맷된 문자열 반환 return `${year}년 ${month}월 ${day}일 ${hours}시 ${minutes}분`; }; + // 모임 목록에서 특정 모임 제거 함수 추가 + const removeMeetingFromList = (meetingId: string) => { + setGatherings(prevGatherings => + prevGatherings.filter(gathering => gathering.meetingId !== meetingId) + ); + }; useEffect(() => { const fetchGatherings = async () => { @@ -122,6 +128,7 @@ export default function Finish() { meetingId={selectedMeetingId} meetingTitle={selectedMeetingTitle} handleModalClose={handleModalClose} + removeMeeting={removeMeetingFromList} // 전달 /> ) : null} {deleteModal === true && selectedMeetingId && selectedMeetingTitle ? ( @@ -129,6 +136,7 @@ export default function Finish() { meetingId={selectedMeetingId} meetingTitle={selectedMeetingTitle} handleModalClose={handleDeleteModalClose} + removeMeeting={removeMeetingFromList} // 전달 /> ) : null} diff --git a/src/app/mypage/prEdit/page.tsx b/src/app/mypage/prEdit/page.tsx index 8ac59021..c4770cc1 100644 --- a/src/app/mypage/prEdit/page.tsx +++ b/src/app/mypage/prEdit/page.tsx @@ -1,3 +1,4 @@ +/* eslint-disable */ 'use client'; import React from 'react'; import styles from './prEdit.module.scss'; @@ -10,13 +11,17 @@ export default function PrEdit() { const [newTag, setNewTag] = useState(''); const [error, setError] = useState(null); const [inputLengthError, setInputLengthError] = useState(null); // 태그 길이 에러 상태 + const [loading, setLoading] = useState(true); const fetchPersonalInfo = async () => { try { + setLoading(true); const response = await getPersonalInfo(); setPrTags(response.data.prTags); } catch (err) { // console.error('err:', err); + } finally { + setLoading(false); } }; @@ -49,6 +54,7 @@ export default function PrEdit() { const value = e.target.value; setNewTag(value); validateTagLength(value); // 실시간 길이 검사 + setError(null); // 다른 오류 메시지 초기화 }; // 유효성 검사 및 엔터 키를 눌렀을 때 태그 추가하는 함수 @@ -56,8 +62,12 @@ export default function PrEdit() { if (e.key === 'Enter') { e.preventDefault(); const trimmedTag = newTag.trim(); - const tagPattern = /^[a-zA-Z0-9가-힣]+$/; // 띄어쓰기 없는 한글, 영어, 숫자만 허용 + if (!trimmedTag) { + // 빈 문자열이면 아무 작업도 하지 않음 + return; + } + const tagPattern = /^[a-zA-Z0-9가-힣]+$/; // 띄어쓰기 없는 한글, 영어, 숫자만 허용 let errorMessage = ''; if (prTags.includes(trimmedTag)) { @@ -66,20 +76,17 @@ export default function PrEdit() { errorMessage = '태그는 최대 10개까지만 추가할 수 있습니다.'; } else if (!tagPattern.test(trimmedTag)) { errorMessage = '한글, 영어, 숫자만 허용됩니다.'; - } else if (!trimmedTag) { - errorMessage = '태그를 입력해주세요.'; } if (errorMessage) { setError(errorMessage); - setNewTag(''); return; } - setError(null); + setError(null); // 오류 초기화 const updatedTags = [...prTags, trimmedTag]; setPrTags(updatedTags); // UI 먼저 업데이트 - setNewTag(''); + setNewTag(''); // 입력 필드 초기화 try { await updateTagsOnServer(updatedTags); // 서버 업데이트 @@ -107,24 +114,28 @@ export default function PrEdit() {

    PR태그

      - {prTags.map((tag, index) => ( -
    • - -
    • - ))} + {loading + ? Array.from({ length: 4 }).map((_, index) => ( +
    • + )) + : prTags.map((tag, index) => ( +
    • + +
    • + ))}
    PR태그는 입력해주세요.
    10개 까지 추가 할 수 있습니다! diff --git a/src/app/mypage/prEdit/prEdit.module.scss b/src/app/mypage/prEdit/prEdit.module.scss index d249acbb..c70fb5b9 100644 --- a/src/app/mypage/prEdit/prEdit.module.scss +++ b/src/app/mypage/prEdit/prEdit.module.scss @@ -32,6 +32,22 @@ } } } + .skeletonTag { + display: block; + padding: 15px 30px; + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: skeleton-animation 1.5s infinite linear; + border-radius: 15px; + } + @keyframes skeleton-animation { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } + } } input { width: 100%; From 3d768176806e2808c28b94a766f2cf587e7eed7d Mon Sep 17 00:00:00 2001 From: KIMSOOHWAN Date: Fri, 27 Dec 2024 17:50:59 +0900 Subject: [PATCH 09/11] =?UTF-8?q?Fix=20:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=A6=AC=EC=95=A1=ED=8A=B8=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC=20=EC=BA=90?= =?UTF-8?q?=EC=8B=B1=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 14 +- package.json | 2 +- src/app/mypage/_components/Info/Info.tsx | 33 ++-- .../mypage/_components/infoEdit/infoEdit.tsx | 28 ---- .../_components/deleteModal/index.tsx | 1 + src/app/mypage/myGatherings/create/page.tsx | 3 + src/app/mypage/page.tsx | 150 +++++++++--------- 7 files changed, 107 insertions(+), 124 deletions(-) diff --git a/package-lock.json b/package-lock.json index dae07e32..66f97db6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@sentry/nextjs": "^8.34.0", "@stomp/stompjs": "^7.0.0", - "@tanstack/react-query": "^5.51.11", + "@tanstack/react-query": "^5.62.10", "axios": "^1.7.2", "draft-js": "^0.11.7", "ffmpeg-static": "^5.2.0", @@ -4071,8 +4071,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.59.20", - "license": "MIT", + "version": "5.62.9", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.62.9.tgz", + "integrity": "sha512-lwePd8hNYhyQ4nM/iRQ+Wz2cDtspGeZZHFZmCzHJ7mfKXt+9S301fULiY2IR2byJYY6Z03T427E5PoVfMexHjw==", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -4088,10 +4089,11 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.59.20", - "license": "MIT", + "version": "5.62.10", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.62.10.tgz", + "integrity": "sha512-1e1WpHM5oGf27nWM/NWLY62/X9pbMBWa6ErWYmeuK0OqB9/g9UzA59ogiWbxCmS2wtAFQRhOdHhfSofrkhPl2g==", "dependencies": { - "@tanstack/query-core": "5.59.20" + "@tanstack/query-core": "5.62.9" }, "funding": { "type": "github", diff --git a/package.json b/package.json index 4841ba01..90eb928c 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "dependencies": { "@sentry/nextjs": "^8.34.0", "@stomp/stompjs": "^7.0.0", - "@tanstack/react-query": "^5.51.11", + "@tanstack/react-query": "^5.62.10", "axios": "^1.7.2", "draft-js": "^0.11.7", "ffmpeg-static": "^5.2.0", diff --git a/src/app/mypage/_components/Info/Info.tsx b/src/app/mypage/_components/Info/Info.tsx index 2d600afb..4eee83ce 100644 --- a/src/app/mypage/_components/Info/Info.tsx +++ b/src/app/mypage/_components/Info/Info.tsx @@ -93,14 +93,25 @@ export default function Info({
    - 프로필사진 + {loggedIn ? ( + 나의 프로필사진 + ) : ( + 기본 프로필사진 + )}
    @@ -123,9 +134,9 @@ export default function Info({ {loggedIn ? (
      {/*
    • - company. -

      BoardGo

      -
    • */} + company. +

      BoardGo

      + */}
    • E-mail.

      {mypageInfo?.email}

      diff --git a/src/app/mypage/_components/infoEdit/infoEdit.tsx b/src/app/mypage/_components/infoEdit/infoEdit.tsx index 11be8b80..ba81e9e7 100644 --- a/src/app/mypage/_components/infoEdit/infoEdit.tsx +++ b/src/app/mypage/_components/infoEdit/infoEdit.tsx @@ -65,20 +65,6 @@ export default function InfoEdit({ window.location.reload(); }, 1000); }; - - // 비밀번호와 비밀번호 확인 비교 함수 - // const validatePasswords = () => { - // if (passwordValue !== confirmPasswordValue) { - // setIsPasswordMatched(false); - // setError('confirmPassword', { - // type: 'manual', - // message: '비밀번호가 일치하지 않습니다.', - // }); - // } else { - // setIsPasswordMatched(true); - // clearErrors('confirmPassword'); - // } - // }; const validatePassword = () => { if (!passwordValue) { clearErrors('password'); // 비밀번호가 없으면 에러 제거 @@ -205,11 +191,6 @@ export default function InfoEdit({ updateInfo(); reset(); handleEditOpen(); - - // 성공 후 새로고침 (선택적) - setTimeout(() => { - window.location.reload(); - }, 1000); } catch (error) { console.error('정보 수정 중 오류가 발생했습니다.', error); } finally { @@ -221,15 +202,6 @@ export default function InfoEdit({ (passwordValue && isPasswordMatched && !errors.password) || // 비밀번호 관련 조건 (nameValue.trim() && !isNameDuplicate && isNameChecked); // 닉네임 관련 조건 - // console.log('isFormValid:', isFormValid); - // console.log('passwordValue:', passwordValue); - // console.log('isPasswordMatched:', isPasswordMatched); - // console.log('errors.password:', errors.password); - // console.log('nameValue:', nameValue); - // console.log('isNameDuplicate:', isNameDuplicate); - // console.log('isNameChecked:', isNameChecked); - // console.log(mypageInfo); - return (

    @@ -148,9 +150,6 @@ export default function MyPage() {

      - {/*
    • - PR 태그 수정 -
    • */}
    • 내 모임 @@ -199,8 +198,3 @@ export default function MyPage() {
    ); } -{ - /*
  • - 친구 목록 -
  • */ -} From d698022142954424f1a1da8d653caa0d2f8db04a Mon Sep 17 00:00:00 2001 From: KIMSOOHWAN Date: Sun, 29 Dec 2024 17:35:33 +0900 Subject: [PATCH 10/11] =?UTF-8?q?Fix=20:=20=EB=A9=94=EC=9D=B8=20=EC=83=88?= =?UTF-8?q?=EB=AA=A8=EC=9E=84,=20=EB=A7=88=EA=B0=90=EC=9E=84=EB=B0=95=20?= =?UTF-8?q?=EB=A6=AC=EC=95=A1=ED=8A=B8=EC=BF=BC=EB=A6=AC=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeadLineGather.module.scss | 0 .../index.tsx | 0 src/app/main/_components/newGather/page.tsx | 6 +- src/app/main/page.tsx | 59 +++++++++++++------ 4 files changed, 44 insertions(+), 21 deletions(-) rename src/app/main/_components/{DaedLineGather => DeadLineGather}/DeadLineGather.module.scss (100%) rename src/app/main/_components/{DaedLineGather => DeadLineGather}/index.tsx (100%) diff --git a/src/app/main/_components/DaedLineGather/DeadLineGather.module.scss b/src/app/main/_components/DeadLineGather/DeadLineGather.module.scss similarity index 100% rename from src/app/main/_components/DaedLineGather/DeadLineGather.module.scss rename to src/app/main/_components/DeadLineGather/DeadLineGather.module.scss diff --git a/src/app/main/_components/DaedLineGather/index.tsx b/src/app/main/_components/DeadLineGather/index.tsx similarity index 100% rename from src/app/main/_components/DaedLineGather/index.tsx rename to src/app/main/_components/DeadLineGather/index.tsx diff --git a/src/app/main/_components/newGather/page.tsx b/src/app/main/_components/newGather/page.tsx index 19c86e87..6c7244c9 100644 --- a/src/app/main/_components/newGather/page.tsx +++ b/src/app/main/_components/newGather/page.tsx @@ -10,7 +10,7 @@ import Link from 'next/link'; import Image from 'next/image'; import SaveGatheringButton from '@/components/common/SaveGatheringButton'; -interface IMeetingProps { +interface IMeeting { id: number; title: string; city: string; @@ -27,7 +27,8 @@ interface IMeetingProps { } interface NewGatherProps { - meetingList: IMeetingProps[] | undefined; + meetingList: IMeeting[]; + refetch: () => void; } export default function NewGather({ meetingList }: NewGatherProps) { @@ -73,7 +74,6 @@ export default function NewGather({ meetingList }: NewGatherProps) { ( - undefined - ); const deadlineRef = useRef(null); const popularRef = useRef(null); const token = localStorage.getItem('accessToken'); @@ -81,17 +84,23 @@ export default function Main() { }); } }, []); - - useEffect(() => { - const fetchMeetingList = async () => { + const { + data: meetingListInfo, + isLoading, + refetch, + } = useQuery({ + queryKey: ['meetingList'], + queryFn: async () => { try { - const res = await getMeetingList(); - setMeetingList(res.data.content); - } catch (error) {} - }; - - fetchMeetingList(); - }, []); + const response = await getMeetingList(); + console.log('API 응답:', response); // 응답 로깅 + return response.data; + } catch (error) { + return null; // 에러 시 null 반환 + } + }, + staleTime: 5 * 60 * 1000, // 5분으로 설정 (밀리초 단위) + }); const scrollToSection = ( ref: React.RefObject, @@ -138,10 +147,24 @@ export default function Main() {
    {/* */} - + {isLoading ? ( + <>스켈레톤 + ) : ( + + )}
    - + {isLoading ? ( + <>스켈레톤 + ) : ( + + )}
    From d44d8bca160ac609e1f417badfc4247051414d75 Mon Sep 17 00:00:00 2001 From: KIMSOOHWAN Date: Thu, 2 Jan 2025 16:15:52 +0900 Subject: [PATCH 11/11] =?UTF-8?q?Fix=20:=203=EC=B0=A8=20qa=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=99=84=EB=A3=8C=20&=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EC=9E=91=EC=97=85=ED=95=84=EC=9A=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/_components/DeadLineGather/index.tsx | 6 ++---- .../newGather/{page.tsx => index.tsx} | 8 ++----- src/app/main/page.tsx | 21 +++++++------------ 3 files changed, 12 insertions(+), 23 deletions(-) rename src/app/main/_components/newGather/{page.tsx => index.tsx} (97%) diff --git a/src/app/main/_components/DeadLineGather/index.tsx b/src/app/main/_components/DeadLineGather/index.tsx index a954463c..1e89aae5 100644 --- a/src/app/main/_components/DeadLineGather/index.tsx +++ b/src/app/main/_components/DeadLineGather/index.tsx @@ -1,4 +1,5 @@ /* eslint-disable indent */ +'use client'; import React from 'react'; import { Swiper, SwiperSlide } from 'swiper/react'; import 'swiper/css'; // Swiper 스타일 @@ -27,6 +28,7 @@ interface IMeetingProps { interface DeadLineGatherProps { meetingList: IMeetingProps[] | undefined; + refetch: () => void; } export default function DeadLineGather({ meetingList }: DeadLineGatherProps) { @@ -92,10 +94,6 @@ export default function DeadLineGather({ meetingList }: DeadLineGatherProps) { />
    - {/*
    -

    추리게임

    -
    */} -
    {Array.isArray(filteredMeetingList) && filteredMeetingList.length > 0 ? ( diff --git a/src/app/main/_components/newGather/page.tsx b/src/app/main/_components/newGather/index.tsx similarity index 97% rename from src/app/main/_components/newGather/page.tsx rename to src/app/main/_components/newGather/index.tsx index 6c7244c9..b8571dd8 100644 --- a/src/app/main/_components/newGather/page.tsx +++ b/src/app/main/_components/newGather/index.tsx @@ -1,5 +1,5 @@ 'use client'; - +/* eslint-disable indent */ import React from 'react'; import { Swiper, SwiperSlide } from 'swiper/react'; import 'swiper/css'; // Swiper 스타일 @@ -27,7 +27,7 @@ interface IMeeting { } interface NewGatherProps { - meetingList: IMeeting[]; + meetingList: IMeeting[] | undefined; refetch: () => void; } @@ -66,10 +66,6 @@ export default function NewGather({ meetingList }: NewGatherProps) { />
    - {/*
    -

    모임 목록

    -
    */} -
    ({ + } = useQuery({ queryKey: ['meetingList'], queryFn: async () => { try { const response = await getMeetingList(); - console.log('API 응답:', response); // 응답 로깅 + // console.log('API 응답:', response); // 응답 로깅 return response.data; } catch (error) { return null; // 에러 시 null 반환 @@ -146,22 +146,17 @@ export default function Main() {
    - {/* */} - {isLoading ? ( - <>스켈레톤 - ) : ( + {meetingListInfo?.content && ( )}
    - {isLoading ? ( - <>스켈레톤 - ) : ( + {meetingListInfo?.content && ( )}