diff --git a/src/api/auth.api.ts b/src/api/auth.api.ts index 223ddd61..44322c56 100644 --- a/src/api/auth.api.ts +++ b/src/api/auth.api.ts @@ -1,4 +1,4 @@ -import { VerifyEmail, VerifyNickname } from '../models/auth'; +import { ApiVerifyNickname, VerifyEmail } from '../models/auth'; import { httpClient } from './http.api'; import { registerFormValues } from '../pages/register/Register'; import { changePasswordFormValues } from '../pages/changePassword/ChangePassword'; @@ -24,16 +24,16 @@ export const postVerifyEmailCode = async (data: VerifyEmail) => { } }; -export const postCheckNickname = async (nickname: string) => { +export const getCheckNickname = async (nickname: string) => { try { - const response = await httpClient.post( + const response = await httpClient.get( '/user/nickname-check', - { nickname } + { params: { nickname } } ); - return response; + + return response.data.message; } catch (error) { console.error('checkNickname:', error); - throw error; } }; diff --git a/src/api/mypage.api.ts b/src/api/mypage.api.ts index 2ce7237f..1178f506 100644 --- a/src/api/mypage.api.ts +++ b/src/api/mypage.api.ts @@ -1,26 +1,25 @@ -import { EditMyInfo, UserInfo } from '../models/userInfo'; -import { - MyAppliedProjectStatusList, - MyJoinedProjectList, -} from '../models/userProject'; +import { ApiUserInfo, ApiUserInfoImg, EditMyInfo } from '../models/userInfo'; +import { ApiAppliedProject, ApiJoinedProject } from '../models/userProject'; import { httpClient } from './http.api'; export const getMyInfo = async () => { try { - const response = await httpClient.get('/user'); + const response = await httpClient.get('/user'); + return response.data; } catch (error) { - console.error('mypage-myinfo:', error); + console.error('내 정보 조회: ', error); throw error; } }; export const putMyInfo = async (data: EditMyInfo) => { try { - const response = await httpClient.put('/user/me', data); - return response; + const response = await httpClient.put('/user', data); + + return response.data.data; } catch (error) { - console.error('mypage-myinfoedit:', error); + console.error('내 정보 수정: ', error); throw error; } }; @@ -30,38 +29,43 @@ export const patchMyProfileImg = async (file: File) => { formData.append('file', file); try { - const response = await httpClient.patch('/user/me/profile-img', formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); - return response; + const response = await httpClient.patch( + '/user/profile-img', + formData, + { + headers: { + 'Content-Type': 'multipart/form-data', + }, + } + ); + + return response.data.data; } catch (error) { - console.error('myprofile upload:', error); + console.error('프로필 이미지 업데이트: ', error); throw error; } }; export const getMyJoinedProjectList = async () => { try { - const response = await httpClient.get( - '/user/me/project' - ); + const response = await httpClient.get('/user/project'); + return response.data; } catch (error) { - console.error('myJoinedProjectList:', error); + console.error('내 프로젝트 리스트: ', error); throw error; } }; export const getMyAppliedStatusList = async () => { try { - const response = await httpClient.get( - '/user/me/applications' + const response = await httpClient.get( + '/user/applications' ); + return response.data; } catch (error) { - console.error('myAppliedProjectList:', error); + console.error('내가 지원한 프로젝트 리스트: ', error); throw error; } }; diff --git a/src/api/projectLists.api.ts b/src/api/projectLists.api.ts index 086f7679..0aa94325 100644 --- a/src/api/projectLists.api.ts +++ b/src/api/projectLists.api.ts @@ -1,8 +1,6 @@ import type { ApiProjectLists, ApiProjectStatistic, - ProjectLists, - ProjectStatistic, } from '../models/mainProjectLists'; import { SearchFilters } from '../models/SearchFilters'; import { httpClient } from './http.api'; diff --git a/src/api/userpage.api.ts b/src/api/userpage.api.ts index 3ada03c0..a7cbf887 100644 --- a/src/api/userpage.api.ts +++ b/src/api/userpage.api.ts @@ -1,25 +1,27 @@ -import { UserInfo } from '../models/userInfo'; -import { UserJoinedProjectList } from '../models/userProject'; +import type { ApiUserInfo } from '../models/userInfo'; +import type { ApiSelectUserProject } from '../models/userProject'; import { httpClient } from './http.api'; export const getUserInfo = async (id: number) => { try { - const response = await httpClient.get(`/user/${id}`); + const response = await httpClient.get(`/user/${id}`); + return response.data; } catch (error) { - console.error('userpage-userinfo:', error); + console.error('다른 유저 정보 조회: ', error); throw error; } }; export const getUserJoinedProjectList = async (id: number) => { try { - const response = await httpClient.get( + const response = await httpClient.get( `/user/${id}/project` ); + return response.data; } catch (error) { - console.error('userJoinedProjectList:', error); + console.error('다른 유저 참여 프로젝트 조회: ', error); throw error; } }; diff --git a/src/components/auth/InputText.styled.ts b/src/components/auth/InputText.styled.ts index 782d635b..b4eea76b 100644 --- a/src/components/auth/InputText.styled.ts +++ b/src/components/auth/InputText.styled.ts @@ -30,7 +30,7 @@ export const Input = styled.input` border: none; outline: none; font-size: 0.9em; - width: 85%; + width: 100%; color: ${({ theme }) => theme.color.primary} &::placeholder { diff --git a/src/components/common/sidebar/Sidebar.styled.ts b/src/components/common/sidebar/Sidebar.styled.ts index 01d6c88f..fe188e68 100644 --- a/src/components/common/sidebar/Sidebar.styled.ts +++ b/src/components/common/sidebar/Sidebar.styled.ts @@ -23,7 +23,7 @@ export const LogoContainer = styled.div` } `; -export const AvartarContainer = styled.div` +export const AvatarContainer = styled.div` width: 100%; display: flex; flex-direction: column; @@ -39,7 +39,7 @@ export const AvartarContainer = styled.div` } `; -export const AvartarWrapper = styled.div` +export const AvatarWrapper = styled.div` position: relative; `; export const MenuList = styled.div` diff --git a/src/components/common/sidebar/Sidebar.tsx b/src/components/common/sidebar/Sidebar.tsx index a9f779ee..6a42d0e9 100644 --- a/src/components/common/sidebar/Sidebar.tsx +++ b/src/components/common/sidebar/Sidebar.tsx @@ -26,7 +26,6 @@ const Sidebar = ({ menuItems, profileImage, nickname }: SidebarProps) => { const isManagePage = location.pathname.includes('/manage'); const isMyProfile = isLoggedIn && !isUserPage && !isManagePage; - const getActiveIndex = useCallback(() => { const currentPath = location.pathname; return menuItems.findIndex((item) => currentPath === item.path) ?? 0; @@ -34,8 +33,8 @@ const Sidebar = ({ menuItems, profileImage, nickname }: SidebarProps) => { return ( - - + + {profileImage === MainLogo ? ( main logo @@ -44,9 +43,9 @@ const Sidebar = ({ menuItems, profileImage, nickname }: SidebarProps) => { )} {isMyProfile && } - + {nickname ? nickname : ''} - + {menuItems.map(({ label, path, icon, isDone = false }, index) => { return ( diff --git a/src/components/home/projectCardLists/cardList/CardList.tsx b/src/components/home/projectCardLists/cardList/CardList.tsx index d0a18053..28d81577 100644 --- a/src/components/home/projectCardLists/cardList/CardList.tsx +++ b/src/components/home/projectCardLists/cardList/CardList.tsx @@ -11,8 +11,6 @@ interface CardListProps { } export default function CardList({ list }: CardListProps) { - console.log('리스트*-*-*-*-*-*-*', list); - const listPositionTag = list.positions.slice(0, 2); const listSkillTag = list.skills.slice(0, 4); const othersPosition = list.positions.length - 2; diff --git a/src/components/mypage/appliedProject/MyApplyProject.tsx b/src/components/mypage/appliedProject/MyApplyProject.tsx index 7c9fe33c..2c7fff21 100644 --- a/src/components/mypage/appliedProject/MyApplyProject.tsx +++ b/src/components/mypage/appliedProject/MyApplyProject.tsx @@ -21,9 +21,9 @@ const MyApplyProject = () => { {myAppliedStatusListData && myAppliedStatusListData?.length > 0 ? ( - {myAppliedStatusListData?.map((status) => ( - - + {myAppliedStatusListData?.map((data) => ( + + ))} diff --git a/src/components/mypage/appliedProject/MyStatus.tsx b/src/components/mypage/appliedProject/MyStatus.tsx index 8991e73f..d14afcf1 100644 --- a/src/components/mypage/appliedProject/MyStatus.tsx +++ b/src/components/mypage/appliedProject/MyStatus.tsx @@ -1,17 +1,17 @@ import { MY_STATUS } from '../../../constants/authConstants'; -import { MyAppliedProjectStatus } from '../../../models/userProject'; +import type { AppliedProject } from '../../../models/userProject'; import * as S from './MyStatus.styled'; interface StatusProps { - status: MyAppliedProjectStatus; + list: AppliedProject; } -const MyStatus = ({ status }: StatusProps) => { +const MyStatus = ({ list }: StatusProps) => { return ( - {status.projectTitle} - - {status.status} + {list.title} + + {list.status} ); diff --git a/src/components/mypage/joinedProject/MyJoinProjects.tsx b/src/components/mypage/joinedProject/MyJoinProjects.tsx index 27723bff..973eb03b 100644 --- a/src/components/mypage/joinedProject/MyJoinProjects.tsx +++ b/src/components/mypage/joinedProject/MyJoinProjects.tsx @@ -13,6 +13,7 @@ const MyJoinProjects = () => { if (isLoading) { return ; } + if (!myJoinedProjectListData) return; return ( <> @@ -22,11 +23,8 @@ const MyJoinProjects = () => { {myJoinedProjectListData && myJoinedProjectListData?.length > 0 ? ( {myJoinedProjectListData?.map((project) => ( - - + + ))} diff --git a/src/components/mypage/joinedProject/Project.tsx b/src/components/mypage/joinedProject/Project.tsx index 09d0cd12..336a7e22 100644 --- a/src/components/mypage/joinedProject/Project.tsx +++ b/src/components/mypage/joinedProject/Project.tsx @@ -1,25 +1,23 @@ -import { UserJoinedProject } from '../../../models/userProject'; +import type { JoinedProject } from '../../../models/userProject'; import * as S from './Project.styled'; import BeginnerIcon from '../../../assets/beginner.svg'; -import { formatDate } from '../../../util/formatDate'; import { EllipsisHorizontalIcon } from '@heroicons/react/24/outline'; interface ProjectProps { - project: UserJoinedProject; + project: JoinedProject; } const Project = ({ project }: ProjectProps) => { - const startProjectDate = formatDate(project.startDate); const maxSkills = 4; - const skillsShow = project.ProjectSkillTag.slice(0, maxSkills); + const skillsShow = project.skills.slice(0, maxSkills); return ( {project.title} - 프로젝트 시작 + 시작일 - {startProjectDate} + {project.recruitmentEndDate} @@ -36,9 +34,9 @@ const Project = ({ project }: ProjectProps) => { {skillsShow.map((skill) => ( - + ))} - {project.ProjectSkillTag.length > maxSkills && ( + {project.skills.length > maxSkills && ( )} diff --git a/src/components/mypage/myProfile/MyProfile.styled.ts b/src/components/mypage/myProfile/MyProfile.styled.ts index c704d3d9..497d2f5d 100644 --- a/src/components/mypage/myProfile/MyProfile.styled.ts +++ b/src/components/mypage/myProfile/MyProfile.styled.ts @@ -28,12 +28,18 @@ export const Container = styled.div` export const BackgroundWrapper = styled.div` background-color: #fff; display: flex; - padding: 0.5rem 1rem; + padding: 0.5rem 1.3rem; border-radius: 15px; - span { - word-break: break-all; - overflow-wrap: break-word; + div { + width: 100%; + display: flex; + gap: 13px; + span { + width: content; + word-break: break-all; + overflow-wrap: break-word; + } @media ${({ theme }) => theme.mediaQuery.tablet} { font-size: ${({ theme }) => theme.heading['semiSmall'].tabletFontSize}; @@ -62,6 +68,7 @@ export const BackgroundBox = styled.div` export const ProfileSection = styled.div` display: flex; flex-direction: column; + gap: 1.25rem; button { position: absolute; @@ -94,14 +101,30 @@ export const ProfileSection = styled.div` } `; +export const NicknameBackgroundBox = styled.div` + background-color: #fff; + display: flex; + padding: 1rem 1.3rem; + gap: 1rem; + border-radius: 15px; + + @media ${({ theme }) => theme.mediaQuery.tablet} { + padding: 1.2rem; + font-size: ${({ theme }) => theme.heading['semiSmall'].tabletFontSize}; + } +`; + +export const NicknameSpan = styled.span` + display: flex; + align-items: center; +`; + export const IconWrapper = styled.div` width: fit-content; height: fit-content; display: flex; justify-content: center; align-items: center; - margin-left: 0.5rem; - margin-top: -0.1rem; background-color: ${({ theme }) => theme.color.white}; padding: 0.2rem; border-radius: 50%; @@ -118,7 +141,6 @@ export const Wrapper = styled.div` flex-wrap: wrap; gap: 1rem; align-items: center; - margin-bottom: 1.25rem; label { font-weight: 700; @@ -165,7 +187,6 @@ export const List = styled.div` display: flex; gap: 1rem; flex-direction: column; - margin-bottom: 1.25rem; label { font-weight: 700; @@ -178,10 +199,10 @@ export const List = styled.div` ul { display: flex; flex-direction: column; + gap: 10px; li { color: #a1a1a1; - margin-bottom: 0.5rem; span { color: ${({ theme }) => theme.color.primary}; @@ -190,13 +211,17 @@ export const List = styled.div` } `; +export const Form = styled.form` + display: flex; + gap: 3rem; +`; + export const EditWrapper = styled.div` display: flex; flex-wrap: wrap; gap: 1rem; width: 100%; align-items: center; - margin-bottom: 3rem; label { font-weight: 700; @@ -224,8 +249,12 @@ export const InputTextNickname = styled.div` min-width: 125px; `; +export const InputBeginner = styled.input` + accent-color: ${({ theme }) => theme.color.navy}; +`; + export const InputTextGithub = styled.div` - width: 90%; + width: 100%; @media ${({ theme }) => theme.mediaQuery.tablet} { width: 100%; @@ -242,6 +271,10 @@ export const InputWrapper = styled.div` position: relative; width: 85%; + input { + width: 100%; + } + @media ${({ theme }) => theme.mediaQuery.tablet} { width: 100%; } @@ -299,7 +332,7 @@ export const EditList = styled.div` gap: 1rem; width: 100%; position: relative; - margin-bottom: 3rem; + background-color: ${({ theme }) => theme.color.white}; padding: 1rem; border-radius: 20px; @@ -342,3 +375,7 @@ export const CareerWrapper = styled.div` flex: auto; } `; + +export const CareerAddButton = styled.button` + /* display: flex; */ +`; diff --git a/src/components/mypage/myProfile/MyProfile.tsx b/src/components/mypage/myProfile/MyProfile.tsx index ade85e91..2f68a9ff 100644 --- a/src/components/mypage/myProfile/MyProfile.tsx +++ b/src/components/mypage/myProfile/MyProfile.tsx @@ -37,14 +37,12 @@ const profileSchema = z.object({ /^[a-zA-Z가-힣ㄱ-ㅎㅏ-ㅣ0-9~`!@#$%^&*()\-_=+]{1,6}$/, ERROR_MESSAGES.NICKNAME_FORMAT ), + beginner: z.boolean(), skillTagIds: z.array(z.number()).min(1, ERROR_MESSAGES.SKILL_REQUIRED), - positionTagId: z.number().refine((id) => id > 0, { - message: ERROR_MESSAGES.POSITION_REQUIRED, - }), + positionTagIds: z.array(z.number()).min(1, ERROR_MESSAGES.POSITION_REQUIRED), github: z .string() .optional() - .optional() .refine( (val) => !val || /^https?:\/\/[^\s$.?#].[^\s]*$/.test(val), ERROR_MESSAGES.GITHUB_SPECIAL @@ -75,8 +73,9 @@ type ProfileFormData = z.infer; const MyProfile = () => { const [isEditing, setIsEditing] = useState(false); + const [nickname, setNickname] = useState(''); const { skillTagsData, positionTagsData } = useSearchFilteringSkillTag(); - const { nicknameMessage, handleNickNameChange, handleNickname } = + const { nicknameMessage, handleDuplicationNickname } = useNickNameVerification(); const { myData, isLoading } = useMyProfileInfo(); @@ -92,8 +91,9 @@ const MyProfile = () => { resolver: zodResolver(profileSchema), defaultValues: { nickname: '', + beginner: false, skillTagIds: [], - positionTagId: 0, + positionTagIds: [], github: '', career: [], bio: '', @@ -105,17 +105,24 @@ const MyProfile = () => { if (myData) { const skillTagIds = myData.skills .map( - (skill) => - skillTagsData.find((tag) => tag.name === skill.skillName)?.id + (skill) => skillTagsData.find((tag) => tag.name === skill.name)?.id + ) + .filter((id): id is number => id !== undefined); + + const positionTagIds = myData.positions + .map( + (position) => + positionTagsData.find((tag) => tag.id === position.id)?.id ) .filter((id): id is number => id !== undefined); reset({ nickname: myData.nickname, bio: myData.bio || '', - positionTagId: myData.positionTag?.id || 0, + beginner: myData.beginner, + positionTagIds, github: myData.github || '', - skillTagIds: skillTagIds, + skillTagIds, career: myData.career?.length ? myData.career.map((item) => ({ name: item.name, @@ -126,14 +133,10 @@ const MyProfile = () => { : [{ name: '', periodStart: '', periodEnd: '', role: '' }], }); } - }, [myData, skillTagsData, reset]); + }, [myData, skillTagsData, positionTagsData, reset]); const { fields, append, remove } = useFieldArray({ control, name: 'career' }); - const handleCheckNickName = (nickname: string) => { - handleNickname(nickname); - }; - const onSubmit = (data: ProfileFormData, e?: React.BaseSyntheticEvent) => { e?.preventDefault(); @@ -159,35 +162,33 @@ const MyProfile = () => { - - {myData.nickname} - - {myData.userLevel === 'Beginner' ? ( + + {myData.nickname} + {Boolean(myData.beginner) && ( + beginner - ) : ( - '' - )} - - + + )} +
    {myData.skills.map((skill) => ( -
  • +
  • {skill.skillName} - {skill.skillName} + {skill.name}
  • ))}
@@ -196,13 +197,17 @@ const MyProfile = () => { - {myData.positionTag?.name} +
+ {myData.positions.sort().map((position) => ( + {position.name} + ))} +
- {myData.github} + {myData.github || '-'} @@ -210,7 +215,7 @@ const MyProfile = () => {
    {myData.career?.map((career) => ( -
  • +
  • {career.name} ( {career.periodStart.slice(0, 10)} ~{' '} {career.periodEnd.slice(0, 10)}{' '} @@ -232,7 +237,7 @@ const MyProfile = () => { ) : ( -
    + {/* 닉네임 */} @@ -250,14 +255,14 @@ const MyProfile = () => { onChange={(e) => { const value = e.target.value; field.onChange(e); - handleNickNameChange(value); + setNickname(value); }} /> {errors.nickname && ( {errors.nickname.message} )} - {!errors.nickname && nicknameMessage && ( + {!errors.nickname && ( {nicknameMessage} )} + ))} @@ -526,7 +558,7 @@ const MyProfile = () => { 취소 - +
    )} diff --git a/src/components/mypage/myProfile/NoMyInfo.styled.ts b/src/components/mypage/myProfile/NoMyInfo.styled.ts index 175659a8..e57cf521 100644 --- a/src/components/mypage/myProfile/NoMyInfo.styled.ts +++ b/src/components/mypage/myProfile/NoMyInfo.styled.ts @@ -12,10 +12,9 @@ export const Container = styled.div` height: 40px; color: #ccc; } +`; - span { - font-size: 1.1rem; - color: $ccc; - margin-top: 1rem; - } +export const Span = styled.span` + font-size: 1.1rem; + color: #ccc; `; diff --git a/src/components/mypage/myProfile/NoMyInfo.tsx b/src/components/mypage/myProfile/NoMyInfo.tsx index bd0e0a9b..e11d9ece 100644 --- a/src/components/mypage/myProfile/NoMyInfo.tsx +++ b/src/components/mypage/myProfile/NoMyInfo.tsx @@ -5,7 +5,7 @@ const NoMyInfo = () => { return ( - 유저 정보를 불러올 수 없습니다. + 유저 정보를 불러올 수 없습니다. ); }; diff --git a/src/components/userPage/joinedProject/UserJoinProject.tsx b/src/components/userPage/joinedProject/UserJoinProject.tsx index 7f640350..ba46642c 100644 --- a/src/components/userPage/joinedProject/UserJoinProject.tsx +++ b/src/components/userPage/joinedProject/UserJoinProject.tsx @@ -17,6 +17,8 @@ const UserJoinProject = () => { return ; } + console.log('userJoinedProjectListData', userJoinedProjectListData); + return ( @@ -28,10 +30,10 @@ const UserJoinProject = () => { {userJoinedProjectListData?.acceptedProjects?.map((project) => ( - + ))} @@ -50,10 +52,10 @@ const UserJoinProject = () => { {userJoinedProjectListData?.ownProjects?.map((project) => ( - + ))} diff --git a/src/components/userPage/userProfile/UserProfile.tsx b/src/components/userPage/userProfile/UserProfile.tsx index 9f921880..a6c19bc8 100644 --- a/src/components/userPage/userProfile/UserProfile.tsx +++ b/src/components/userPage/userProfile/UserProfile.tsx @@ -38,14 +38,14 @@ const UserProfile = () => {
      {userData?.skills.map((skill) => ( -
    • +
    • {skill.skillName} - {skill.skillName} + {skill.name}
    • ))}
    @@ -54,7 +54,9 @@ const UserProfile = () => { - {userData?.positionTag?.name} + {userData?.positions.map((position) => ( + {position.name} + ))} diff --git a/src/constants/authConstants.ts b/src/constants/authConstants.ts index 96ed9fff..8e5b0362 100644 --- a/src/constants/authConstants.ts +++ b/src/constants/authConstants.ts @@ -12,7 +12,7 @@ export const ERROR_MESSAGES = { NICKNAME_FORMAT: '특수문자는 (~,`,!,@,#,$,%,^,&,*,(,),-,_,+)만 사용할 수 있습니다.', SKILL_REQUIRED: '스킬을 최소 1개 선택해주세요.', - POSITION_REQUIRED: '주요 포지션을 선택해주세요.', + POSITION_REQUIRED: '주요 포지션을 최소 1개 선택해주세요.', GITHUB_SPECIAL: '유효한 깃허브 링크를 입력해주세요.', CAREERNAME_REQUIRED: '회사나 참여한 프로젝트 이름을 입력해주세요.', STARTPERIOD_REQUIRED: '시작 날짜를 입력해주세요.', diff --git a/src/hooks/useMyInfo.ts b/src/hooks/useMyInfo.ts index bc2f0584..1b920c73 100644 --- a/src/hooks/useMyInfo.ts +++ b/src/hooks/useMyInfo.ts @@ -6,29 +6,29 @@ import { patchMyProfileImg, putMyInfo, } from '../api/mypage.api'; -import { EditMyInfo, UserInfo } from '../models/userInfo'; +import type { ApiUserInfo, EditMyInfo } from '../models/userInfo'; import { AxiosError } from 'axios'; import { useNavigate } from 'react-router-dom'; import { ROUTES } from '../constants/routes'; import { myInfoKey, ProjectListKey } from './queries/keys'; import useAuthStore from '../store/authStore'; -import { - MyAppliedProjectStatusList, - MyJoinedProjectList, +import type { + ApiAppliedProject, + ApiJoinedProject, } from '../models/userProject'; import { MODAL_MESSAGE } from '../constants/modalMessage'; export const useMyProfileInfo = () => { const isLoggedIn = useAuthStore((state) => state.isLoggedIn); - const { data, isLoading } = useQuery({ + const { data, isLoading } = useQuery({ queryKey: myInfoKey.myProfile, queryFn: () => getMyInfo(), staleTime: 1 * 60 * 1000, enabled: isLoggedIn, }); - return { myData: data, isLoading }; + return { myData: data?.data, isLoading }; }; export const useEditMyProfileInfo = ( @@ -86,23 +86,23 @@ export const useUploadProfileImg = ( export const useMyJoinedProjectList = () => { const isLoggedIn = useAuthStore((state) => state.isLoggedIn); - const { data, isLoading } = useQuery({ + const { data, isLoading } = useQuery({ queryKey: ProjectListKey.myJoinedList, queryFn: () => getMyJoinedProjectList(), enabled: isLoggedIn, }); - return { myJoinedProjectListData: data, isLoading }; + return { myJoinedProjectListData: data?.data, isLoading }; }; export const useMyAppliedStatusList = () => { const isLoggedIn = useAuthStore((state) => state.isLoggedIn); - const { data, isLoading } = useQuery({ + const { data, isLoading } = useQuery({ queryKey: ProjectListKey.myAppliedStatusList, queryFn: () => getMyAppliedStatusList(), enabled: isLoggedIn, }); - return { myAppliedStatusListData: data, isLoading }; + return { myAppliedStatusListData: data?.data, isLoading }; }; diff --git a/src/hooks/useNicknameVerification.ts b/src/hooks/useNicknameVerification.ts index 24287844..6428a1d2 100644 --- a/src/hooks/useNicknameVerification.ts +++ b/src/hooks/useNicknameVerification.ts @@ -1,46 +1,17 @@ -import { useMutation } from '@tanstack/react-query'; import { useState } from 'react'; -import { postCheckNickname } from '../api/auth.api'; -import { AxiosError } from 'axios'; +import { getCheckNickname } from '../api/auth.api'; const useNickNameVerification = () => { - const [nicknameMessage, setNicknameMessage] = useState(null); + const [nicknameMessage, setNicknameMessage] = useState( + undefined + ); - const verifyNicknameMutation = useMutation({ - mutationFn: async (nickname) => { - await postCheckNickname(nickname); - }, - onSuccess: () => { - setNicknameMessage('사용 가능한 닉네임입니다.'); - }, - onError: (error: AxiosError) => { - const status = error?.response?.status; - - if (status === 400) { - setNicknameMessage('이미 사용 중인 닉네임입니다.'); - } else { - setNicknameMessage('유효한 닉네임을 입력해주세요.'); - } - }, - }); - - const handleNickNameChange = (nickname: string) => { - setNicknameMessage(nickname ? '' : null); + const handleDuplicationNickname = async (nickname: string) => { + const result = await getCheckNickname(nickname); + setNicknameMessage(result); }; - const handleNickname = async (nickname: string) => { - setNicknameMessage(null); - if (!nickname) { - return; - } - verifyNicknameMutation.mutate(nickname); - }; - - return { - nicknameMessage, - handleNickNameChange, - handleNickname, - }; + return { nicknameMessage, handleDuplicationNickname }; }; export default useNickNameVerification; diff --git a/src/hooks/useUserInfo.ts b/src/hooks/useUserInfo.ts index 7d5919f6..b2da4100 100644 --- a/src/hooks/useUserInfo.ts +++ b/src/hooks/useUserInfo.ts @@ -1,32 +1,32 @@ import { useQuery } from '@tanstack/react-query'; -import { UserInfo } from '../models/userInfo'; +import type { ApiUserInfo } from '../models/userInfo'; import { userInfoKey } from './queries/keys'; import { getUserInfo, getUserJoinedProjectList } from '../api/userpage.api'; import useAuthStore from '../store/authStore'; -import { UserJoinedProjectList } from '../models/userProject'; +import type { ApiSelectUserProject } from '../models/userProject'; export const useUserProfileInfo = (id: number) => { const isLoggedIn = useAuthStore((state) => state.isLoggedIn); - const { data, isLoading } = useQuery({ + const { data, isLoading } = useQuery({ queryKey: [userInfoKey.userProfile, id], queryFn: () => getUserInfo(id), staleTime: 1 * 60 * 1000, enabled: isLoggedIn, }); - return { userData: data, isLoading }; + return { userData: data?.data, isLoading }; }; export const useUserJoinedProjectList = (id: number) => { const isLoggedIn = useAuthStore((state) => state.isLoggedIn); - const { data, isLoading } = useQuery({ + const { data, isLoading } = useQuery({ queryKey: [userInfoKey.userJoinedList, id], queryFn: () => getUserJoinedProjectList(id), staleTime: 1 * 60 * 1000, enabled: isLoggedIn, }); - return { userJoinedProjectListData: data, isLoading }; + return { userJoinedProjectListData: data?.data, isLoading }; }; diff --git a/src/models/auth.ts b/src/models/auth.ts index 47958dec..a0a12380 100644 --- a/src/models/auth.ts +++ b/src/models/auth.ts @@ -1,15 +1,15 @@ //model import { UserData } from '../store/authStore'; +import { ApiCommonType } from './apiCommon'; export interface VerifyEmail { email: string; code: string; } -export interface VerifyNickname { - nickname: string; - message: string; +export interface ApiVerifyNickname extends ApiCommonType { + data: null; } export interface LoginResponse { diff --git a/src/models/mainProjectLists.ts b/src/models/mainProjectLists.ts index 8d0ebb01..e8216d96 100644 --- a/src/models/mainProjectLists.ts +++ b/src/models/mainProjectLists.ts @@ -33,7 +33,7 @@ export interface ProjectLists { } export interface ApiProjectLists extends ApiCommonType { - data: ProjectLists; + data: ProjectLists | null; } export interface ProjectStatistic { @@ -43,5 +43,5 @@ export interface ProjectStatistic { } export interface ApiProjectStatistic extends ApiCommonType { - data: ProjectStatistic; + data: ProjectStatistic | null; } diff --git a/src/models/projectDetail.ts b/src/models/projectDetail.ts index 427aecc7..3f33f628 100644 --- a/src/models/projectDetail.ts +++ b/src/models/projectDetail.ts @@ -1,4 +1,5 @@ import { joinProject } from './joinProject'; +import type { PositionTag, SkillTag } from './tags'; export interface User { id: number; @@ -6,25 +7,12 @@ export interface User { img: string; } -export interface SkillTag { - id: number; - name: string; - img: string; - createdAt: string; -} - export interface ProjectSkillTagList { SkillTag: SkillTag; projectId: number; skillTagId: number; } -export interface PositionTag { - id: number; - name: string; - createdAt: string; -} - export interface ProjectPositionTag { projectId: number; positionTagId: number; diff --git a/src/models/tags.ts b/src/models/tags.ts index 3b19eba0..bb7922b2 100644 --- a/src/models/tags.ts +++ b/src/models/tags.ts @@ -20,13 +20,13 @@ export interface MethodTag { } export interface ApiSkillTag extends ApiCommonType { - data: SkillTag[]; + data: SkillTag[] | null; } export interface ApiPositionTag extends ApiCommonType { - data: PositionTag[]; + data: PositionTag[] | null; } export interface ApiMethodTag extends ApiCommonType { - data: MethodTag[]; + data: MethodTag[] | null; } diff --git a/src/models/userInfo.ts b/src/models/userInfo.ts index 3e617f03..242c09fb 100644 --- a/src/models/userInfo.ts +++ b/src/models/userInfo.ts @@ -1,3 +1,6 @@ +import type { ApiCommonType } from './apiCommon'; +import type { PositionTag, SkillTag } from './tags'; + export interface Career { name: string; periodStart: string; @@ -5,35 +8,34 @@ export interface Career { role: string; } -export interface Position { - id: number; - name: string; -} - -export interface Skill { - skillName: string; - skillImg: string; -} - export interface UserInfo { id: number; nickname: string; email: string; - bio: string | null; - profileImg: string | null; - userLevel: string; - github: string | null; - career: Career[] | null; - positionTag: Position | null; - skills: Skill[]; - createdAt: string; + bio?: string; + profileImg?: string; + beginner: boolean; + github?: string; + career?: Career[]; + positions: Omit[]; + skills: Omit[]; + createdAt?: string; +} + +export interface ApiUserInfo extends ApiCommonType { + data: UserInfo | null; } export interface EditMyInfo { nickname: string; - bio?: string | null; - github?: string | null; - positionTagId: number; + bio?: string; + github?: string; + beginner: boolean; + positionTagIds: number[]; skillTagIds: number[]; - career?: Career[] | null; + career?: Career[]; +} + +export interface ApiUserInfoImg extends ApiCommonType { + data: string; } diff --git a/src/models/userProject.ts b/src/models/userProject.ts index 9baee3e8..f843a084 100644 --- a/src/models/userProject.ts +++ b/src/models/userProject.ts @@ -1,54 +1,35 @@ -export interface SkillTag { - id: number; - name: string; - img: string; -} +import type { ApiCommonType } from './apiCommon'; +import type { SkillTag } from './tags'; -export interface PositionTag { +export interface JoinedProject { + title: string; id: number; - name: string; -} - -export interface ProjectSkillTagItem { - projectId: number; - skillTagId: number; - SkillTag: SkillTag; + isBeginner: boolean; + isDone: boolean; + recruitmentEndDate: string; + totalMember: number; + skills: Omit[]; } -export interface ProjectPositionTagItem { - projectId: number; - positionTagId: number; - PositionTag: PositionTag; +export interface ApiJoinedProject extends ApiCommonType { + data: JoinedProject[] | null; } -export interface UserJoinedProject { - projectId: number; +export interface AppliedProject { title: string; - description: string; - totalMember: number; - startDate: string; - estimatedPeriod: string; - methodId: number; - isBeginner: boolean; - isDone: boolean; - recruitmentStartDate: string; - recruitmentEndDate: string; status: string; - ProjectSkillTag: ProjectSkillTagItem[]; - ProjectPositionTag: ProjectPositionTagItem[]; + id: number; } -export type MyJoinedProjectList = UserJoinedProject[]; - -export interface UserJoinedProjectList { - acceptedProjects: UserJoinedProject[]; - ownProjects: UserJoinedProject[]; +export interface ApiAppliedProject extends ApiCommonType { + data: AppliedProject[] | null; } -export interface MyAppliedProjectStatus { - id: number; - projectTitle: string; - status: string; +export interface SelectUserProject { + acceptedProjects: JoinedProject[]; + ownProjects: AppliedProject[]; } -export type MyAppliedProjectStatusList = MyAppliedProjectStatus[]; +export interface ApiSelectUserProject extends ApiCommonType { + data: SelectUserProject; +} diff --git a/src/pages/register/Register.tsx b/src/pages/register/Register.tsx index 4d7da77f..4ac20e00 100644 --- a/src/pages/register/Register.tsx +++ b/src/pages/register/Register.tsx @@ -5,7 +5,7 @@ import Title from '../../components/common/title/Title'; import { z } from 'zod'; import { Controller, useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; -import React from 'react'; +import React, { useState } from 'react'; import InputText from '../../components/auth/InputText'; import { EnvelopeIcon, @@ -51,6 +51,7 @@ const registerSchema = z export type registerFormValues = z.infer; const Register = () => { + const [nicknameText, setNicknameText] = useState(''); const { isOpen, message, handleModalOpen, handleModalClose } = useModal(); const { userSignup } = useAuth(handleModalOpen); @@ -80,7 +81,7 @@ const Register = () => { handleEmailChange, } = useEmailVerification(); - const { nicknameMessage, handleNickNameChange, handleNickname } = + const { nicknameMessage, handleDuplicationNickname } = useNickNameVerification(); const handleClickEmail = (email: string) => { @@ -91,10 +92,6 @@ const Register = () => { handleVerifyCode(email, code); }; - const handleCheckNickname = (nickname: string) => { - handleNickname(nickname); - }; - const onSubmit = ( data: Pick, e?: React.BaseSyntheticEvent @@ -261,13 +258,12 @@ const Register = () => { onChange={(e) => { const value = e.target.value; field.onChange(e); - handleNickNameChange(value); + setNicknameText(value); }} /> - {errors.nickname && ( + {errors.nickname ? ( {errors.nickname.message} - )} - {!errors.nickname && nicknameMessage && ( + ) : ( {nicknameMessage} )} @@ -277,7 +273,7 @@ const Register = () => { radius='large' type='button' onClick={() => { - handleCheckNickname(field.value); + handleDuplicationNickname(text); }} > 중복 확인