From 510ea31ca45130dbc6be567c677d8b58e2ead9bf Mon Sep 17 00:00:00 2001 From: aken-you Date: Fri, 18 Jul 2025 17:15:08 +0900 Subject: [PATCH 01/58] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=A6=84,=20?= =?UTF-8?q?=EC=97=B0=EB=9D=BD=EC=B2=98=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my-page/ui/profile-edit-modal.tsx | 27 +++++++++++++------ src/shared/ui/form/form-field.tsx | 7 ++++- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index 02cdb8a0..40cac8f0 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -29,9 +29,15 @@ export default function ProfileEditModal({ const [name, setName] = useState( memberProfile.memberName ?? '', ); + // 이름 유효성 검사: 2~10자, 한글 또는 영문만 허용 + const isNameValid = /^[가-힣a-zA-Z]{2,10}$/.test(name); + const [tel, setTel] = useState( memberProfile.tel ?? '', ); + // 연락처 유효성 검사: "(2~3자리 지역번호)-(3~4자리 번호)-(4자리 번호)" 형식 + const isTelValid = /^\d{2,3}-\d{3,4}-\d{4}$/.test(tel); + const [githubLink, setGithubLink] = useState< UpdateUserProfileRequest['githubLink'] >(memberProfile.githubLink?.url ?? ''); @@ -70,12 +76,6 @@ export default function ProfileEditModal({ }; const handleSubmit = async () => { - if (!name || !tel) { - alert('모든 필수 정보를 입력해주세요!'); - - return; - } - const file = fileInputRef.current?.files?.[0]; const hasImageFile = !!file; @@ -158,7 +158,12 @@ export default function ProfileEditModal({ 수정 완료 diff --git a/src/shared/ui/form/form-field.tsx b/src/shared/ui/form/form-field.tsx index f617a5d6..737f3edf 100644 --- a/src/shared/ui/form/form-field.tsx +++ b/src/shared/ui/form/form-field.tsx @@ -21,10 +21,12 @@ interface FormFieldProps { value: T; direction?: 'horizontal' | 'vertical'; onChange: (value: T) => void; + error?: boolean; } export function FormField({ label, + error = false, description, type, required = false, @@ -42,10 +44,13 @@ export function FormField({ onChange(e.target.value as T)} /> {description && ( -
+
{description}
)} From ff35c9007ddf9d5c7dc63230590796033f570307 Mon Sep 17 00:00:00 2001 From: aken-you Date: Fri, 18 Jul 2025 17:20:52 +0900 Subject: [PATCH 02/58] =?UTF-8?q?refactor:=20=EC=97=B0=EB=9D=BD=EC=B2=98?= =?UTF-8?q?=20input=EC=97=90=20=EC=88=AB=EC=9E=90=EC=99=80=20-=EB=A7=8C=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=A0=9C?= =?UTF-8?q?=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/my-page/ui/profile-edit-modal.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index 40cac8f0..a8d81b0d 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -178,7 +178,10 @@ export default function ProfileEditModal({ : '연락처는 숫자와 하이픈(-)을 포함한 형식으로 입력해주세요.' } value={tel} - onChange={setTel} + onChange={(value) => { + const onlyNumberAndHyphen = value.replace(/[^\d-]/g, ''); + setTel(onlyNumberAndHyphen); + }} required /> Date: Fri, 18 Jul 2025 17:29:33 +0900 Subject: [PATCH 03/58] =?UTF-8?q?refactor:=20=EB=82=B4=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=88=98=EC=A0=95=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=EC=9D=98=20Footer=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/my-page/ui/profile-edit-modal.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index a8d81b0d..47d537e5 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -223,16 +223,16 @@ export default function ProfileEditModal({
-
+
); From 6724fde5226ebca7e4b7b8cc737af558a0b189c2 Mon Sep 17 00:00:00 2001 From: aken-you Date: Fri, 18 Jul 2025 22:14:39 +0900 Subject: [PATCH 06/58] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=83=81=EC=88=98=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/my-page/consts/my-page-const.ts | 2 ++ src/features/my-page/ui/profile-edit-modal.tsx | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/features/my-page/consts/my-page-const.ts b/src/features/my-page/consts/my-page-const.ts index 15b20a72..5f909266 100644 --- a/src/features/my-page/consts/my-page-const.ts +++ b/src/features/my-page/consts/my-page-const.ts @@ -29,3 +29,5 @@ export const MBTI_OPTIONS = [ { label: 'ENFJ', value: 'ENFJ' }, { label: 'ENTJ', value: 'ENTJ' }, ]; + +export const DEFAULT_PROFILE_IMAGE_URL = '/profile-default.svg'; diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index 5bb94386..48147397 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -10,7 +10,11 @@ import Button from '@/shared/ui/button'; import { Modal } from '@/shared/ui/modal'; import { FormField } from '../../../shared/ui/form/form-field'; import { UpdateUserProfileRequest } from '../api/types'; -import { DEFAULT_OPTIONS, MBTI_OPTIONS } from '../consts/my-page-const'; +import { + DEFAULT_OPTIONS, + DEFAULT_PROFILE_IMAGE_URL, + MBTI_OPTIONS, +} from '../consts/my-page-const'; import { useUpdateUserProfileMutation } from '../model/use-update-user-profile-mutation'; interface Props { @@ -58,7 +62,7 @@ export default function ProfileEditModal({ memberProfile, memberId }: Props) { const [image, setImage] = useState( memberProfile.profileImage?.resizedImages?.[0]?.resizedImageUrl ?? - '/profile-default.svg', + DEFAULT_PROFILE_IMAGE_URL, ); const queryClient = useQueryClient(); @@ -119,6 +123,7 @@ export default function ProfileEditModal({ memberProfile, memberId }: Props) { alert('이미지 업로드에 실패했습니다.'); } } + console.log('memberinfo 업데이트'); await queryClient.invalidateQueries({ queryKey: ['memberInfo'] }); }; From 6ec6122e68a04e35e52f2cb062000ece9b7a669d Mon Sep 17 00:00:00 2001 From: aken-you Date: Fri, 18 Jul 2025 22:44:11 +0900 Subject: [PATCH 07/58] =?UTF-8?q?style:=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/auth/api/auth.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/features/auth/api/auth.ts b/src/features/auth/api/auth.ts index a055640c..26828943 100644 --- a/src/features/auth/api/auth.ts +++ b/src/features/auth/api/auth.ts @@ -19,13 +19,10 @@ export async function uploadProfileImage( filename: string, file: FormData, ) { - console.log('uploadProfileImage 요청직전', file); - // Bug : 여기서 에러발생중 const res = await axiosInstanceForMultipart.put( `/files/members/${memberId}/profile/image/${filename}`, file, ); - console.log('auth.ts - uploadProfileImage 응답', res); return res.data; } From 7e89030d06bf5f97b8cece4d7c9c67914f2f16fd Mon Sep 17 00:00:00 2001 From: aken-you Date: Fri, 18 Jul 2025 22:48:49 +0900 Subject: [PATCH 08/58] =?UTF-8?q?style:=20--color-background-dimmer=20opac?= =?UTF-8?q?ity=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/global.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/global.css b/app/global.css index bcbecc55..74119091 100644 --- a/app/global.css +++ b/app/global.css @@ -185,7 +185,7 @@ https://velog.io/@oneook/tailwindcss-4.0-%EB%AC%B4%EC%97%87%EC%9D%B4-%EB%8B%AC%E --color-background-alternative: var(--color-gray-50); --color-background-default: var(--color-gray-0); - --color-background-dimmer: rgba(24, 29, 39, 80); + --color-background-dimmer: rgba(24, 29, 39, 0.8); --color-background-disabled: var(--color-gray-100); --color-background-brand-subtle: var(--color-rose-300); From 4153fd3d6e10040e3ebc8f04c54c4c5144203992 Mon Sep 17 00:00:00 2001 From: aken-you Date: Fri, 18 Jul 2025 23:23:01 +0900 Subject: [PATCH 09/58] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=ED=8E=B8=EC=A7=91=20=EB=AA=A8=EB=8B=AC=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=ED=8F=BC=EA=B3=BC=20=EA=B4=80=EB=A0=A8=EB=90=9C=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my-page/ui/profile-edit-modal.tsx | 107 +++++++++--------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index 48147397..b9275896 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -25,46 +25,29 @@ interface Props { export type MbtiValue = (typeof MBTI_OPTIONS)[number]['value']; export default function ProfileEditModal({ memberProfile, memberId }: Props) { - const [isOpen, setIsOpen] = useState(false); const fileInputRef = useRef(null); + const [isOpen, setIsOpen] = useState(false); - const [name, setName] = useState( - memberProfile.memberName ?? '', - ); - // 이름 유효성 검사: 2~10자, 한글 또는 영문만 허용 - const isNameValid = /^[가-힣a-zA-Z]{2,10}$/.test(name); - - const [tel, setTel] = useState( - memberProfile.tel ?? '', - ); - // 연락처 유효성 검사: "(2~3자리 지역번호)-(3~4자리 번호)-(4자리 번호)" 형식 - const isTelValid = /^\d{2,3}-\d{3,4}-\d{4}$/.test(tel); - - const [githubLink, setGithubLink] = useState< - UpdateUserProfileRequest['githubLink'] - >(memberProfile.githubLink?.url ?? ''); - - const [blogOrSnsLink, setBlogOrSnsLink] = useState< - UpdateUserProfileRequest['blogOrSnsLink'] - >(memberProfile.blogOrSnsLink?.url ?? ''); - - const [mbti, setMbti] = useState( - memberProfile.mbti ?? '', - ); - - const [simpleIntroduction, setSimpleIntroduction] = useState< - UpdateUserProfileRequest['simpleIntroduction'] - >(memberProfile.simpleIntroduction ?? ''); - - const [interests, setInterests] = useState< - UpdateUserProfileRequest['interests'] - >(memberProfile.interests?.map((item) => item.name) ?? []); + const [profileForm, setProfileForm] = useState({ + name: memberProfile.memberName ?? '', + tel: memberProfile.tel ?? '', + githubLink: memberProfile.githubLink?.url ?? '', + blogOrSnsLink: memberProfile.blogOrSnsLink?.url ?? '', + mbti: memberProfile.mbti ?? '', + simpleIntroduction: memberProfile.simpleIntroduction ?? '', + interests: memberProfile.interests?.map((item) => item.name) ?? [], + }); const [image, setImage] = useState( memberProfile.profileImage?.resizedImages?.[0]?.resizedImageUrl ?? DEFAULT_PROFILE_IMAGE_URL, ); + // 이름 유효성 검사: 2~10자, 한글 또는 영문만 허용 + const isNameValid = /^[가-힣a-zA-Z]{2,10}$/.test(profileForm.name); + // 연락처 유효성 검사: "(2~3자리 지역번호)-(3~4자리 번호)-(4자리 번호)" 형식 + const isTelValid = /^\d{2,3}-\d{3,4}-\d{4}$/.test(profileForm.tel); + const queryClient = useQueryClient(); const { mutateAsync: updateProfile, data: updatedProfile } = useUpdateUserProfileMutation(memberId); @@ -86,13 +69,14 @@ export default function ProfileEditModal({ memberProfile, memberId }: Props) { ext && ['JPG', 'PNG', 'GIF', 'WEBP'].includes(ext) ? ext : undefined; const rawFormData: UpdateUserProfileRequest = { - name, - tel, - githubLink: githubLink.trim() || undefined, - blogOrSnsLink: blogOrSnsLink.trim() || undefined, - simpleIntroduction: simpleIntroduction.trim() || undefined, - mbti: mbti.trim() || undefined, - interests: interests.length > 0 ? interests : undefined, + name: profileForm.name, + tel: profileForm.tel, + githubLink: profileForm.githubLink.trim() || undefined, + blogOrSnsLink: profileForm.blogOrSnsLink.trim() || undefined, + simpleIntroduction: profileForm.simpleIntroduction.trim() || undefined, + mbti: profileForm.mbti.trim() || undefined, + interests: + profileForm.interests.length > 0 ? profileForm.interests : undefined, profileImageExtension: hasImageFile ? profileImageExtension : undefined, }; @@ -168,8 +152,14 @@ export default function ProfileEditModal({ memberProfile, memberId }: Props) { ? '소셜 계정에서 불러온 닉네임 대신 이름을 입력해 주세요.' : '이름은 2~10자의 한글 또는 영문만 허용됩니다.' } - value={name} - onChange={setName} + value={profileForm.name} + onChange={(value) => { + // 공백 입력하지 못하도록 제한 + setProfileForm({ + ...profileForm, + name: value.replace(/\s/g, ''), + }); + }} required /> { + // 숫자와 하이픈(-)만 입력 허용 const onlyNumberAndHyphen = value.replace(/[^\d-]/g, ''); - setTel(onlyNumberAndHyphen); + setProfileForm({ ...profileForm, tel: onlyNumberAndHyphen }); }} required /> @@ -192,37 +183,47 @@ export default function ProfileEditModal({ memberProfile, memberId }: Props) { label="Github" type="text" description="스터디 진행을 위한 연락 가능한 정보를 입력해 주세요." - value={githubLink} - onChange={setGithubLink} + value={profileForm.githubLink} + onChange={(value) => + setProfileForm({ ...profileForm, githubLink: value }) + } /> + setProfileForm({ ...profileForm, mbti: value }) + } options={MBTI_OPTIONS} /> + setProfileForm({ ...profileForm, interests: value }) + } options={DEFAULT_OPTIONS} /> + setProfileForm({ ...profileForm, simpleIntroduction: value }) + } /> + setProfileForm({ ...profileForm, blogOrSnsLink: value }) + } /> From 71e0dcbd6db4d7cfc423ab34452117962a4a9a20 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 00:02:39 +0900 Subject: [PATCH 10/58] =?UTF-8?q?fix:=20=EB=AA=A8=EB=8B=AC=EC=9D=84=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=20=EC=97=B4=EC=96=B4=EB=8F=84=20=EC=9D=B4?= =?UTF-8?q?=EC=A0=84=EC=97=90=20=EC=88=98=EC=A0=95=ED=95=9C=20=ED=8F=BC=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=9C=A0=EC=A7=80=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/my-page/ui/profile-edit-modal.tsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index b9275896..37806d92 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -60,6 +60,22 @@ export default function ProfileEditModal({ memberProfile, memberId }: Props) { } }; + const resetForm = () => { + setProfileForm({ + name: memberProfile.memberName ?? '', + tel: memberProfile.tel ?? '', + githubLink: memberProfile.githubLink?.url ?? '', + blogOrSnsLink: memberProfile.blogOrSnsLink?.url ?? '', + mbti: memberProfile.mbti ?? '', + simpleIntroduction: memberProfile.simpleIntroduction ?? '', + interests: memberProfile.interests?.map((item) => item.name) ?? [], + }); + setImage( + memberProfile.profileImage?.resizedImages?.[0]?.resizedImageUrl ?? + DEFAULT_PROFILE_IMAGE_URL, + ); + }; + const handleSubmit = async () => { const file = fileInputRef.current?.files?.[0]; @@ -121,7 +137,7 @@ export default function ProfileEditModal({ memberProfile, memberId }: Props) { - +
내 프로필 수정 From d2e95181e1cadac13da49f9ed44da8922c4872d7 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 00:27:15 +0900 Subject: [PATCH 11/58] =?UTF-8?q?fix:=20github=20=EB=A7=81=ED=81=AC?= =?UTF-8?q?=EC=99=80=20blog/sns=20=EB=A7=81=ED=81=AC=EA=B0=80=20=EC=97=86?= =?UTF-8?q?=EC=9D=84=20=EA=B2=BD=EC=9A=B0,=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EB=B6=80=ED=83=81=20=EB=A9=94=EC=84=B8=EC=A7=80=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/my-page/ui/profile.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/my-page/ui/profile.tsx b/src/features/my-page/ui/profile.tsx index 2b277bde..811cbc3f 100644 --- a/src/features/my-page/ui/profile.tsx +++ b/src/features/my-page/ui/profile.tsx @@ -65,7 +65,7 @@ export default function Profile({ memberId, memberProfile }: ProfileProps) { width={16} height={16} /> - {memberProfile.githubLink?.url ?? '깃허브 링크를 입력해주세요!'} + {memberProfile.githubLink?.url || '깃허브 링크를 입력해주세요!'}
- {memberProfile.blogOrSnsLink?.url ?? + {memberProfile.blogOrSnsLink?.url || '블로그 링크를 입력해주세요!'}
From af97facddf5186917adff442511693e9c61ec63e Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 00:28:29 +0900 Subject: [PATCH 12/58] =?UTF-8?q?refactor:=20=EA=B3=B5=EB=B0=B1=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=ED=9B=84=20github,=20=EB=B8=94=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8/SNS=20=EB=A7=81=ED=81=AC=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/my-page/ui/profile-edit-modal.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index 37806d92..ee15d672 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -87,10 +87,10 @@ export default function ProfileEditModal({ memberProfile, memberId }: Props) { const rawFormData: UpdateUserProfileRequest = { name: profileForm.name, tel: profileForm.tel, - githubLink: profileForm.githubLink.trim() || undefined, - blogOrSnsLink: profileForm.blogOrSnsLink.trim() || undefined, + githubLink: profileForm.githubLink, + blogOrSnsLink: profileForm.blogOrSnsLink, simpleIntroduction: profileForm.simpleIntroduction.trim() || undefined, - mbti: profileForm.mbti.trim() || undefined, + mbti: profileForm.mbti || undefined, interests: profileForm.interests.length > 0 ? profileForm.interests : undefined, profileImageExtension: hasImageFile ? profileImageExtension : undefined, @@ -201,7 +201,10 @@ export default function ProfileEditModal({ memberProfile, memberId }: Props) { description="스터디 진행을 위한 연락 가능한 정보를 입력해 주세요." value={profileForm.githubLink} onChange={(value) => - setProfileForm({ ...profileForm, githubLink: value }) + setProfileForm({ + ...profileForm, + githubLink: value.replace(/\s/g, ''), + }) } /> - setProfileForm({ ...profileForm, blogOrSnsLink: value }) + setProfileForm({ + ...profileForm, + blogOrSnsLink: value.replace(/\s/g, ''), + }) } /> From 8909fedf0a6a772a483d6b2cad200c882c63acec Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 09:05:28 +0900 Subject: [PATCH 13/58] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=ED=8F=BC=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProfileEditModal에서 폼상태를 관리하면 memberProfile이 변경돼도, 폼 상태가 변경되지 않음 --- .../my-page/ui/profile-edit-modal.tsx | 318 +++++++++--------- 1 file changed, 156 insertions(+), 162 deletions(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index ee15d672..a9325855 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -25,9 +25,44 @@ interface Props { export type MbtiValue = (typeof MBTI_OPTIONS)[number]['value']; export default function ProfileEditModal({ memberProfile, memberId }: Props) { - const fileInputRef = useRef(null); const [isOpen, setIsOpen] = useState(false); + return ( + + setIsOpen(true)} + className="rounded-100 bg-fill-brand-default-default font-designer-16b text-text-inverse w-full px-150 py-100" + > + 내 프로필 수정 + + + + + +
+ 내 프로필 수정 + + + +
+
+ setIsOpen(false)} + /> +
+
+
+ ); +} + +function ProfileEditForm({ + memberProfile, + memberId, + onClose, +}: Props & { onClose: () => void }) { + const fileInputRef = useRef(null); const [profileForm, setProfileForm] = useState({ name: memberProfile.memberName ?? '', tel: memberProfile.tel ?? '', @@ -60,22 +95,6 @@ export default function ProfileEditModal({ memberProfile, memberId }: Props) { } }; - const resetForm = () => { - setProfileForm({ - name: memberProfile.memberName ?? '', - tel: memberProfile.tel ?? '', - githubLink: memberProfile.githubLink?.url ?? '', - blogOrSnsLink: memberProfile.blogOrSnsLink?.url ?? '', - mbti: memberProfile.mbti ?? '', - simpleIntroduction: memberProfile.simpleIntroduction ?? '', - interests: memberProfile.interests?.map((item) => item.name) ?? [], - }); - setImage( - memberProfile.profileImage?.resizedImages?.[0]?.resizedImageUrl ?? - DEFAULT_PROFILE_IMAGE_URL, - ); - }; - const handleSubmit = async () => { const file = fileInputRef.current?.files?.[0]; @@ -128,150 +147,125 @@ export default function ProfileEditModal({ memberProfile, memberId }: Props) { }; return ( - - setIsOpen(true)} - className="rounded-100 bg-fill-brand-default-default font-designer-16b text-text-inverse w-full px-150 py-100" - > - 내 프로필 수정 - - - - - -
- 내 프로필 수정 - - - -
-
- -
-
-
- 이미지 설정 -
- -
- { - // 공백 입력하지 못하도록 제한 - setProfileForm({ - ...profileForm, - name: value.replace(/\s/g, ''), - }); - }} - required - /> - { - // 숫자와 하이픈(-)만 입력 허용 - const onlyNumberAndHyphen = value.replace(/[^\d-]/g, ''); - setProfileForm({ ...profileForm, tel: onlyNumberAndHyphen }); - }} - required - /> - - setProfileForm({ - ...profileForm, - githubLink: value.replace(/\s/g, ''), - }) - } - /> - - setProfileForm({ ...profileForm, mbti: value }) - } - options={MBTI_OPTIONS} - /> - - setProfileForm({ ...profileForm, interests: value }) - } - options={DEFAULT_OPTIONS} - /> - - setProfileForm({ ...profileForm, simpleIntroduction: value }) - } - /> - - setProfileForm({ - ...profileForm, - blogOrSnsLink: value.replace(/\s/g, ''), - }) - } - /> -
-
- -
- - -
-
-
-
-
+ <> + +
+
+
이미지 설정
+ +
+ { + // 공백 입력하지 못하도록 제한 + setProfileForm({ + ...profileForm, + name: value.replace(/\s/g, ''), + }); + }} + required + /> + { + // 숫자와 하이픈(-)만 입력 허용 + const onlyNumberAndHyphen = value.replace(/[^\d-]/g, ''); + setProfileForm({ ...profileForm, tel: onlyNumberAndHyphen }); + }} + required + /> + + setProfileForm({ + ...profileForm, + githubLink: value.replace(/\s/g, ''), + }) + } + /> + + setProfileForm({ ...profileForm, mbti: value }) + } + options={MBTI_OPTIONS} + /> + + setProfileForm({ ...profileForm, interests: value }) + } + options={DEFAULT_OPTIONS} + /> + + setProfileForm({ ...profileForm, simpleIntroduction: value }) + } + /> + + setProfileForm({ + ...profileForm, + blogOrSnsLink: value.replace(/\s/g, ''), + }) + } + /> +
+
+ +
+ + +
+
+ ); } From 81d58b024745d4e2c8e6e1b7925d43f67186b988 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 11:21:22 +0900 Subject: [PATCH 14/58] =?UTF-8?q?style:=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/my-page/ui/profile-edit-modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index a9325855..c4fea137 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -142,7 +142,7 @@ function ProfileEditForm({ alert('이미지 업로드에 실패했습니다.'); } } - console.log('memberinfo 업데이트'); + await queryClient.invalidateQueries({ queryKey: ['memberInfo'] }); }; From b910c3811ac778c98998f187404dffe9c476497e Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 11:24:01 +0900 Subject: [PATCH 15/58] =?UTF-8?q?refactor:=201024px=20=EA=B8=B0=EC=A4=80?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20layout=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EB=8B=A4=EB=A5=B4=EA=B2=8C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(my)/layout.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/(my)/layout.tsx b/app/(my)/layout.tsx index 76a6fae5..c4c054fd 100644 --- a/app/(my)/layout.tsx +++ b/app/(my)/layout.tsx @@ -14,7 +14,9 @@ export default function MyLayout({ return (
-
{children}
+
+ {children} +
); } From a738ab45b99be58361fe8683e2b835fbc14f59f8 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 12:01:38 +0900 Subject: [PATCH 16/58] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20input=EC=97=90=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=ED=99=95=EC=9E=A5=EC=9E=90=EC=99=80=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EA=B0=9C=EC=88=98=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/auth/ui/sign-up-image-selector.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/features/auth/ui/sign-up-image-selector.tsx b/src/features/auth/ui/sign-up-image-selector.tsx index 4e7fe757..4b0ffb78 100644 --- a/src/features/auth/ui/sign-up-image-selector.tsx +++ b/src/features/auth/ui/sign-up-image-selector.tsx @@ -20,7 +20,7 @@ export default function SignupImageSelector({
프로필 - next최적화안쓴프로필 + next최적화안쓴프로필
@@ -53,8 +53,9 @@ export default function SignupImageSelector({
From dd6dd4996d63e9c7e3e5e88a251599b310818524 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 12:05:45 +0900 Subject: [PATCH 17/58] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my-page/ui/profile-edit-modal.tsx | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index c4fea137..acac51f4 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -84,8 +84,7 @@ function ProfileEditForm({ const isTelValid = /^\d{2,3}-\d{3,4}-\d{4}$/.test(profileForm.tel); const queryClient = useQueryClient(); - const { mutateAsync: updateProfile, data: updatedProfile } = - useUpdateUserProfileMutation(memberId); + const { mutateAsync: updateProfile } = useUpdateUserProfileMutation(memberId); const { mutateAsync: uploadProfileImage } = useUploadProfileImageMutation(); const handleImageChange = (e: React.ChangeEvent) => { @@ -97,11 +96,7 @@ function ProfileEditForm({ const handleSubmit = async () => { const file = fileInputRef.current?.files?.[0]; - - const hasImageFile = !!file; - const ext = file?.name.split('.').pop()?.toUpperCase(); - const profileImageExtension = - ext && ['JPG', 'PNG', 'GIF', 'WEBP'].includes(ext) ? ext : undefined; + const profileImageExtension = file?.name.split('.').pop(); const rawFormData: UpdateUserProfileRequest = { name: profileForm.name, @@ -112,21 +107,18 @@ function ProfileEditForm({ mbti: profileForm.mbti || undefined, interests: profileForm.interests.length > 0 ? profileForm.interests : undefined, - profileImageExtension: hasImageFile ? profileImageExtension : undefined, + profileImageExtension: profileImageExtension, }; const formData = Object.fromEntries( Object.entries(rawFormData).filter(([_, v]) => v !== undefined), ) as UpdateUserProfileRequest; - await updateProfile(formData); + const updatedProfile = await updateProfile(formData); - if ( - fileInputRef.current?.files?.[0] && - updatedProfile.profileImageUploadUrl - ) { + if (file && updatedProfile.profileImageUploadUrl) { const imageFormData = new FormData(); - imageFormData.append('file', fileInputRef.current.files[0]); + imageFormData.append('file', file); const filename = updatedProfile.profileImageUploadUrl.split('/').pop(); if (!filename) return; @@ -143,7 +135,9 @@ function ProfileEditForm({ } } - await queryClient.invalidateQueries({ queryKey: ['memberInfo'] }); + await queryClient.invalidateQueries({ + queryKey: ['userProfile', memberId], + }); }; return ( From d48ef193780adb49cc47ffbf038e0fa66ffa5dd0 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 12:15:31 +0900 Subject: [PATCH 18/58] =?UTF-8?q?style:=20=ED=8C=8C=EC=9D=BC=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=EB=A5=BC=20=EC=97=AC=EB=8A=94=20=ED=95=B8=EB=93=A4?= =?UTF-8?q?=EB=9F=AC=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/auth/ui/sign-up-image-selector.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/auth/ui/sign-up-image-selector.tsx b/src/features/auth/ui/sign-up-image-selector.tsx index 4b0ffb78..8a8f9ce0 100644 --- a/src/features/auth/ui/sign-up-image-selector.tsx +++ b/src/features/auth/ui/sign-up-image-selector.tsx @@ -14,7 +14,7 @@ export default function SignupImageSelector({ handleImageChange: (event: React.ChangeEvent) => void; }) { const setDefaultImage = () => setImage('/profile-default.svg'); - const openFileDialog = () => fileInputRef.current?.click(); + const openFileFolder = () => fileInputRef.current?.click(); return (
@@ -44,7 +44,7 @@ export default function SignupImageSelector({ 앨범에서 선택 From d773b55eca1e62d92729d1db420151463eda695a Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 12:38:03 +0900 Subject: [PATCH 19/58] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=EB=A5=BC=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=EB=A1=9C=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=ED=95=98=EB=A9=B4,=20=EC=84=9C=EB=B2=84=EC=97=90=EA=B2=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/profile-default.jpg | Bin 0 -> 7003 bytes .../my-page/ui/profile-edit-modal.tsx | 27 ++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 public/profile-default.jpg diff --git a/public/profile-default.jpg b/public/profile-default.jpg new file mode 100644 index 0000000000000000000000000000000000000000..46ce5400c3215ec3c00d42dc7cb915d22c50a664 GIT binary patch literal 7003 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 XljZOBrNj+CJz9aIyo|Gei^=~0tTkT@ literal 0 HcmV?d00001 diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index acac51f4..9c26e4a8 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -116,6 +116,33 @@ function ProfileEditForm({ const updatedProfile = await updateProfile(formData); + // todo: 서버 측에서 api 수정되면, 리팩토링 + // 기본 프로필 이미지를 선택할 경우, 기본 프로필 이미지 전송 -> 서버 측에서 나중에 리팩토링 예정 + // public 폴더의 profile-default.jpg 파일을 fetch해서 FormData에 추가 + // profile-default.jpg를 추가한 이유는 서버에서 프로필 이미지 확장자에 svg 파일을 고려하지 못함 -> 서버 측에서 리팩토링 예정 + if (image === DEFAULT_PROFILE_IMAGE_URL) { + try { + const defaultProfileImage = 'profile-default.jpg'; + const response = await fetch(defaultProfileImage); + const blob = await response.blob(); + const defaultFile = new File([blob], defaultProfileImage, { + type: 'image/jpeg', + }); + const imageFormData = new FormData(); + imageFormData.append('file', defaultFile); + + await uploadProfileImage({ + memberId, + filename: defaultProfileImage, + file: imageFormData, + }); + } catch (error) { + console.error('기본 프로필 이미지 업로드 실패:', error); + alert('기본 프로필 이미지 업로드에 실패했습니다.'); + } + } + + // 폴더에서 이미지를 선택한 경우 if (file && updatedProfile.profileImageUploadUrl) { const imageFormData = new FormData(); imageFormData.append('file', file); From f690b4d91d6726ed05252243bad6ecd1b726afb0 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 13:23:32 +0900 Subject: [PATCH 20/58] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=ED=8F=BC=20=EC=95=88=EB=82=B4=20?= =?UTF-8?q?=EB=AC=B8=EA=B5=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/my-page/ui/profile-edit-modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index 9c26e4a8..9c9028f6 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -219,7 +219,7 @@ function ProfileEditForm({ setProfileForm({ @@ -250,7 +250,7 @@ function ProfileEditForm({ setProfileForm({ ...profileForm, simpleIntroduction: value }) From 2e4b595919cd86e847b176688f7eb93cd0b80dd7 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 17:49:08 +0900 Subject: [PATCH 21/58] =?UTF-8?q?refactor:=20=EB=A9=B4=EC=A0=91=20?= =?UTF-8?q?=EC=A4=80=EB=B9=84/=EC=99=84=EB=A3=8C=20=EB=AA=A8=EB=8B=AC=20Fo?= =?UTF-8?q?oter=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/ui/today-study-modal.tsx | 136 +++++++++++--------- 1 file changed, 78 insertions(+), 58 deletions(-) diff --git a/src/features/study/ui/today-study-modal.tsx b/src/features/study/ui/today-study-modal.tsx index f21c3127..372f3a17 100644 --- a/src/features/study/ui/today-study-modal.tsx +++ b/src/features/study/ui/today-study-modal.tsx @@ -62,13 +62,11 @@ export default function TodayStudyModal({ - - {isInterviewee ? ( - - ) : ( - - )} - + {isInterviewee ? ( + + ) : ( + + )} @@ -113,34 +111,44 @@ function ReadyForm({ return ( <> -
- - - 이번 스터디에서 다룰 면접 주제를 입력하세요 - - setInterviewTopic(e.target.value)} - /> -
+ +
+
+ + + 이번 스터디에서 다룰 면접 주제를 입력하세요 + +
-
- - - 참고할 링크나 자료가 있다면 입력해 주세요 - - setReferenceLink(e.target.value)} - /> -
+ setInterviewTopic(e.target.value)} + /> +
+ +
+
+ + + 참고할 링크나 자료가 있다면 입력해 주세요 + +
+ + setReferenceLink(e.target.value)} + /> +
+
@@ -202,37 +210,49 @@ function DoneForm({ return ( <> -
- - - 면접 완료 후 해당 지원자의 상태를 업데이트해 주세요. - - setProgressStatus(e as StudyProgressStatus)} - /> -
+ +
+
+ + + 면접 완료 후 해당 지원자의 상태를 업데이트해 주세요. + +
+ + setProgressStatus(e as StudyProgressStatus)} + /> +
+ +
+
+ + + 면접 결과에 대한 간단한 피드백을 입력해 주세요. + +
+
-
- - - 면접 결과에 대한 간단한 피드백을 입력해 주세요. - setFeedback(e)} /> -
+
From b17c97df7ddbd2651b7745a3dce63e4a32496a4f Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 18:05:25 +0900 Subject: [PATCH 22/58] =?UTF-8?q?refactor:=20=EB=A9=B4=EC=A0=91=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20trigger=20text=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/ui/today-study-modal.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/features/study/ui/today-study-modal.tsx b/src/features/study/ui/today-study-modal.tsx index 372f3a17..e6eae9de 100644 --- a/src/features/study/ui/today-study-modal.tsx +++ b/src/features/study/ui/today-study-modal.tsx @@ -44,11 +44,8 @@ export default function TodayStudyModal({ return ( - -
- - 작성하기 -
+ + From 2fdee7542ca4a55c7c199801839f4ce137c793a8 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 18:06:18 +0900 Subject: [PATCH 23/58] =?UTF-8?q?refactor:=20DoneForm=EC=9D=98=20TextAreaI?= =?UTF-8?q?nput=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/ui/today-study-modal.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/features/study/ui/today-study-modal.tsx b/src/features/study/ui/today-study-modal.tsx index e6eae9de..93fd5d80 100644 --- a/src/features/study/ui/today-study-modal.tsx +++ b/src/features/study/ui/today-study-modal.tsx @@ -241,14 +241,14 @@ function DoneForm({ 면접 결과에 대한 간단한 피드백을 입력해 주세요.
- - setFeedback(e)} - /> + setFeedback(e)} + /> + From 64c828fe44aefd485b225c53f3779732c9b4ab86 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 20:04:35 +0900 Subject: [PATCH 24/58] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=EC=9D=84=20=EA=B8=B0=EB=B3=B8=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95=ED=96=88=EC=9D=84=20=EB=95=8C?= =?UTF-8?q?=EC=9D=98=20=EC=9A=94=EC=B2=AD=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my-page/ui/profile-edit-modal.tsx | 39 +++++++------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index 9c9028f6..08cedf1a 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -96,7 +96,8 @@ function ProfileEditForm({ const handleSubmit = async () => { const file = fileInputRef.current?.files?.[0]; - const profileImageExtension = file?.name.split('.').pop(); + const profileImageExtension = + image === DEFAULT_PROFILE_IMAGE_URL ? 'jpg' : file?.name.split('.').pop(); const rawFormData: UpdateUserProfileRequest = { name: profileForm.name, @@ -116,36 +117,22 @@ function ProfileEditForm({ const updatedProfile = await updateProfile(formData); - // todo: 서버 측에서 api 수정되면, 리팩토링 - // 기본 프로필 이미지를 선택할 경우, 기본 프로필 이미지 전송 -> 서버 측에서 나중에 리팩토링 예정 - // public 폴더의 profile-default.jpg 파일을 fetch해서 FormData에 추가 - // profile-default.jpg를 추가한 이유는 서버에서 프로필 이미지 확장자에 svg 파일을 고려하지 못함 -> 서버 측에서 리팩토링 예정 - if (image === DEFAULT_PROFILE_IMAGE_URL) { - try { + if (updatedProfile.profileImageUploadUrl) { + const imageFormData = new FormData(); + + if (file) imageFormData.append('file', file); + + // 기본 프로필 이미지를 선택할 경우, public 폴더의 profile-default.jpg로 서버 api에게 기본 프로필 이미지 전송 -> 서버 측에서 나중에 리팩토링 예정 + // profile-default.jpg를 추가한 이유는 서버에서 프로필 이미지 확장자에 svg 파일을 고려하지 못함 -> 서버 측에서 리팩토링 예정 + if (!file && image === DEFAULT_PROFILE_IMAGE_URL) { const defaultProfileImage = 'profile-default.jpg'; const response = await fetch(defaultProfileImage); const blob = await response.blob(); - const defaultFile = new File([blob], defaultProfileImage, { - type: 'image/jpeg', + const defaultProfileFile = new File([blob], defaultProfileImage, { + type: 'image/jpg', }); - const imageFormData = new FormData(); - imageFormData.append('file', defaultFile); - - await uploadProfileImage({ - memberId, - filename: defaultProfileImage, - file: imageFormData, - }); - } catch (error) { - console.error('기본 프로필 이미지 업로드 실패:', error); - alert('기본 프로필 이미지 업로드에 실패했습니다.'); + imageFormData.append('file', defaultProfileFile); } - } - - // 폴더에서 이미지를 선택한 경우 - if (file && updatedProfile.profileImageUploadUrl) { - const imageFormData = new FormData(); - imageFormData.append('file', file); const filename = updatedProfile.profileImageUploadUrl.split('/').pop(); if (!filename) return; From 1ebd8fd158515536b1a994a30c8dd492508d43dd Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 19 Jul 2025 21:59:28 +0900 Subject: [PATCH 25/58] =?UTF-8?q?fix:=201=EC=9D=BC=20=EC=A0=84=EC=9D=98=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=EB=A1=9C=20=ED=8A=B9=EC=A0=95=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=EC=9D=98=20=EB=82=B4=20=EC=8A=A4=ED=84=B0=EB=94=94=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/ui/study-card.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/features/study/ui/study-card.tsx b/src/features/study/ui/study-card.tsx index 4ce50232..f56729bd 100644 --- a/src/features/study/ui/study-card.tsx +++ b/src/features/study/ui/study-card.tsx @@ -62,7 +62,10 @@ export default function StudyCard() { day: selectedDate.getDate(), }; - const studyParams = { studyDate: selectedDate.toISOString().split('T')[0] }; + const offset = selectedDate.getTimezoneOffset() * 60000; // ms단위라 60000곱해줌 + const dateOffset = new Date(selectedDate.getTime() - offset); + + const studyParams = { studyDate: dateOffset.toISOString().split('T')[0] }; const { data: participationData } = useWeeklyParticipation(params); const isParticipate = participationData?.isParticipate ?? false; From d626510e5e1d2e75f884e99cb3b42cefb9cfb8da Mon Sep 17 00:00:00 2001 From: aken-you Date: Sun, 20 Jul 2025 23:31:19 +0900 Subject: [PATCH 26/58] =?UTF-8?q?refactor:=20ProfileInfoEditModal=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=8F=BC=20UI=EB=A5=BC=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my-page/ui/profile-info-edit-modal.tsx | 310 ++++++++++-------- 1 file changed, 178 insertions(+), 132 deletions(-) diff --git a/src/features/my-page/ui/profile-info-edit-modal.tsx b/src/features/my-page/ui/profile-info-edit-modal.tsx index 29b6247a..a50dd1f6 100644 --- a/src/features/my-page/ui/profile-info-edit-modal.tsx +++ b/src/features/my-page/ui/profile-info-edit-modal.tsx @@ -22,42 +22,11 @@ interface Props { } export default function ProfileInfoEditModal({ memberInfo, onSubmit }: Props) { - const [selfIntroduction, setSelfIntroduction] = useState< - UpdateUserProfileInfoRequest['selfIntroduction'] - >(memberInfo.selfIntroduction ?? ''); - const [studyPlan, setStudyPlan] = useState< - UpdateUserProfileInfoRequest['studyPlan'] - >(memberInfo.studyPlan ?? ''); - const [preferredStudySubjectId, setPreferredStudySubjectId] = useState< - UpdateUserProfileInfoRequest['preferredStudySubjectId'] - >(memberInfo.preferredStudySubject?.studySubjectId ?? undefined); - const [availableTimeSlots, setAvailableTimeSlots] = useState< - UpdateUserProfileInfoRequest['availableStudyTimeIds'] - >((memberInfo.availableStudyTimes ?? []).map((time) => time?.id ?? 0)); - const [selectedSkills, setSelectedSkills] = useState< - UpdateUserProfileInfoRequest['techStackIds'] - >((memberInfo.techStacks ?? []).map((tech) => tech?.techStackId ?? 0)); - - const handleSubmit = () => { - const formData: UpdateUserProfileInfoRequest = { - selfIntroduction, - studyPlan, - preferredStudySubjectId: preferredStudySubjectId, - availableStudyTimeIds: availableTimeSlots - .filter((id) => id) - .map((id) => Number(id)), - techStackIds: selectedSkills.filter((id) => id).map((id) => Number(id)), - }; - onSubmit(formData); - }; - - const { data: availableStudyTimes } = useAvailableStudyTimesQuery(); - const { data: studySubjects } = useStudySubjectsQuery(); - const { data: techStacks } = useTechStacksQuery(); + const [isOpen, setIsOpen] = useState(false); return ( - - + + setIsOpen(true)}>
편집
@@ -73,106 +42,183 @@ export default function ProfileInfoEditModal({ memberInfo, onSubmit }: Props) {
- -
- - - ({ - value: studySubjectId, - label: name, - })) ?? [] - } - /> - ({ - value: availableTimeId.toString(), - label: display, - })) ?? [] - } - onChange={(availableStudyTimeIds) => - setAvailableTimeSlots(availableStudyTimeIds.map(Number)) - } - required - /> - ({ - value: techStackId, - label: techStackName, - }), - )} - /> -
-
- - -
- - -
-
-
+ setIsOpen(false)} + />
); } + +function ProfileInfoEditForm({ + memberInfo, + onSubmit, + onClose, +}: { + memberInfo: MemberInfo; + onSubmit: (formData: UpdateUserProfileInfoRequest) => void; + onClose: () => void; +}) { + const [infoForm, setInfoForm] = useState({ + selfIntroduction: memberInfo.selfIntroduction ?? '', + studyPlan: memberInfo.studyPlan ?? '', + preferredStudySubjectId: memberInfo.preferredStudySubject?.studySubjectId, + availableStudyTimeIds: (memberInfo.availableStudyTimes ?? []).map( + (time) => time?.id ?? 0, + ), + techStackIds: (memberInfo.techStacks ?? []).map( + (tech) => tech?.techStackId ?? 0, + ), + }); + + const handleSubmit = () => { + const formData: UpdateUserProfileInfoRequest = { + selfIntroduction: infoForm.selfIntroduction, + studyPlan: infoForm.studyPlan, + preferredStudySubjectId: infoForm.preferredStudySubjectId, + availableStudyTimeIds: infoForm.availableStudyTimeIds + .filter((id) => id) + .map((id) => Number(id)), + techStackIds: infoForm.techStackIds + .filter((id) => id) + .map((id) => Number(id)), + }; + + onSubmit(formData); + }; + + const { data: availableStudyTimes } = useAvailableStudyTimesQuery(); + const { data: studySubjects } = useStudySubjectsQuery(); + const { data: techStacks } = useTechStacksQuery(); + + return ( + <> + +
+ + setInfoForm((prev) => ({ + ...prev, + selfIntroduction: value, + })) + } + direction="vertical" + maxLength={500} + /> + + setInfoForm((prev) => ({ + ...prev, + studyPlan: value, + })) + } + direction="vertical" + maxLength={500} + required + /> + + setInfoForm((prev) => ({ + ...prev, + preferredStudySubjectId: value, + })) + } + direction="vertical" + required + options={ + studySubjects?.map(({ studySubjectId, name }) => ({ + value: studySubjectId, + label: name, + })) ?? [] + } + /> + ({ + value: availableTimeId.toString(), + label: display, + })) ?? [] + } + onChange={(availableStudyTimeIds) => + setInfoForm((prev) => ({ + ...prev, + availableStudyTimeIds: availableStudyTimeIds.map(Number), + })) + } + required + /> + + setInfoForm((prev) => ({ + ...prev, + techStackIds: value, + })) + } + direction="vertical" + required + options={(techStacks ?? []).map( + ({ techStackId, techStackName }) => ({ + value: techStackId, + label: techStackName, + }), + )} + /> +
+
+ +
+ + + +
+
+ + ); +} From 24dc7af222c903c558bb975faf031312b27d2ced Mon Sep 17 00:00:00 2001 From: aken-you Date: Sun, 20 Jul 2025 23:37:59 +0900 Subject: [PATCH 27/58] =?UTF-8?q?refactor:=20=EB=82=B4=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=88=98=EC=A0=95=20mutation=20=ED=9B=85=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my-page/ui/profile-info-edit-modal.tsx | 22 ++++++++++--------- src/features/my-page/ui/profile-info.tsx | 13 +---------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/features/my-page/ui/profile-info-edit-modal.tsx b/src/features/my-page/ui/profile-info-edit-modal.tsx index a50dd1f6..2ae158dc 100644 --- a/src/features/my-page/ui/profile-info-edit-modal.tsx +++ b/src/features/my-page/ui/profile-info-edit-modal.tsx @@ -5,7 +5,6 @@ import { XIcon } from 'lucide-react'; import { useState } from 'react'; import { MemberInfo } from '@/entities/user/api/types'; import { hashValue } from '@/shared/lib/hash'; -import { getCookie } from '@/shared/tanstack-query/cookie'; import Button from '@/shared/ui/button'; import { FormField } from '@/shared/ui/form/form-field'; import { Modal } from '@/shared/ui/modal'; @@ -14,14 +13,15 @@ import { useAvailableStudyTimesQuery, useStudySubjectsQuery, useTechStacksQuery, + useUpdateUserProfileInfoMutation, } from '../model/use-update-user-profile-mutation'; interface Props { + memberId: number; memberInfo: MemberInfo; - onSubmit: (formData: UpdateUserProfileInfoRequest) => void; } -export default function ProfileInfoEditModal({ memberInfo, onSubmit }: Props) { +export default function ProfileInfoEditModal({ memberId, memberInfo }: Props) { const [isOpen, setIsOpen] = useState(false); return ( @@ -43,8 +43,8 @@ export default function ProfileInfoEditModal({ memberInfo, onSubmit }: Props) { setIsOpen(false)} /> @@ -54,12 +54,12 @@ export default function ProfileInfoEditModal({ memberInfo, onSubmit }: Props) { } function ProfileInfoEditForm({ + memberId, memberInfo, - onSubmit, onClose, }: { + memberId: number; memberInfo: MemberInfo; - onSubmit: (formData: UpdateUserProfileInfoRequest) => void; onClose: () => void; }) { const [infoForm, setInfoForm] = useState({ @@ -74,6 +74,9 @@ function ProfileInfoEditForm({ ), }); + const { mutate: updateProfileInfo } = + useUpdateUserProfileInfoMutation(memberId); + const handleSubmit = () => { const formData: UpdateUserProfileInfoRequest = { selfIntroduction: infoForm.selfIntroduction, @@ -87,7 +90,7 @@ function ProfileInfoEditForm({ .map((id) => Number(id)), }; - onSubmit(formData); + updateProfileInfo(formData); }; const { data: availableStudyTimes } = useAvailableStudyTimesQuery(); @@ -189,7 +192,7 @@ function ProfileInfoEditForm({ -
+
@@ -200,7 +203,6 @@ function ProfileInfoEditForm({ onClick={() => { handleSubmit(); - const memberId = getCookie('memberId'); const selectedSkillNames = techStacks.filter((techStack) => infoForm.techStackIds.includes(techStack.techStackId), ); @@ -208,7 +210,7 @@ function ProfileInfoEditForm({ sendGTMEvent({ event: 'custom_member_card', dl_timestamp: new Date().toISOString(), - dl_member_id: hashValue(memberId), + dl_member_id: hashValue(String(memberId)), dl_tags: selectedSkillNames, }); diff --git a/src/features/my-page/ui/profile-info.tsx b/src/features/my-page/ui/profile-info.tsx index 4ba127ac..93e87afe 100644 --- a/src/features/my-page/ui/profile-info.tsx +++ b/src/features/my-page/ui/profile-info.tsx @@ -3,8 +3,6 @@ import { MemberInfo } from '@/entities/user/api/types'; import ProfileInfoEditModal from '@/features/my-page/ui/profile-info-edit-modal'; import ProfileInfoCard from '@/widgets/my-page/profileinfo-card'; -import { UpdateUserProfileInfoRequest } from '../api/types'; -import { useUpdateUserProfileInfoMutation } from '../model/use-update-user-profile-mutation'; interface ProfileInfoProps { memberId: number; @@ -15,21 +13,12 @@ export default function ProfileInfo({ memberId, memberInfo, }: ProfileInfoProps) { - const { mutate } = useUpdateUserProfileInfoMutation(memberId); - - const handleSubmit = (formData: UpdateUserProfileInfoRequest) => { - mutate(formData); - }; - return (
내 정보
- +
Date: Sun, 20 Jul 2025 23:53:53 +0900 Subject: [PATCH 28/58] =?UTF-8?q?refactor:=20=EB=82=B4=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=88=98=EC=A0=95=EC=9D=B4=20=EC=84=B1=EA=B3=B5?= =?UTF-8?q?=ED=96=88=EC=9D=84=20=EA=B2=BD=EC=9A=B0=20=ED=98=B8=EC=B6=9C?= =?UTF-8?q?=ED=95=A0=20onSuccess=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my-page/ui/profile-info-edit-modal.tsx | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/src/features/my-page/ui/profile-info-edit-modal.tsx b/src/features/my-page/ui/profile-info-edit-modal.tsx index 2ae158dc..1d9840af 100644 --- a/src/features/my-page/ui/profile-info-edit-modal.tsx +++ b/src/features/my-page/ui/profile-info-edit-modal.tsx @@ -74,6 +74,9 @@ function ProfileInfoEditForm({ ), }); + const { data: availableStudyTimes } = useAvailableStudyTimesQuery(); + const { data: studySubjects } = useStudySubjectsQuery(); + const { data: techStacks } = useTechStacksQuery(); const { mutate: updateProfileInfo } = useUpdateUserProfileInfoMutation(memberId); @@ -90,12 +93,23 @@ function ProfileInfoEditForm({ .map((id) => Number(id)), }; - updateProfileInfo(formData); - }; + updateProfileInfo(formData, { + onSuccess: () => { + const selectedSkillNames = techStacks.filter((techStack) => + infoForm.techStackIds.includes(techStack.techStackId), + ); - const { data: availableStudyTimes } = useAvailableStudyTimesQuery(); - const { data: studySubjects } = useStudySubjectsQuery(); - const { data: techStacks } = useTechStacksQuery(); + sendGTMEvent({ + event: 'custom_member_card', + dl_timestamp: new Date().toISOString(), + dl_member_id: hashValue(String(memberId)), + dl_tags: selectedSkillNames, + }); + + onClose(); + }, + }); + }; return ( <> @@ -197,26 +211,7 @@ function ProfileInfoEditForm({ 취소 -
From 6ebf8c403cdb721551d3f08caa86ed4738debf5c Mon Sep 17 00:00:00 2001 From: aken-you Date: Sun, 20 Jul 2025 23:56:09 +0900 Subject: [PATCH 29/58] =?UTF-8?q?refactor:=20=EB=82=B4=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=88=98=EC=A0=95=20=EB=AA=A8=EB=8B=AC=EC=9D=98=20?= =?UTF-8?q?"=EC=88=98=EC=A0=95=20=EC=99=84=EB=A3=8C"=20=EB=B2=84=ED=8A=BC?= =?UTF-8?q?=20disabled=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/my-page/ui/profile-info-edit-modal.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/features/my-page/ui/profile-info-edit-modal.tsx b/src/features/my-page/ui/profile-info-edit-modal.tsx index 1d9840af..f1ed5a2b 100644 --- a/src/features/my-page/ui/profile-info-edit-modal.tsx +++ b/src/features/my-page/ui/profile-info-edit-modal.tsx @@ -211,7 +211,17 @@ function ProfileInfoEditForm({ 취소 -
From 51541549b4c27777657cbfa0d63d1cf0d83fe1ef Mon Sep 17 00:00:00 2001 From: aken-you Date: Tue, 22 Jul 2025 01:06:50 +0900 Subject: [PATCH 30/58] =?UTF-8?q?refactor:=20=EC=97=B0=EB=9D=BD=EC=B2=98?= =?UTF-8?q?=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EC=A1=B0=EA=B1=B4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/my-page/ui/profile-edit-modal.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/features/my-page/ui/profile-edit-modal.tsx b/src/features/my-page/ui/profile-edit-modal.tsx index 08cedf1a..dd6a4500 100644 --- a/src/features/my-page/ui/profile-edit-modal.tsx +++ b/src/features/my-page/ui/profile-edit-modal.tsx @@ -81,7 +81,9 @@ function ProfileEditForm({ // 이름 유효성 검사: 2~10자, 한글 또는 영문만 허용 const isNameValid = /^[가-힣a-zA-Z]{2,10}$/.test(profileForm.name); // 연락처 유효성 검사: "(2~3자리 지역번호)-(3~4자리 번호)-(4자리 번호)" 형식 - const isTelValid = /^\d{2,3}-\d{3,4}-\d{4}$/.test(profileForm.tel); + const isTelValid = + profileForm.tel.length === 0 || + /^\d{2,3}-\d{3,4}-\d{4}$/.test(profileForm.tel); const queryClient = useQueryClient(); const { mutateAsync: updateProfile } = useUpdateUserProfileMutation(memberId); From bb21d01109bb369b4c0b077a8fd11f389c316797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EB=AF=BC=EC=A3=BC?= Date: Tue, 22 Jul 2025 01:50:15 +0900 Subject: [PATCH 31/58] =?UTF-8?q?fix:=20=EC=8A=A4=ED=84=B0=EB=94=94=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20studyDate=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/api/types.ts | 4 +--- src/features/study/model/use-study-query.ts | 5 +++++ src/widgets/home/study-list-table.tsx | 11 +++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/features/study/api/types.ts b/src/features/study/api/types.ts index 978f3503..f43c039b 100644 --- a/src/features/study/api/types.ts +++ b/src/features/study/api/types.ts @@ -47,9 +47,7 @@ export interface GetDailyStudyDetailParams2 { export interface GetDailyStudiesParams { cursor?: number; pageSize?: number; - year?: number; - month?: number; - day?: number; + studyDate?: string; } export interface GetDailyStudiesResponse { diff --git a/src/features/study/model/use-study-query.ts b/src/features/study/model/use-study-query.ts index 091764ca..b8f4cf99 100644 --- a/src/features/study/model/use-study-query.ts +++ b/src/features/study/model/use-study-query.ts @@ -15,6 +15,7 @@ import { MonthlyCalendarResponse, } from '../api/types'; +// 스터디 주간 참여 유무 확인 query export const useWeeklyParticipation = (params: GetDailyStudyDetailParams2) => { return useQuery({ queryKey: ['weeklyParticipation', params], @@ -23,6 +24,7 @@ export const useWeeklyParticipation = (params: GetDailyStudyDetailParams2) => { }); }; +// 스터디 상세 조회 query export const useDailyStudyDetailQuery = ( params: GetDailyStudyDetailParams, enabled: boolean = true, @@ -35,6 +37,7 @@ export const useDailyStudyDetailQuery = ( }); }; +// 스터디 전체 조회 query export const useDailyStudiesQuery = (params?: GetDailyStudiesParams) => { return useQuery({ queryKey: ['dailyStudies', params], @@ -43,6 +46,7 @@ export const useDailyStudiesQuery = (params?: GetDailyStudiesParams) => { }); }; +// 스터디 캘린더 조회 query export const useMonthlyStudyCalendarQuery = ( params: GetMonthlyCalendarParams, ) => { @@ -54,6 +58,7 @@ export const useMonthlyStudyCalendarQuery = ( }); }; +// 스터디 신청 mutation export const useJoinStudyMutation = () => { return useMutation({ mutationFn: (payload: JoinStudyRequest) => postJoinStudy(payload), diff --git a/src/widgets/home/study-list-table.tsx b/src/widgets/home/study-list-table.tsx index 1ad06baa..80778e99 100644 --- a/src/widgets/home/study-list-table.tsx +++ b/src/widgets/home/study-list-table.tsx @@ -68,16 +68,15 @@ function mapDailyStudyToDisplayData( } export default function StudyListSection({ date }: Props) { - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); + const offset = date.getTimezoneOffset() * 60000; + const dateOffset = new Date(date.getTime() - offset); + + const studyDate = dateOffset.toISOString().split('T')[0]; const { data, isLoading, error } = useDailyStudiesQuery({ cursor: 0, pageSize: 10, - year, - month, - day, + studyDate, }); if (isLoading) return
로딩 중...
; From d7d91423c0cfdb06035932d74f0899e8ee70008c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EB=AF=BC=EC=A3=BC?= Date: Tue, 22 Jul 2025 02:12:40 +0900 Subject: [PATCH 32/58] =?UTF-8?q?fix:=20=EB=A9=B4=EC=A0=91=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20api=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/api/get-study-data.ts | 29 +++-------- src/features/study/api/types.ts | 3 +- src/features/study/ui/today-study-modal.tsx | 58 ++++++++++----------- 3 files changed, 36 insertions(+), 54 deletions(-) diff --git a/src/features/study/api/get-study-data.ts b/src/features/study/api/get-study-data.ts index 07cc4932..72354c27 100644 --- a/src/features/study/api/get-study-data.ts +++ b/src/features/study/api/get-study-data.ts @@ -1,4 +1,5 @@ import type { + CompleteStudyRequest, DailyStudyDetail, GetDailyStudiesParams, GetDailyStudiesResponse, @@ -8,9 +9,7 @@ import type { JoinStudyRequest, MonthlyCalendarResponse, PostDailyRetrospectRequest, - PutRetrospectRequest, PutStudyDailyRequest, - StudyProgressStatus, WeeklyParticipationResponse, } from '@/features/study/api/types'; import { axiosInstance } from '@/shared/tanstack-query/axios'; @@ -60,29 +59,13 @@ export const putStudyDaily = async ( return res.data; }; -// 면접자 스터디 업데이트 [스터디 진행 상태] -export const patchStudyStatus = async ( +// 면접 완료 및 회고 작성 +export const completeStudy = async ( dailyStudyId: number, - progressStatus: StudyProgressStatus, + body: CompleteStudyRequest, ) => { - const res = await axiosInstance.patch( - `/study/daily/${dailyStudyId}/status`, - null, - { - params: { progressStatus }, - }, - ); - - return res.data; -}; - -// 면접자 스터디 업데이트 [피드백] -export const putRetrospect = async ( - retrospectId: number, - body: PutRetrospectRequest, -) => { - const res = await axiosInstance.put( - `/study/daily/retrospect/${retrospectId}`, + const res = await axiosInstance.post( + `/study/daily/${dailyStudyId}/complete`, body, ); diff --git a/src/features/study/api/types.ts b/src/features/study/api/types.ts index f43c039b..45ea1e9c 100644 --- a/src/features/study/api/types.ts +++ b/src/features/study/api/types.ts @@ -101,6 +101,7 @@ export interface WeeklyParticipationResponse { isParticipate: boolean; } -export interface PutRetrospectRequest { +export interface CompleteStudyRequest { description: string; + progressStatus: StudyProgressStatus; } diff --git a/src/features/study/ui/today-study-modal.tsx b/src/features/study/ui/today-study-modal.tsx index 93fd5d80..fc8e9f96 100644 --- a/src/features/study/ui/today-study-modal.tsx +++ b/src/features/study/ui/today-study-modal.tsx @@ -8,15 +8,10 @@ import Button from '@/shared/ui/button'; import { SingleDropdown } from '@/shared/ui/dropdown'; import { BaseInput, TextAreaInput } from '@/shared/ui/input'; import { Modal } from '@/shared/ui/modal'; -import CreateIcon from 'public/icons/create.svg'; -import { - patchStudyStatus, - putRetrospect, - putStudyDaily, -} from '../api/get-study-data'; +import { completeStudy, putStudyDaily } from '../api/get-study-data'; import { + CompleteStudyRequest, DailyStudyDetail, - PutRetrospectRequest, PutStudyDailyRequest, StudyProgressStatus, } from '../api/types'; @@ -60,9 +55,9 @@ export default function TodayStudyModal({ {isInterviewee ? ( - + ) : ( - + )} @@ -70,7 +65,7 @@ export default function TodayStudyModal({ ); } -function ReadyForm({ +function StudyReadyForm({ refetch, data, }: { @@ -168,34 +163,27 @@ function ReadyForm({ ); } -function DoneForm({ +function StudyDoneForm({ data, refetch, }: { data: DailyStudyDetail; refetch: () => void; }) { - const [feedback, setFeedback] = useState( - data.description ?? '', - ); - - const [progressStatus, setProgressStatus] = useState( - data.progressStatus ?? 'PENDING', - ); + const [form, setForm] = useState({ + description: data.description ?? '', + progressStatus: data.progressStatus ?? 'PENDING', + }); const handleSubmit = async (e: React.MouseEvent) => { - if (!feedback || !progressStatus) { + if (!form.description || !form.progressStatus) { e.preventDefault(); return; } try { - await patchStudyStatus(data.dailyStudyId, progressStatus); - - await putRetrospect(data.dailyStudyId, { - description: feedback, - }); + await completeStudy(data.dailyStudyId, form); await refetch(); } catch (err) { @@ -223,9 +211,14 @@ function DoneForm({ setProgressStatus(e as StudyProgressStatus)} + onChange={(e) => + setForm((prev) => ({ + ...prev, + progressStatus: e as StudyProgressStatus, + })) + } />
@@ -244,9 +237,11 @@ function DoneForm({ setFeedback(e)} + onChange={(value) => + setForm((prev) => ({ ...prev, description: value })) + } />
@@ -260,9 +255,12 @@ function DoneForm({ + + +
+ + ); +} diff --git a/src/features/study/ui/today-study-card.tsx b/src/features/study/ui/today-study-card.tsx index 6ad64340..80356be2 100644 --- a/src/features/study/ui/today-study-card.tsx +++ b/src/features/study/ui/today-study-card.tsx @@ -1,8 +1,11 @@ 'use client'; +import { useEffect, useState } from 'react'; +import { getCookie } from '@/shared/tanstack-query/cookie'; import UserAvatar from '@/shared/ui/avatar'; import Badge from '@/shared/ui/badge'; -import TodayStudyModal from './today-study-modal'; +import StudyDoneModal from './study-done-modal'; +import StudyReadyModal from './study-ready-modal'; import { DailyStudyDetail, StudyProgressStatus } from '../api/types'; interface Props { @@ -18,11 +21,25 @@ const statusBadgeMap: Partial> = { }; export default function TodayStudyCard({ data, refetch }: Props) { + const [memberId, setMemberId] = useState(null); + + useEffect(() => { + const id = getCookie('memberId'); + setMemberId(id ? Number(id) : null); + }, []); + + const isInterviewee = memberId === data.intervieweeId; + return (

오늘의 스터디

- + {memberId !== null && + (isInterviewee ? ( + + ) : ( + + ))}
diff --git a/src/features/study/ui/today-study-modal.tsx b/src/features/study/ui/today-study-modal.tsx deleted file mode 100644 index fc8e9f96..00000000 --- a/src/features/study/ui/today-study-modal.tsx +++ /dev/null @@ -1,273 +0,0 @@ -'use client'; - -import { XIcon } from 'lucide-react'; -import { useEffect, useState } from 'react'; -import { cn } from '@/shared/shadcn/lib/utils'; -import { getCookie } from '@/shared/tanstack-query/cookie'; -import Button from '@/shared/ui/button'; -import { SingleDropdown } from '@/shared/ui/dropdown'; -import { BaseInput, TextAreaInput } from '@/shared/ui/input'; -import { Modal } from '@/shared/ui/modal'; -import { completeStudy, putStudyDaily } from '../api/get-study-data'; -import { - CompleteStudyRequest, - DailyStudyDetail, - PutStudyDailyRequest, - StudyProgressStatus, -} from '../api/types'; -import { STUDY_PROGRESS_OPTIONS } from '../consts/study-const'; - -interface TodayStudyModalProps { - data: DailyStudyDetail; - refetch: () => void; -} - -export default function TodayStudyModal({ - data, - refetch, -}: TodayStudyModalProps) { - const [memberId, setMemberId] = useState(null); - - useEffect(() => { - const id = getCookie('memberId'); - setMemberId(id ? Number(id) : null); - }, []); - - if (memberId === null) return null; - - const isInterviewee = memberId === data.intervieweeId; - - return ( - - - - - - - - - - {isInterviewee ? '면접 준비하기' : '면접 완료하기'} - - - - - - - {isInterviewee ? ( - - ) : ( - - )} - - - - ); -} - -function StudyReadyForm({ - refetch, - data, -}: { - refetch: () => void; - data: DailyStudyDetail; -}) { - const [interviewTopic, setInterviewTopic] = useState< - PutStudyDailyRequest['subject'] - >(data.subject ?? ''); - - const [referenceLink, setReferenceLink] = useState< - PutStudyDailyRequest['link'] - >(data.link ?? ''); - - const handleSubmit = async (e: React.MouseEvent) => { - if (!interviewTopic) { - e.preventDefault(); - - return; - } - - try { - await putStudyDaily(data.dailyStudyId, { - subject: interviewTopic, - description: '', - link: referenceLink, - }); - await refetch(); - } catch (err) { - e.preventDefault(); - console.error(err); - alert('요청 처리에 실패했습니다. 다시 시도해주세요.'); - } - }; - - return ( - <> - -
-
- - - 이번 스터디에서 다룰 면접 주제를 입력하세요 - -
- - setInterviewTopic(e.target.value)} - /> -
- -
-
- - - 참고할 링크나 자료가 있다면 입력해 주세요 - -
- - setReferenceLink(e.target.value)} - /> -
-
- - - - - - - - - - - ); -} - -function StudyDoneForm({ - data, - refetch, -}: { - data: DailyStudyDetail; - refetch: () => void; -}) { - const [form, setForm] = useState({ - description: data.description ?? '', - progressStatus: data.progressStatus ?? 'PENDING', - }); - - const handleSubmit = async (e: React.MouseEvent) => { - if (!form.description || !form.progressStatus) { - e.preventDefault(); - - return; - } - - try { - await completeStudy(data.dailyStudyId, form); - - await refetch(); - } catch (err) { - e.preventDefault(); - console.error(err); - alert('요청 처리에 실패했습니다. 다시 시도해주세요.'); - } - }; - - return ( - <> - -
-
- - - 면접 완료 후 해당 지원자의 상태를 업데이트해 주세요. - -
- - - setForm((prev) => ({ - ...prev, - progressStatus: e as StudyProgressStatus, - })) - } - /> -
- -
-
- - - 면접 결과에 대한 간단한 피드백을 입력해 주세요. - -
- - - setForm((prev) => ({ ...prev, description: value })) - } - /> -
-
- - - - - - - - - - - ); -} From 4268bb9d79732c5d1bfd9c46f952c1eff0633481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EB=AF=BC=EC=A3=BC?= Date: Tue, 22 Jul 2025 22:16:48 +0900 Subject: [PATCH 34/58] =?UTF-8?q?fix:=20=EB=A9=B4=EC=A0=91=20=EC=A4=80?= =?UTF-8?q?=EB=B9=84=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/api/get-study-data.ts | 8 +- src/features/study/api/types.ts | 3 +- src/features/study/ui/study-ready-modal.tsx | 145 ++++++++++++++++++++ 3 files changed, 150 insertions(+), 6 deletions(-) create mode 100644 src/features/study/ui/study-ready-modal.tsx diff --git a/src/features/study/api/get-study-data.ts b/src/features/study/api/get-study-data.ts index 72354c27..7dbcd6b6 100644 --- a/src/features/study/api/get-study-data.ts +++ b/src/features/study/api/get-study-data.ts @@ -9,7 +9,7 @@ import type { JoinStudyRequest, MonthlyCalendarResponse, PostDailyRetrospectRequest, - PutStudyDailyRequest, + PrepareStudyRequest, WeeklyParticipationResponse, } from '@/features/study/api/types'; import { axiosInstance } from '@/shared/tanstack-query/axios'; @@ -49,12 +49,12 @@ export const postDailyRetrospect = async (body: PostDailyRetrospectRequest) => { return res.data; }; -// 피면접자 스터디 업데이트 +// 면접 준비 시작 export const putStudyDaily = async ( dailyId: number, - body: PutStudyDailyRequest, + body: PrepareStudyRequest, ) => { - const res = await axiosInstance.put(`/study/daily/${dailyId}`, body); + const res = await axiosInstance.put(`/study/daily/${dailyId}/prepare`, body); return res.data; }; diff --git a/src/features/study/api/types.ts b/src/features/study/api/types.ts index 45ea1e9c..557ddcb6 100644 --- a/src/features/study/api/types.ts +++ b/src/features/study/api/types.ts @@ -78,9 +78,8 @@ export interface PostDailyRetrospectRequest { parentId: number; } -export interface PutStudyDailyRequest { +export interface PrepareStudyRequest { subject: string; - description: string; link: string; } diff --git a/src/features/study/ui/study-ready-modal.tsx b/src/features/study/ui/study-ready-modal.tsx new file mode 100644 index 00000000..c5e498bc --- /dev/null +++ b/src/features/study/ui/study-ready-modal.tsx @@ -0,0 +1,145 @@ +'use client'; + +import { XIcon } from 'lucide-react'; +import { useState } from 'react'; +import { cn } from '@/shared/shadcn/lib/utils'; +import Button from '@/shared/ui/button'; +import { BaseInput } from '@/shared/ui/input'; +import { Modal } from '@/shared/ui/modal'; +import { putStudyDaily } from '../api/get-study-data'; +import { DailyStudyDetail, PrepareStudyRequest } from '../api/types'; + +interface Props { + data: DailyStudyDetail; + refetch: () => void; +} + +export default function StudyReadyModal({ data, refetch }: Props) { + const [isOpen, setIsOpen] = useState(false); + + return ( + + setIsOpen(true)}> +
+ 준비하기 +
+
+ + + + + +
+ 면접 준비하기 + + + +
+
+ + setIsOpen(false)} + /> +
+
+
+ ); +} + +function StudyReadyForm({ + data, + refetch, + onClose, +}: { + data: DailyStudyDetail; + refetch: () => void; + onClose: () => void; +}) { + const [form, setForm] = useState({ + subject: data.subject ?? '', + link: data.link ?? '', + }); + + const handleSubmit = async (e: React.MouseEvent) => { + if (!form.subject.trim()) { + e.preventDefault(); + + return; + } + + try { + await putStudyDaily(data.dailyStudyId, form); + await refetch(); + onClose(); + } catch (err) { + e.preventDefault(); + console.error(err); + alert('요청 처리에 실패했습니다. 다시 시도해주세요.'); + } + }; + + return ( + <> + +
+
+ + + 이번 스터디에서 다룰 면접 주제를 입력하세요 + +
+ + + setForm((prev) => ({ ...prev, subject: e.target.value })) + } + /> +
+ +
+
+ + + 참고할 링크나 자료가 있다면 입력해 주세요 + +
+ + + setForm((prev) => ({ ...prev, link: e.target.value })) + } + /> +
+
+ + +
+ + +
+
+ + ); +} From 54da5e5f5287ca293069f07d070f080b80b4c36e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EB=AF=BC=EC=A3=BC?= Date: Tue, 22 Jul 2025 22:57:59 +0900 Subject: [PATCH 35/58] =?UTF-8?q?refactor:=20=EB=B1=83=EC=A7=80=EB=A7=B5?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/lib/ui/status-badge-map.tsx | 18 ++++++++++++++++++ src/features/study/ui/today-study-card.tsx | 15 ++------------- src/shared/ui/badge/index.tsx | 13 ++++++++----- src/widgets/home/study-list-table.tsx | 16 +++------------- 4 files changed, 31 insertions(+), 31 deletions(-) create mode 100644 src/features/study/lib/ui/status-badge-map.tsx diff --git a/src/features/study/lib/ui/status-badge-map.tsx b/src/features/study/lib/ui/status-badge-map.tsx new file mode 100644 index 00000000..290721c9 --- /dev/null +++ b/src/features/study/lib/ui/status-badge-map.tsx @@ -0,0 +1,18 @@ +import type { ReactNode } from 'react'; +import Badge from '@/shared/ui/badge'; +import { StudyProgressStatus } from '../../api/types'; + +export function getStatusBadge(status: StudyProgressStatus): ReactNode { + switch (status) { + case 'PENDING': + return 시작 전; + case 'IN_PROGRESS': + return 진행중; + case 'COMPLETE': + return 완료; + case 'ABSENT': + return 불참; + default: + return null; + } +} diff --git a/src/features/study/ui/today-study-card.tsx b/src/features/study/ui/today-study-card.tsx index 80356be2..c5997f3e 100644 --- a/src/features/study/ui/today-study-card.tsx +++ b/src/features/study/ui/today-study-card.tsx @@ -1,25 +1,18 @@ 'use client'; import { useEffect, useState } from 'react'; +import { getStatusBadge } from '@/features/study/lib/ui/status-badge-map'; import { getCookie } from '@/shared/tanstack-query/cookie'; import UserAvatar from '@/shared/ui/avatar'; -import Badge from '@/shared/ui/badge'; import StudyDoneModal from './study-done-modal'; import StudyReadyModal from './study-ready-modal'; -import { DailyStudyDetail, StudyProgressStatus } from '../api/types'; +import { DailyStudyDetail } from '../api/types'; interface Props { data: DailyStudyDetail; refetch: () => void; } -const statusBadgeMap: Partial> = { - PENDING: 시작 전, - IN_PROGRESS: 진행중, - COMPLETE: 완료, - ABSENT: 불참, -}; - export default function TodayStudyCard({ data, refetch }: Props) { const [memberId, setMemberId] = useState(null); @@ -81,8 +74,4 @@ export default function TodayStudyCard({ data, refetch }: Props) {
); } - - function getStatusBadge(status: StudyProgressStatus) { - return statusBadgeMap[status] ?? null; - } } diff --git a/src/shared/ui/badge/index.tsx b/src/shared/ui/badge/index.tsx index d8663c74..121e3802 100644 --- a/src/shared/ui/badge/index.tsx +++ b/src/shared/ui/badge/index.tsx @@ -6,14 +6,17 @@ import * as React from 'react'; import { cn } from '@/shared/shadcn/lib/utils'; const badgeVariants = cva( - 'inline-flex min-w-[24px] px-100 py-50 justify-center items-center gap-[2px] text-xs font-medium whitespace-nowrap', + 'inline-flex min-w-[24px] px-100 py-50 justify-center items-center border gap-[2px] text-xs font-medium whitespace-nowrap', { variants: { color: { - default: - 'bg-background-accent-blue-subtle text-text-subtlest border-border-default', - completed: 'bg-fill-success-subtle-default text-text-success', - incomplete: 'bg-fill-danger-subtle-default text-text-error', + default: 'text-text-subtlest border-border-default', + completed: + 'bg-fill-success-subtle-default text-text-success border-fill-success-subtle-default', + incomplete: + 'bg-fill-danger-subtle-default text-text-error border-fill-danger-subtle-default', + progress: + 'bg-background-accent-blue-default text-text-information border-background-accent-blue-default', orange: 'bg-background-accent-orange-subtle text-background-accent-orange-strong', gray: 'bg-background-accent-gray-subtle text-background-accent-gray-strong', diff --git a/src/widgets/home/study-list-table.tsx b/src/widgets/home/study-list-table.tsx index 80778e99..2640c64e 100644 --- a/src/widgets/home/study-list-table.tsx +++ b/src/widgets/home/study-list-table.tsx @@ -1,12 +1,9 @@ +import { getStatusBadge } from '@/features/study/lib/ui/status-badge-map'; import { useDailyStudiesQuery } from '@/features/study/model/use-study-query'; import UserAvatar from '@/shared/ui/avatar'; -import Badge from '@/shared/ui/badge/index'; import TableList from '@/shared/ui/table'; import LinkIcon from 'public/icons/Link.svg'; -import { - DailyStudy, - StudyProgressStatus, -} from '../../features/study/api/types'; +import { DailyStudy } from '../../features/study/api/types'; const headers = [ '조', @@ -23,13 +20,6 @@ interface Props { date: Date; } -const statusBadgeMap: Partial> = { - PENDING: 시작 전, - IN_PROGRESS: 진행중, - COMPLETE: 완료, - ABSENT: 불참, -}; - function mapDailyStudyToDisplayData( row: DailyStudy, index: number, @@ -58,7 +48,7 @@ function mapDailyStudyToDisplayData( {row.feedback ?? '-'}

), - '진행 상태': statusBadgeMap[row.progressStatus], + '진행 상태': getStatusBadge(row.progressStatus), '참고 자료': row.link ? ( From 52f730718082c3ddd30cd2eabbb93af7240112e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EB=AF=BC=EC=A3=BC?= Date: Tue, 22 Jul 2025 23:29:41 +0900 Subject: [PATCH 36/58] =?UTF-8?q?style:=20link=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/Link.svg | 2 +- src/widgets/home/study-list-table.tsx | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/public/icons/Link.svg b/public/icons/Link.svg index 1b6bd7da..90694b76 100644 --- a/public/icons/Link.svg +++ b/public/icons/Link.svg @@ -1,5 +1,5 @@ - + diff --git a/src/widgets/home/study-list-table.tsx b/src/widgets/home/study-list-table.tsx index 2640c64e..0e1caca5 100644 --- a/src/widgets/home/study-list-table.tsx +++ b/src/widgets/home/study-list-table.tsx @@ -51,9 +51,11 @@ function mapDailyStudyToDisplayData( '진행 상태': getStatusBadge(row.progressStatus), '참고 자료': row.link ? ( - + - ) : null, + ) : ( + + ), }; } From be689c645f0fbc5eff7801b18eaccd4c2ab6d4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EB=AF=BC=EC=A3=BC?= Date: Wed, 23 Jul 2025 23:21:06 +0900 Subject: [PATCH 37/58] =?UTF-8?q?style:=20badge=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/lib/ui/status-badge-map.tsx | 6 +++--- src/shared/ui/badge/index.tsx | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/features/study/lib/ui/status-badge-map.tsx b/src/features/study/lib/ui/status-badge-map.tsx index 290721c9..30dfd1cc 100644 --- a/src/features/study/lib/ui/status-badge-map.tsx +++ b/src/features/study/lib/ui/status-badge-map.tsx @@ -7,11 +7,11 @@ export function getStatusBadge(status: StudyProgressStatus): ReactNode { case 'PENDING': return 시작 전; case 'IN_PROGRESS': - return 진행중; + return 진행중; case 'COMPLETE': - return 완료; + return 완료; case 'ABSENT': - return 불참; + return 불참; default: return null; } diff --git a/src/shared/ui/badge/index.tsx b/src/shared/ui/badge/index.tsx index 121e3802..9dd0978e 100644 --- a/src/shared/ui/badge/index.tsx +++ b/src/shared/ui/badge/index.tsx @@ -11,12 +11,10 @@ const badgeVariants = cva( variants: { color: { default: 'text-text-subtlest border-border-default', - completed: + green: 'bg-fill-success-subtle-default text-text-success border-fill-success-subtle-default', - incomplete: - 'bg-fill-danger-subtle-default text-text-error border-fill-danger-subtle-default', - progress: - 'bg-background-accent-blue-default text-text-information border-background-accent-blue-default', + red: 'bg-fill-danger-subtle-default text-text-error border-fill-danger-subtle-default', + blue: 'bg-background-accent-blue-subtle text-text-information border-background-accent-blue-subtle', orange: 'bg-background-accent-orange-subtle text-background-accent-orange-strong', gray: 'bg-background-accent-gray-subtle text-background-accent-gray-strong', From be1c023fa85175bff62c3218f7805e9ab4c16e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EB=AF=BC=EC=A3=BC?= Date: Wed, 23 Jul 2025 23:21:15 +0900 Subject: [PATCH 38/58] =?UTF-8?q?fix:=20=EB=A9=B4=EC=A0=91=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/api/types.ts | 2 +- src/features/study/ui/study-done-modal.tsx | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/features/study/api/types.ts b/src/features/study/api/types.ts index 557ddcb6..a5eb1c54 100644 --- a/src/features/study/api/types.ts +++ b/src/features/study/api/types.ts @@ -101,6 +101,6 @@ export interface WeeklyParticipationResponse { } export interface CompleteStudyRequest { - description: string; + feedback: string; progressStatus: StudyProgressStatus; } diff --git a/src/features/study/ui/study-done-modal.tsx b/src/features/study/ui/study-done-modal.tsx index f5a6f438..a8e5bcdb 100644 --- a/src/features/study/ui/study-done-modal.tsx +++ b/src/features/study/ui/study-done-modal.tsx @@ -63,12 +63,12 @@ function StudyDoneForm({ onClose: () => void; }) { const [form, setForm] = useState({ - description: data.description ?? '', + feedback: data.feedback ?? '', progressStatus: data.progressStatus ?? 'PENDING', }); const handleSubmit = async (e: React.MouseEvent) => { - if (!form.description || !form.progressStatus) { + if (!form.feedback || !form.progressStatus) { e.preventDefault(); return; @@ -129,10 +129,10 @@ function StudyDoneForm({ - setForm((prev) => ({ ...prev, description: value })) + setForm((prev) => ({ ...prev, feedback: value })) } /> @@ -146,11 +146,10 @@ function StudyDoneForm({
); diff --git a/src/widgets/home/study-list-table.tsx b/src/widgets/home/study-list-table.tsx index 0e1caca5..7dcd14d9 100644 --- a/src/widgets/home/study-list-table.tsx +++ b/src/widgets/home/study-list-table.tsx @@ -1,5 +1,5 @@ -import { getStatusBadge } from '@/features/study/lib/ui/status-badge-map'; import { useDailyStudiesQuery } from '@/features/study/model/use-study-query'; +import { getStatusBadge } from '@/features/study/ui/status-badge-map'; import UserAvatar from '@/shared/ui/avatar'; import TableList from '@/shared/ui/table'; import LinkIcon from 'public/icons/Link.svg'; @@ -16,10 +16,6 @@ const headers = [ ] as const; type Header = (typeof headers)[number]; -interface Props { - date: Date; -} - function mapDailyStudyToDisplayData( row: DailyStudy, index: number, @@ -59,12 +55,7 @@ function mapDailyStudyToDisplayData( }; } -export default function StudyListSection({ date }: Props) { - const offset = date.getTimezoneOffset() * 60000; - const dateOffset = new Date(date.getTime() - offset); - - const studyDate = dateOffset.toISOString().split('T')[0]; - +export default function StudyListSection({ studyDate }: { studyDate: string }) { const { data, isLoading, error } = useDailyStudiesQuery({ cursor: 0, pageSize: 10, From f89b41195d41aa8230d6220cca9e62c5340763e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EB=AF=BC=EC=A3=BC?= Date: Thu, 24 Jul 2025 02:36:22 +0900 Subject: [PATCH 43/58] =?UTF-8?q?fix:=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EC=BF=BC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/model/use-study-query.ts | 31 ++++++-- src/features/study/ui/study-done-modal.tsx | 81 ++++++++++----------- src/features/study/ui/study-ready-modal.tsx | 78 ++++++++++---------- 3 files changed, 105 insertions(+), 85 deletions(-) diff --git a/src/features/study/model/use-study-query.ts b/src/features/study/model/use-study-query.ts index b8f4cf99..4926aefb 100644 --- a/src/features/study/model/use-study-query.ts +++ b/src/features/study/model/use-study-query.ts @@ -1,4 +1,4 @@ -import { useMutation, useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { getDailyStudies, getDailyStudyDetail, @@ -8,7 +8,6 @@ import { } from '@/features/study/api/get-study-data'; import { GetDailyStudiesParams, - GetDailyStudyDetailParams, GetDailyStudyDetailParams2, GetMonthlyCalendarParams, JoinStudyRequest, @@ -25,15 +24,12 @@ export const useWeeklyParticipation = (params: GetDailyStudyDetailParams2) => { }; // 스터디 상세 조회 query -export const useDailyStudyDetailQuery = ( - params: GetDailyStudyDetailParams, - enabled: boolean = true, -) => { +export const useDailyStudyDetailQuery = (params: string) => { return useQuery({ queryKey: ['dailyStudyDetail', params], queryFn: () => getDailyStudyDetail(params), staleTime: 60 * 1000, - enabled: enabled && !!params, + enabled: !!params, }); }; @@ -64,3 +60,24 @@ export const useJoinStudyMutation = () => { mutationFn: (payload: JoinStudyRequest) => postJoinStudy(payload), }); }; + +// 스터디 상세 & 리스트 업데이트 +export const useInvalidateStudyQueries = () => { + const queryClient = useQueryClient(); + + const invalidateDailyStudyDetail = async (params: string) => { + await queryClient.invalidateQueries({ + queryKey: ['dailyStudyDetail', params], + exact: true, + }); + }; + + const invalidateDailyStudies = async (params: GetDailyStudiesParams) => { + await queryClient.invalidateQueries({ + queryKey: ['dailyStudies', params], + exact: true, + }); + }; + + return { invalidateDailyStudyDetail, invalidateDailyStudies }; +}; diff --git a/src/features/study/ui/study-done-modal.tsx b/src/features/study/ui/study-done-modal.tsx index a8e5bcdb..ca2703f3 100644 --- a/src/features/study/ui/study-done-modal.tsx +++ b/src/features/study/ui/study-done-modal.tsx @@ -14,37 +14,40 @@ import { StudyProgressStatus, } from '../api/types'; import { STUDY_PROGRESS_OPTIONS } from '../consts/study-const'; +import { useInvalidateStudyQueries } from '../model/use-study-query'; -interface Props { +interface StudyDoneModalProps { data: DailyStudyDetail; - refetch: () => void; + studyDate: string; } -export default function StudyDoneModal({ data, refetch }: Props) { +export default function StudyDoneModal({ + data, + studyDate, +}: StudyDoneModalProps) { const [isOpen, setIsOpen] = useState(false); return ( - setIsOpen(true)}> +
완료하기
+ -
- 면접 완료하기 - - - -
+ 면접 완료하기 + + +
setIsOpen(false)} />
@@ -53,33 +56,35 @@ export default function StudyDoneModal({ data, refetch }: Props) { ); } -function StudyDoneForm({ - data, - refetch, - onClose, -}: { +interface StudyDoneFormProps { data: DailyStudyDetail; - refetch: () => void; + studyDate: string; onClose: () => void; -}) { +} + +function StudyDoneForm({ data, studyDate, onClose }: StudyDoneFormProps) { const [form, setForm] = useState({ feedback: data.feedback ?? '', progressStatus: data.progressStatus ?? 'PENDING', }); - const handleSubmit = async (e: React.MouseEvent) => { - if (!form.feedback || !form.progressStatus) { - e.preventDefault(); + const { feedback, progressStatus } = form; + const { invalidateDailyStudyDetail, invalidateDailyStudies } = + useInvalidateStudyQueries(); - return; - } + const handleChange = (key: keyof CompleteStudyRequest) => (value: string) => { + setForm((prev) => ({ ...prev, [key]: value })); + }; + + const handleSubmit = async () => { + if (!feedback.trim() || !progressStatus) return; try { await completeStudy(data.dailyStudyId, form); - await refetch(); + await invalidateDailyStudyDetail(studyDate); + await invalidateDailyStudies({ studyDate, cursor: 0, pageSize: 10 }); onClose(); } catch (err) { - e.preventDefault(); console.error(err); alert('요청 처리에 실패했습니다. 다시 시도해주세요.'); } @@ -90,7 +95,7 @@ function StudyDoneForm({
-
+ {/* 피드백 */}
-
@@ -145,11 +146,9 @@ function StudyDoneForm({
), - '면접 주제': row.subject, - 피드백: ( -

- {row.feedback ?? '-'} -

- ), + '면접 주제': row.subject || '-', + 피드백: row.feedback || '-', '진행 상태': getStatusBadge(row.progressStatus), '참고 자료': row.link ? ( From e8a2755750a9d3b7155241dd825b98e5257f5f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EB=AF=BC=EC=A3=BC?= Date: Thu, 24 Jul 2025 02:58:38 +0900 Subject: [PATCH 45/58] =?UTF-8?q?delete:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/ui/status-badge-map.tsx | 2 +- src/features/study/ui/study-card.tsx | 5 +---- src/features/study/ui/study-done-modal.tsx | 6 +----- src/features/study/ui/study-ready-modal.tsx | 3 +-- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/features/study/ui/status-badge-map.tsx b/src/features/study/ui/status-badge-map.tsx index 30dfd1cc..19cbaa63 100644 --- a/src/features/study/ui/status-badge-map.tsx +++ b/src/features/study/ui/status-badge-map.tsx @@ -1,6 +1,6 @@ import type { ReactNode } from 'react'; import Badge from '@/shared/ui/badge'; -import { StudyProgressStatus } from '../../api/types'; +import { StudyProgressStatus } from '../api/types'; export function getStatusBadge(status: StudyProgressStatus): ReactNode { switch (status) { diff --git a/src/features/study/ui/study-card.tsx b/src/features/study/ui/study-card.tsx index a54adc3e..6cb4faa4 100644 --- a/src/features/study/ui/study-card.tsx +++ b/src/features/study/ui/study-card.tsx @@ -5,10 +5,7 @@ import { useState } from 'react'; import DateSelector from './data-selector'; import TodayStudyCard from './today-study-card'; import StudyListSection from '../../../widgets/home/study-list-table'; -import { - useDailyStudyDetailQuery, - useWeeklyParticipation, -} from '../model/use-study-query'; +import { useWeeklyParticipation } from '../model/use-study-query'; // 스터디 주차 구하는 함수 function getWeekly(date: Date): { month: number; week: number } { diff --git a/src/features/study/ui/study-done-modal.tsx b/src/features/study/ui/study-done-modal.tsx index ca2703f3..6ca7f462 100644 --- a/src/features/study/ui/study-done-modal.tsx +++ b/src/features/study/ui/study-done-modal.tsx @@ -2,7 +2,6 @@ import { XIcon } from 'lucide-react'; import { useState } from 'react'; -import { cn } from '@/shared/shadcn/lib/utils'; import Button from '@/shared/ui/button'; import { SingleDropdown } from '@/shared/ui/dropdown'; import { TextAreaInput } from '@/shared/ui/input'; @@ -116,7 +115,6 @@ function StudyDoneForm({ data, studyDate, onClose }: StudyDoneFormProps) { />
- {/* 피드백 */}
@@ -123,7 +123,7 @@ function StudyReadyForm({ data, studyDate, onClose }: StudyReadyFormProps) { handleChange('link')(e.target.value)} />
From 9662ba9250c0fe351ae2f7624c1d0735a69e17b8 Mon Sep 17 00:00:00 2001 From: SEONG JIN JO <123255935+seong-jin-jo@users.noreply.github.com> Date: Fri, 25 Jul 2025 01:03:48 +0900 Subject: [PATCH 49/58] =?UTF-8?q?fix=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EB=A6=AC=EB=94=94=EB=A0=89=EC=85=98=EC=8B=9C=20=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=EC=97=90=20=EC=86=8C=EC=85=9C=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EB=94=94=EC=BD=94=EB=94=A9=ED=95=B4=EC=84=9C=20?= =?UTF-8?q?=EB=84=A3=EA=B8=B02?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/auth/ui/sign-up-modal.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/features/auth/ui/sign-up-modal.tsx b/src/features/auth/ui/sign-up-modal.tsx index ebf64fae..56630242 100644 --- a/src/features/auth/ui/sign-up-modal.tsx +++ b/src/features/auth/ui/sign-up-modal.tsx @@ -29,6 +29,9 @@ export default function SignupModal({ const signUp = useSignUpMutation(); const uploadProfileImage = useUploadProfileImageMutation(); + // 디버깅 + console.log('Decoded 소셜이미지 cookie', getCookie('socialImageURL')); + // 이름 유효성 검사 const validateName = (value: string) => { if (!/^[가-힣a-zA-Z]{2,10}$/.test(value)) { From 8f9d035c6af3c2c5dbad105e94d4fd90917c1f11 Mon Sep 17 00:00:00 2001 From: aken-you Date: Fri, 25 Jul 2025 01:21:26 +0900 Subject: [PATCH 50/58] =?UTF-8?q?feat:=20=EC=8A=A4=ED=84=B0=EB=94=94=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=20=EC=99=84=EB=A3=8C=20=EC=95=88=EB=82=B4=20?= =?UTF-8?q?=EB=AC=B8=EA=B5=AC=20alert=EB=A1=9C=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/study/ui/start-study-modal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/features/study/ui/start-study-modal.tsx b/src/features/study/ui/start-study-modal.tsx index bbf66317..e411b252 100644 --- a/src/features/study/ui/start-study-modal.tsx +++ b/src/features/study/ui/start-study-modal.tsx @@ -321,6 +321,7 @@ export default function StartStudyModal({ memberId }: StartStudyModalProps) { }, { onSuccess: () => { + alert('스터디 신청이 완료되었습니다!'); router.refresh(); }, onError: () => { From e945d9184681b33e50a901f8be2379e666732db2 Mon Sep 17 00:00:00 2001 From: SEONG JIN JO <123255935+seong-jin-jo@users.noreply.github.com> Date: Fri, 25 Jul 2025 02:50:39 +0900 Subject: [PATCH 51/58] =?UTF-8?q?chore=20:=20=EC=A3=BC=EC=84=9D=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20(=EB=B0=B0=ED=8F=AC=ED=99=98=EA=B2=BD=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=8A=94=20=EC=9D=B8=ED=94=84=EB=9D=BC?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=A7=84=ED=96=89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/auth/ui/sign-up-modal.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/features/auth/ui/sign-up-modal.tsx b/src/features/auth/ui/sign-up-modal.tsx index 56630242..ebf64fae 100644 --- a/src/features/auth/ui/sign-up-modal.tsx +++ b/src/features/auth/ui/sign-up-modal.tsx @@ -29,9 +29,6 @@ export default function SignupModal({ const signUp = useSignUpMutation(); const uploadProfileImage = useUploadProfileImageMutation(); - // 디버깅 - console.log('Decoded 소셜이미지 cookie', getCookie('socialImageURL')); - // 이름 유효성 검사 const validateName = (value: string) => { if (!/^[가-힣a-zA-Z]{2,10}$/.test(value)) { From 6dcce3a01363ef085a2d0e480024fc8e5acd7dfb Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 26 Jul 2025 01:12:37 +0900 Subject: [PATCH 52/58] =?UTF-8?q?refactor:=20=EC=95=BD=EA=B4=80=EB=8F=99?= =?UTF-8?q?=EC=9D=98=20checkbox=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/auth/ui/login-modal.tsx | 6 --- src/features/auth/ui/sign-up-modal.tsx | 57 +++++++++++++-------- src/features/auth/ui/sign-up-name-input.tsx | 31 ----------- 3 files changed, 35 insertions(+), 59 deletions(-) delete mode 100644 src/features/auth/ui/sign-up-name-input.tsx diff --git a/src/features/auth/ui/login-modal.tsx b/src/features/auth/ui/login-modal.tsx index 260dfccd..0a53946d 100644 --- a/src/features/auth/ui/login-modal.tsx +++ b/src/features/auth/ui/login-modal.tsx @@ -112,12 +112,6 @@ export default function LoginModal({ -
- - -
diff --git a/src/features/auth/ui/sign-up-modal.tsx b/src/features/auth/ui/sign-up-modal.tsx index ebf64fae..0a43171e 100644 --- a/src/features/auth/ui/sign-up-modal.tsx +++ b/src/features/auth/ui/sign-up-modal.tsx @@ -1,3 +1,4 @@ +import { sendGTMEvent } from '@next/third-parties/google'; import { XIcon } from 'lucide-react'; import { useState, useRef } from 'react'; import { @@ -5,12 +6,11 @@ import { useUploadProfileImageMutation, } from '@/features/auth/model/use-auth-mutation'; import SignupImageSelector from '@/features/auth/ui/sign-up-image-selector'; -import SignupNameInput from '@/features/auth/ui/sign-up-name-input'; +import { hashValue } from '@/shared/lib/hash'; import { getCookie, setCookie } from '@/shared/tanstack-query/cookie'; import Button from '@/shared/ui/button'; +import { BaseInput } from '@/shared/ui/input'; import { Modal } from '@/shared/ui/modal'; -import { sendGTMEvent } from '@next/third-parties/google'; -import { hashValue } from '@/shared/lib/hash'; export default function SignupModal({ open, @@ -20,7 +20,6 @@ export default function SignupModal({ onClose: () => void; }) { const [name, setName] = useState(''); - const [error, setError] = useState(''); const [image, setImage] = useState( getCookie('socialImageURL') || 'profile-default.svg', ); @@ -29,15 +28,7 @@ export default function SignupModal({ const signUp = useSignUpMutation(); const uploadProfileImage = useUploadProfileImageMutation(); - // 이름 유효성 검사 - const validateName = (value: string) => { - if (!/^[가-힣a-zA-Z]{2,10}$/.test(value)) { - setError('이름에는 숫자나 특수문자를 사용할 수 없습니다.'); - } else { - setError(''); - } - setName(value); - }; + const isValidName = /^[가-힣a-zA-Z]{2,10}$/.test(name); // 이미지 업로드 const handleImageChange = (e: React.ChangeEvent) => { @@ -60,12 +51,11 @@ export default function SignupModal({ { // 회원가입 성공 시 프로필 이미지 업로드 onSuccess: (data) => { - const memberId = data.content.generatedMemberId; if (data && memberId) { - setCookie('memberId', memberId) - + setCookie('memberId', memberId); + // 회원가입 GA 이벤트 전송 sendGTMEvent({ event: 'custom_member_join', @@ -85,7 +75,6 @@ export default function SignupModal({ file: formData, }); } - // 성공 후 홈페이지로 이동 window.location.href = '/'; @@ -124,17 +113,41 @@ export default function SignupModal({
서비스 이용을 위해 이름을 입력해주세요.
- +
+
+ setName(e.target.value)} + placeholder="홍길동" + className={`w-full`} + color={isValidName ? 'default' : 'error'} + /> +
+ {isValidName + ? '신뢰 있는 매칭을 위해 실명을 사용해주세요. (예: 홍길동 )' + : '이름에는 숫자나 특수문자를 사용할 수 없습니다. 두 글자 이상 입력해주세요.'} +
+
+
+ + +
+
diff --git a/src/features/auth/ui/sign-up-name-input.tsx b/src/features/auth/ui/sign-up-name-input.tsx deleted file mode 100644 index 96e1ae6d..00000000 --- a/src/features/auth/ui/sign-up-name-input.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { BaseInput } from '@/shared/ui/input'; - -export default function SignupNameInput({ - name, - setName, - error, -}: { - name: string; - setName: (name: string) => void; - error: string; -}) { - return ( -
- setName(e.target.value)} - placeholder="최예림" - className={`w-full`} - color={error ? 'error' : 'default'} - /> -
- {error - ? '이름에는 숫자나 특수문자를 사용할 수 없습니다. 두 글자 이상 입력해주세요.' - : '신뢰 있는 매칭을 위해 실명을 사용해주세요. (예: 홍길동 )'} -
-
- ); -} From e4d6e3840bee3fb4cfe37617ef0d37125755b1ae Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 26 Jul 2025 10:30:03 +0900 Subject: [PATCH 53/58] =?UTF-8?q?fix:=20=EC=9D=B4=EB=A6=84=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 아무것도 입력하지 않았을 경우, 기본 input 스타일 적용 --- src/features/auth/ui/sign-up-modal.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/features/auth/ui/sign-up-modal.tsx b/src/features/auth/ui/sign-up-modal.tsx index 0a43171e..aa12b59a 100644 --- a/src/features/auth/ui/sign-up-modal.tsx +++ b/src/features/auth/ui/sign-up-modal.tsx @@ -121,10 +121,12 @@ export default function SignupModal({ onChange={(e) => setName(e.target.value)} placeholder="홍길동" className={`w-full`} - color={isValidName ? 'default' : 'error'} + color={ + isValidName || name.length === 0 ? 'default' : 'error' + } />
{isValidName ? '신뢰 있는 매칭을 위해 실명을 사용해주세요. (예: 홍길동 )' From 70df1e3142bc00eea4bc07aec5b9aad7e1c4d43a Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 26 Jul 2025 10:42:05 +0900 Subject: [PATCH 54/58] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=A6=84=20Input?= =?UTF-8?q?=EC=9D=98=20=EC=9E=85=EB=A0=A5=EA=B0=92=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/auth/ui/sign-up-modal.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/features/auth/ui/sign-up-modal.tsx b/src/features/auth/ui/sign-up-modal.tsx index aa12b59a..cd7a358f 100644 --- a/src/features/auth/ui/sign-up-modal.tsx +++ b/src/features/auth/ui/sign-up-modal.tsx @@ -118,17 +118,25 @@ export default function SignupModal({ setName(e.target.value)} + onChange={(e) => { + const value = e.target.value; + const filteredValue = value + .replace(/[^a-zA-Zㄱ-힣]/g, '') + .slice(0, 10) + .trim(); + setName(filteredValue); + }} placeholder="홍길동" className={`w-full`} color={ isValidName || name.length === 0 ? 'default' : 'error' } + maxLength={10} />
- {isValidName + {isValidName || name.length === 0 ? '신뢰 있는 매칭을 위해 실명을 사용해주세요. (예: 홍길동 )' : '이름에는 숫자나 특수문자를 사용할 수 없습니다. 두 글자 이상 입력해주세요.'}
From b44c6c38ca8974070edf749155c64492ddc9fc23 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 26 Jul 2025 10:48:05 +0900 Subject: [PATCH 55/58] =?UTF-8?q?feat:=20=EA=B0=9C=EC=9D=B8=20=EC=95=BD?= =?UTF-8?q?=EA=B4=80=20=EB=8F=99=EC=9D=98=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=9C=BC=EB=A9=B4=20=EA=B0=80=EC=9E=85=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/auth/ui/sign-up-modal.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/features/auth/ui/sign-up-modal.tsx b/src/features/auth/ui/sign-up-modal.tsx index cd7a358f..e2b8e15b 100644 --- a/src/features/auth/ui/sign-up-modal.tsx +++ b/src/features/auth/ui/sign-up-modal.tsx @@ -20,6 +20,7 @@ export default function SignupModal({ onClose: () => void; }) { const [name, setName] = useState(''); + const [checked, setChecked] = useState(false); const [image, setImage] = useState( getCookie('socialImageURL') || 'profile-default.svg', ); @@ -142,7 +143,12 @@ export default function SignupModal({
- + setChecked(e.target.checked)} + />