From d8a9bba8e14a55b340fbb7fd222ec61a30b9c34b Mon Sep 17 00:00:00 2001 From: minchodang Date: Wed, 24 Apr 2024 16:21:19 +0900 Subject: [PATCH 1/4] =?UTF-8?q?Fix:=20=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=B0=8F=20=ED=8C=8C=ED=8B=B0=20=ED=98=84=ED=99=A9?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20api=20=ED=83=80=EC=9E=85=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EB=A7=A4=EB=84=88=20=EC=98=A8=EB=8F=84?= =?UTF-8?q?=20=EA=B3=84=EC=82=B0=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/getPartyJoin.ts | 26 ++-- src/components/profile/ProfileInfo.tsx | 170 +++++++++++----------- types/party/join/PartyJoinResponse.ts | 23 ++- types/profile/user/UserProfileResponse.ts | 28 ++-- 4 files changed, 131 insertions(+), 116 deletions(-) diff --git a/src/api/getPartyJoin.ts b/src/api/getPartyJoin.ts index 4ebce7b..24bab38 100644 --- a/src/api/getPartyJoin.ts +++ b/src/api/getPartyJoin.ts @@ -1,20 +1,22 @@ -import variableAssignMent from "@utils/variableAssignment"; -import defaultRequest from "src/lib/axios/defaultRequest"; -import { PartyJoinResponse } from "types/party/join/PartyJoinResponse"; +import variableAssignMent from '@utils/variableAssignment'; +import defaultRequest from 'src/lib/axios/defaultRequest'; +import { PartyJoinResponse } from 'types/party/join/PartyJoinResponse'; +export type GetPartyJoinRequestRole = 'HOST' | 'VOLUNTEER'; interface getPartyJoinParameter { - role: string; + page: number; + size: number; + sort: string; + role: GetPartyJoinRequestRole; } -export const API_GET_PARTY_JOIN_KEY = "/api/party/party-join?role={{role}}"; +export const API_GET_PARTY_JOIN_KEY = '/api/party/party-join?role={{role}}'; -const getPartyJoin = async ({ - role, -}: getPartyJoinParameter): Promise => { - const { data } = await defaultRequest.get( - variableAssignMent(API_GET_PARTY_JOIN_KEY, { role: role }) - ); - return data; +const getPartyJoin = async ({ role }: getPartyJoinParameter): Promise => { + const { data } = await defaultRequest.get( + variableAssignMent(API_GET_PARTY_JOIN_KEY, { role: role }), + ); + return data; }; export default getPartyJoin; diff --git a/src/components/profile/ProfileInfo.tsx b/src/components/profile/ProfileInfo.tsx index 29a170d..d324c2b 100644 --- a/src/components/profile/ProfileInfo.tsx +++ b/src/components/profile/ProfileInfo.tsx @@ -1,117 +1,117 @@ -import React from "react"; -import styled from "@emotion/styled"; -import Progressbar from "@components/common/ProgressBar"; -import Image from "next/image"; -import { DefaultText } from "@components/common/DefaultText"; -import GenderIcon from "@components/icons/profile/Gender.icon"; -import InfoIcon from "@components/icons/profile/Info.icon"; -import { useQuery, useSuspenseQuery } from "@tanstack/react-query"; -import { API_GET_PROFILE_KEY } from "src/api/getProfile"; -import getProfile from "src/api/getProfile"; -import { PARTY_GENDER_LABEL } from "src/constants/options"; -import { labelDataConvert } from "@utils/labelDataConvert"; +import React, { useMemo } from 'react'; +import styled from '@emotion/styled'; +import Progressbar from '@components/common/ProgressBar'; +import Image from 'next/image'; +import { DefaultText } from '@components/common/DefaultText'; +import GenderIcon from '@components/icons/profile/Gender.icon'; +import InfoIcon from '@components/icons/profile/Info.icon'; +import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; +import { API_GET_PROFILE_KEY } from 'src/api/getProfile'; +import getProfile from 'src/api/getProfile'; +import { PARTY_GENDER_LABEL } from 'src/constants/options'; +import { labelDataConvert } from '@utils/labelDataConvert'; const Container = styled.div` - display: flex; - width: 100%; - flex-direction: column; - padding-bottom: 20px; - background-color: white; + display: flex; + width: 100%; + flex-direction: column; + padding-bottom: 20px; + background-color: white; `; const ProfileImgContainer = styled.div` - width: 200px; - display: flex; - justify-content: center; - align-items: center; + width: 200px; + display: flex; + justify-content: center; + align-items: center; `; const ProfileDetailContainer = styled.div` - width: 200px; - display: flex; - width: 100%; - flex-direction: row; - padding: 20px 0; - z-index: 99; - background-color: white; + width: 200px; + display: flex; + width: 100%; + flex-direction: row; + padding: 20px 0; + z-index: 99; + background-color: white; `; const MannerDegreeContainer = styled.div` - display: flex; - flex-direction: row; - gap: 16px; - align-items: center; + display: flex; + flex-direction: row; + gap: 16px; + align-items: center; `; const UserInfo = styled.div` - display: flex; - flex-direction: row; - align-items: center; - gap: 8px; - margin-bottom: 8px; + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + margin-bottom: 8px; `; const Name = styled.div` - margin-bottom: 16px; + margin-bottom: 16px; `; const ProfileDetail = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - width: 60%; - padding: 20px; - gap: 8px; + display: flex; + flex-direction: column; + justify-content: center; + width: 60%; + padding: 20px; + gap: 8px; `; const userId = 11; // 로그인 기능 연결후 userid 받아올 예정 const ProfileInfo = () => { - const { data } = useSuspenseQuery({ - queryKey: [API_GET_PROFILE_KEY], - queryFn: () => getProfile(), - }); + const { data } = useSuspenseQuery({ + queryKey: [API_GET_PROFILE_KEY], + queryFn: getProfile, + }); - if (!data) { - return; - } + const { gender, age, nickname, imgUrl, negativeReviewCount, positiveReviewCount } = data; - const { gender, age, socialType, nickname, imgUrl } = data; + const mannerDegree = useMemo(() => { + const basicDegree = 36.5; + return basicDegree + (negativeReviewCount * -0.5 + positiveReviewCount * 0.5); + }, [negativeReviewCount, positiveReviewCount]); - return ( - - - - {"profile-image"} - - - - - - - - - - - - - {30}°C - {/* 매너온도는 후기기능에 포함되어 보류 */} - - - - - ); + return ( + + + + {'profile-image'} + + + + + + + + + + + + + {mannerDegree}°C + + + + + ); }; export default ProfileInfo; diff --git a/types/party/join/PartyJoinResponse.ts b/types/party/join/PartyJoinResponse.ts index 942101c..d0da8af 100644 --- a/types/party/join/PartyJoinResponse.ts +++ b/types/party/join/PartyJoinResponse.ts @@ -1,10 +1,17 @@ +import { UserGender } from 'types/profile/user/UserProfileResponse'; + +export type PartyAge = 'ALL' | 'TWENTY' | 'THIRTY' | 'FORTY'; + export interface PartyJoinResponse { - partyId: number; - partyTitle: string; - nickname: string; - imgUrl: string; - partyGender: string; - partyAge: string; - userGender: string; - userAge: number; + partyId: number; + partyTitle: string; + nickname: string; + imgUrl: string; + partyGender: string; + partyAge: PartyAge; + userGender: UserGender; + userAge: number; + createAt: string; + oneLineIntroduce: string; + typeMatch: boolean; } diff --git a/types/profile/user/UserProfileResponse.ts b/types/profile/user/UserProfileResponse.ts index b5f7d35..8a94758 100644 --- a/types/profile/user/UserProfileResponse.ts +++ b/types/profile/user/UserProfileResponse.ts @@ -1,13 +1,19 @@ +export type UserGender = 'ALL' | 'MALE' | 'FEMALE' | 'UNKNOWN'; + +export type OauthProvider = 'KAKAO' | 'NAVER'; +export type UserRole = 'GUEST' | 'USER' | 'VOLUNTEER' | 'HOST'; + export interface UserProfileResponse { - createDate: string; - modifiedDate: string; - id: number; - socialId: string; - socialType: string; - email: string; - nickname: string; - age: number; - imgUrl: string; - gender: string; - role: string; + userId: number; + socialId: string; + oauthProvider: string; + email: string; + nickname: string; + age: number; + imgUrl: string; + gender: UserGender; + role: UserRole; + rating: number; + positiveReviewCount: number; + negativeReviewCount: number; } From f60f3ec68d824dd524f5bf69bc44c5ca874cc456 Mon Sep 17 00:00:00 2001 From: minchodang Date: Wed, 24 Apr 2024 17:45:23 +0900 Subject: [PATCH 2/4] =?UTF-8?q?Fix:=20=EB=AC=B4=ED=95=9C=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EB=A6=AC=EC=8A=A4=ED=8F=B0=EC=8A=A4=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/getPartyJoin.ts | 14 +- src/components/home/HomeList.tsx | 2 +- .../profile/PartyRequestItemList.tsx | 38 ++++- src/components/search/SearchResult.tsx | 140 +++++++++--------- types/common/InfinitePaginationDataType.ts | 10 +- 5 files changed, 114 insertions(+), 90 deletions(-) diff --git a/src/api/getPartyJoin.ts b/src/api/getPartyJoin.ts index 24bab38..7269c90 100644 --- a/src/api/getPartyJoin.ts +++ b/src/api/getPartyJoin.ts @@ -10,12 +10,16 @@ interface getPartyJoinParameter { role: GetPartyJoinRequestRole; } -export const API_GET_PARTY_JOIN_KEY = '/api/party/party-join?role={{role}}'; +export const API_GET_PARTY_JOIN_KEY = '/api/party/party-join'; -const getPartyJoin = async ({ role }: getPartyJoinParameter): Promise => { - const { data } = await defaultRequest.get( - variableAssignMent(API_GET_PARTY_JOIN_KEY, { role: role }), - ); +const getPartyJoin = async (params: getPartyJoinParameter) => { + const { data } = await defaultRequest.get< + InfinitePaginationDataType<'partyList', PartyJoinResponse> + >(API_GET_PARTY_JOIN_KEY, { + params: { + ...params, + }, + }); return data; }; diff --git a/src/components/home/HomeList.tsx b/src/components/home/HomeList.tsx index e82afd7..c17a437 100644 --- a/src/components/home/HomeList.tsx +++ b/src/components/home/HomeList.tsx @@ -41,7 +41,7 @@ export const HomeList: FC = () => { }) : Promise.resolve(null), initialPageParam: 0, - getNextPageParam: (lastPage) => lastPage?.pageInfo?.lastPartyId, + getNextPageParam: (lastPage) => lastPage?.pageInfo?.page, }); const onClickPartyCard = (id: number) => { diff --git a/src/components/profile/PartyRequestItemList.tsx b/src/components/profile/PartyRequestItemList.tsx index 27ddef4..f2f92e5 100644 --- a/src/components/profile/PartyRequestItemList.tsx +++ b/src/components/profile/PartyRequestItemList.tsx @@ -1,4 +1,4 @@ -import { useSuspenseQuery } from '@tanstack/react-query'; +import { useSuspenseInfiniteQuery, useSuspenseQuery } from '@tanstack/react-query'; import { FC } from 'react'; import getPartyJoin, { API_GET_PARTY_JOIN_KEY } from 'src/api/getPartyJoin'; import { PartyRequestRole } from './PartyRequest'; @@ -6,6 +6,7 @@ import PartyRequestList from './PartyRequestCard'; import { DefaultText } from '@components/common/DefaultText'; import styled from '@emotion/styled'; import PartyRequestCard from './PartyRequestCard'; +import { ObserverTrigger } from '@components/hoc/ObserverTrigger'; interface PartyRequestItemListProps { role: PartyRequestRole; @@ -20,12 +21,23 @@ const Container = styled.div` `; const PartyRequestItemList: FC = ({ role }) => { - const requestList = useSuspenseQuery({ - queryKey: [API_GET_PARTY_JOIN_KEY, { role }], - queryFn: () => getPartyJoin({ role }), + const partyRequestList = useSuspenseInfiniteQuery({ + queryKey: [API_GET_PARTY_JOIN_KEY, , { role }], + queryFn: ({ pageParam = 0 }) => + getPartyJoin({ + page: pageParam, + role, + sort: '', + size: 5, + }), + initialPageParam: 0, + getNextPageParam: (lastPage) => lastPage?.pageInfo?.page, }); + const onObserve = () => { + if (partyRequestList.hasNextPage) partyRequestList.fetchNextPage(); + }; - if (!requestList.data.length) { + if (!partyRequestList.data.pages[0].partyList.length) { return ( @@ -33,9 +45,19 @@ const PartyRequestItemList: FC = ({ role }) => { ); } - return requestList.data.map((request) => ( - - )); + return ( + + {partyRequestList.data.pages.map((request) => + request.partyList.map((individualRequest) => ( + + )), + )} + + ); }; export default PartyRequestItemList; diff --git a/src/components/search/SearchResult.tsx b/src/components/search/SearchResult.tsx index 0a323ec..2c24db6 100644 --- a/src/components/search/SearchResult.tsx +++ b/src/components/search/SearchResult.tsx @@ -1,85 +1,83 @@ -import { DefaultText } from "@components/common/DefaultText"; -import NoResult from "@components/common/NoResult"; -import { ObserverTrigger } from "@components/hoc/ObserverTrigger"; -import { PartyCard } from "@components/home/PartyCard"; -import styled from "@emotion/styled"; -import { useSuspenseInfiniteQuery } from "@tanstack/react-query"; -import { useRouter } from "next/router"; -import { FC } from "react"; -import getSearchResult, { - API_GET_SEARCH_RESULT, -} from "src/api/getSearchResult"; -import { Color } from "styles/Color"; +import { DefaultText } from '@components/common/DefaultText'; +import NoResult from '@components/common/NoResult'; +import { ObserverTrigger } from '@components/hoc/ObserverTrigger'; +import { PartyCard } from '@components/home/PartyCard'; +import styled from '@emotion/styled'; +import { useSuspenseInfiniteQuery } from '@tanstack/react-query'; +import { useRouter } from 'next/router'; +import { FC } from 'react'; +import getSearchResult, { API_GET_SEARCH_RESULT } from 'src/api/getSearchResult'; +import { Color } from 'styles/Color'; interface SearchResultProps { - keyword: string; + keyword: string; } const Container = styled.div` - display: flex; - flex-direction: column; - width: 100%; - background-color: ${Color.VeryLightGrey}; - overflow-y: scroll; - overflow-x: hidden; - align-items: center; - padding: 0 15px 60px 15px; + display: flex; + flex-direction: column; + width: 100%; + background-color: ${Color.VeryLightGrey}; + overflow-y: scroll; + overflow-x: hidden; + align-items: center; + padding: 0 15px 60px 15px; `; export const SearchResult: FC = ({ keyword }) => { - const router = useRouter(); + const router = useRouter(); - const { fetchNextPage, hasNextPage, data } = useSuspenseInfiniteQuery({ - queryKey: [API_GET_SEARCH_RESULT, { keyword }], - queryFn: ({ pageParam = 0 }) => - getSearchResult({ - keyword, - lastPartyId: pageParam, - }), - initialPageParam: 0, - getNextPageParam: (lastPage) => lastPage?.pageInfo?.lastPartyId, - staleTime: 0, - }); + const { fetchNextPage, hasNextPage, data } = useSuspenseInfiniteQuery({ + queryKey: [API_GET_SEARCH_RESULT, { keyword }], + queryFn: ({ pageParam = 0 }) => + getSearchResult({ + keyword, + lastPartyId: pageParam, + }), + initialPageParam: 0, + getNextPageParam: (lastPage) => lastPage?.pageInfo?.page, + staleTime: 0, + }); - const onClickPartyCard = (id: number) => { - router.push(`/party/${id}`); - }; - const onObserve = () => { - if (hasNextPage) fetchNextPage(); - }; + const onClickPartyCard = (id: number) => { + router.push(`/party/${id}`); + }; + const onObserve = () => { + if (hasNextPage) fetchNextPage(); + }; - if (!data || !data.pages) { - return; - } + if (!data || !data.pages) { + return; + } - const partyData = data.pages; + const partyData = data.pages; - return ( - - {partyData[0]?.partyList.length > 0 ? ( - - {partyData.map((item) => - item?.partyList.map((party) => ( - - )) - )} - - ) : ( - - - - )} - - ); + return ( + + {partyData[0]?.partyList.length > 0 ? ( + + {partyData.map((item) => + item?.partyList.map((party) => ( + + )), + )} + + ) : ( + + + + )} + + ); }; diff --git a/types/common/InfinitePaginationDataType.ts b/types/common/InfinitePaginationDataType.ts index 5ae5dc7..0fc8b01 100644 --- a/types/common/InfinitePaginationDataType.ts +++ b/types/common/InfinitePaginationDataType.ts @@ -1,8 +1,8 @@ type InfinitePaginationDataType = { - [key in K]: T[]; + [key in K]: T[]; } & { - pageInfo: { - lastPartyId: number; - hasNext: boolean; - }; + pageInfo: { + page: number; + hasNext: boolean; + }; }; From fabba05fff808cbcd76ea84a79a28901d78ff786 Mon Sep 17 00:00:00 2001 From: minchodang Date: Thu, 25 Apr 2024 17:04:20 +0900 Subject: [PATCH 3/4] =?UTF-8?q?Fix:=20=ED=8C=8C=ED=8B=B0=20=ED=98=84?= =?UTF-8?q?=ED=99=A9=20api=20=EC=9D=91=EB=8B=B5=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20ui=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/getPartyCurrentSituation.ts | 25 ++++++ src/api/getPartyJoin.ts | 10 +-- src/api/getPartyMainPage.ts | 33 ++++---- src/api/getPartyStatus.ts | 20 ----- src/components/profile/PartySituation.tsx | 78 +++++++++++++++---- .../profile/PartySituationItemList.tsx | 2 +- types/party.ts | 13 ---- types/party/index.ts | 28 +++++++ 8 files changed, 136 insertions(+), 73 deletions(-) create mode 100644 src/api/getPartyCurrentSituation.ts delete mode 100644 src/api/getPartyStatus.ts delete mode 100644 types/party.ts create mode 100644 types/party/index.ts diff --git a/src/api/getPartyCurrentSituation.ts b/src/api/getPartyCurrentSituation.ts new file mode 100644 index 0000000..17c1001 --- /dev/null +++ b/src/api/getPartyCurrentSituation.ts @@ -0,0 +1,25 @@ +import defaultRequest from 'src/lib/axios/defaultRequest'; +import { GetPartyCurrentSituationResponse } from 'types/party'; + +type GetPartyCurrentSituationRequestRole = 'HOST' | 'VOLUNTEER'; +export type GetPartyCurrentSituationRequestStatus = 'RECRUIT' | 'RECRUIT_FINISH' | 'PARTY_FINISH'; +interface GetPartyCurrentSituationParameter { + page: number; + size: number; + sort: string; + role: GetPartyCurrentSituationRequestRole; + status: GetPartyCurrentSituationRequestStatus; +} + +export const API_GET_PARTY_STATUS_KEY = '/api/party/party-status'; + +const getPartyStatus = async (params: GetPartyCurrentSituationParameter) => { + const { data } = await defaultRequest.get< + InfinitePaginationDataType<'partyList', GetPartyCurrentSituationResponse> + >(API_GET_PARTY_STATUS_KEY, { + params, + }); + return data; +}; + +export default getPartyStatus; diff --git a/src/api/getPartyJoin.ts b/src/api/getPartyJoin.ts index 7269c90..ffe8ad6 100644 --- a/src/api/getPartyJoin.ts +++ b/src/api/getPartyJoin.ts @@ -2,8 +2,8 @@ import variableAssignMent from '@utils/variableAssignment'; import defaultRequest from 'src/lib/axios/defaultRequest'; import { PartyJoinResponse } from 'types/party/join/PartyJoinResponse'; -export type GetPartyJoinRequestRole = 'HOST' | 'VOLUNTEER'; -interface getPartyJoinParameter { +type GetPartyJoinRequestRole = 'HOST' | 'VOLUNTEER'; +interface GetPartyJoinParameter { page: number; size: number; sort: string; @@ -12,13 +12,11 @@ interface getPartyJoinParameter { export const API_GET_PARTY_JOIN_KEY = '/api/party/party-join'; -const getPartyJoin = async (params: getPartyJoinParameter) => { +const getPartyJoin = async (params: GetPartyJoinParameter) => { const { data } = await defaultRequest.get< InfinitePaginationDataType<'partyList', PartyJoinResponse> >(API_GET_PARTY_JOIN_KEY, { - params: { - ...params, - }, + params, }); return data; }; diff --git a/src/api/getPartyMainPage.ts b/src/api/getPartyMainPage.ts index c735b61..8e28eea 100644 --- a/src/api/getPartyMainPage.ts +++ b/src/api/getPartyMainPage.ts @@ -1,28 +1,23 @@ -import variableAssignMent from "@utils/variableAssignment"; -import defaultRequest from "src/lib/axios/defaultRequest"; -import { PartyListResponse } from "types/common/PartyListResponse"; +import variableAssignMent from '@utils/variableAssignment'; +import defaultRequest from 'src/lib/axios/defaultRequest'; +import { PartyListResponse } from 'types/common/PartyListResponse'; interface GetMainPageParameter { - longitude: number; - latitude: number; - lastPartyId?: number; - size?: number; + longitude: number; + latitude: number; + lastPartyId?: number; + size?: number; } -export const API_GET_MAIN_PAGE = - "/api/main"; +export const API_GET_MAIN_PAGE = '/api/main'; const getMainPageData = async (params: GetMainPageParameter) => { - const { data } = await defaultRequest.get< - InfinitePaginationDataType<"partyList", PartyListResponse> - >( - API_GET_MAIN_PAGE, { - params:{ - ...params - } - } - ); - return data; + const { data } = await defaultRequest.get< + InfinitePaginationDataType<'partyList', PartyListResponse> + >(API_GET_MAIN_PAGE, { + params, + }); + return data; }; export default getMainPageData; diff --git a/src/api/getPartyStatus.ts b/src/api/getPartyStatus.ts deleted file mode 100644 index 2bb6691..0000000 --- a/src/api/getPartyStatus.ts +++ /dev/null @@ -1,20 +0,0 @@ -import defaultRequest from "src/lib/axios/defaultRequest"; -import variableAssignMent from "@utils/variableAssignment"; -import { PartyDetailResponse } from "types/party/detail/PartyDetailResponse"; - -interface getPartyStatusParameter { - role: string; -} - -export const API_GET_PARTY_STATUS_KEY = "/api/party/party-status?role={{role}}"; - -const getPartyStatus = async ({ - role, -}: getPartyStatusParameter): Promise => { - const { data } = await defaultRequest.get( - variableAssignMent(API_GET_PARTY_STATUS_KEY, { role: role }) - ); - return data; -}; - -export default getPartyStatus; diff --git a/src/components/profile/PartySituation.tsx b/src/components/profile/PartySituation.tsx index 91381e7..99284ca 100644 --- a/src/components/profile/PartySituation.tsx +++ b/src/components/profile/PartySituation.tsx @@ -5,15 +5,22 @@ import PartySituationItemList from './PartySituationItemList'; import ProfileTabSortingButton from './ProfileTabSortingButton'; import { useRouter } from 'next/router'; import { useSearchParam } from 'react-use'; +import { PartyCurrentSituationRequestStatus } from 'types/party'; type PartySituationType = '모집중' | '참가중'; export type PartySituationRole = 'HOST' | 'VOLUNTEER'; +type PartyCurrentStatus = '모집중' | '모집완료' | '파티종료'; interface CategoryItemType { id: PartySituationRole; label: PartySituationType; } +interface StatusItemType { + id: PartyCurrentSituationRequestStatus; + label: PartyCurrentStatus; +} + const Container = styled.div` display: flex; flex-direction: column; @@ -27,23 +34,41 @@ const PartyListContainer = styled.div` `; const TabWrapper = styled.div` display: flex; + flex-direction: column; gap: 10px; padding: 10px; `; +const TabSection = styled.section` + display: flex; + gap: 10px; +`; + const categoryTab: CategoryItemType[] = [ { id: 'HOST', label: '모집중' }, { id: 'VOLUNTEER', label: '참가중' }, ]; +const statusTab: StatusItemType[] = [ + { id: 'RECRUIT', label: '모집중' }, + { + id: 'RECRUIT_FINISH', + label: '모집완료', + }, + { id: 'PARTY_FINISH', label: '파티종료' }, +]; + function isPartySituationRole(value: unknown): value is PartySituationRole { return value === 'HOST' || value === 'VOLUNTEER'; } +function isPartySituation(value: unknown): value is PartyCurrentSituationRequestStatus { + return value === 'RECRUIT' || value === 'RECRUIT_FINISH' || value === 'PARTY_FINISH'; +} const PartySituation = () => { - // const [selectedRole, setSelectedRole] = useState('HOST'); const { replace, query } = useRouter(); const situationRole = useSearchParam('role'); + const situation = useSearchParam('situation'); const selectedRole = useMemo(() => { if (!situationRole || !isPartySituationRole(situationRole)) { return; @@ -51,23 +76,48 @@ const PartySituation = () => { return situationRole; }, [situationRole]); + const selectedSituation = useMemo(() => { + if (!situation || !isPartySituation(situation)) { + return; + } + return situation; + }, [situation]); + return ( - {categoryTab.map((tab) => { - const onClick = () => { - replace({ query: { ...query, role: tab.id } }); - }; + + {categoryTab.map((tab) => { + const onClick = () => { + replace({ query: { ...query, role: tab.id } }); + }; + + return ( + + ); + })} + + + {statusTab.map((tab) => { + const onClick = () => { + replace({ query: { ...query, status: tab.id } }); + }; - return ( - - ); - })} + return ( + + ); + })} + diff --git a/src/components/profile/PartySituationItemList.tsx b/src/components/profile/PartySituationItemList.tsx index e82dbcd..3ec460c 100644 --- a/src/components/profile/PartySituationItemList.tsx +++ b/src/components/profile/PartySituationItemList.tsx @@ -1,6 +1,6 @@ import { useSuspenseQuery } from '@tanstack/react-query'; import { FC } from 'react'; -import getPartyStatus, { API_GET_PARTY_STATUS_KEY } from 'src/api/getPartyStatus'; +import getPartyStatus, { API_GET_PARTY_STATUS_KEY } from 'src/api/getPartyCurrentSituation'; import { PartySituationRole } from './PartySituation'; import PartyList from './PartyList'; import { DefaultText } from '@components/common/DefaultText'; diff --git a/types/party.ts b/types/party.ts deleted file mode 100644 index 3b4b522..0000000 --- a/types/party.ts +++ /dev/null @@ -1,13 +0,0 @@ -interface PartyData { - categoryId: string; - thumbnailUrl: string; - partyTitle: string; - region: string; - partyTime: string; - genderLimit: string; - agePreference: string; - partyMessage: string; - totalRecruitment: string; -} - -export type { PartyData }; diff --git a/types/party/index.ts b/types/party/index.ts new file mode 100644 index 0000000..51c8e14 --- /dev/null +++ b/types/party/index.ts @@ -0,0 +1,28 @@ +import { UserGender } from 'types/profile/user/UserProfileResponse'; +import { PartyAge } from './join/PartyJoinResponse'; + +export type PartyCurrentSituationRequestStatus = 'RECRUIT' | 'RECRUIT_FINISH' | 'PARTY_FINISH'; +export type PartyFoodCategory = 'KOREAN' | 'WESTERN' | 'JAPANESE' | 'CHINESE' | 'ETC'; + +export interface GetPartyCurrentSituationResponse { + userId: number; + partyId: number; + partyTitle: string; + partyContent: string; + address: string; + longitude: number; + latitude: number; + partyPlaceName: string; + status: PartyCurrentSituationRequestStatus; + gender: UserGender; + age: PartyAge; + deadline: string; + partyTime: string; + totalParticipate: 4; + participate: 2; + menu: string; + category: PartyFoodCategory; + thumbnail: string; + hit: number; + reviewExist: boolean; +} From 74e8e81b9b85749844796c48ffec92c09458848b Mon Sep 17 00:00:00 2001 From: minchodang Date: Sun, 28 Apr 2024 12:14:35 +0900 Subject: [PATCH 4/4] =?UTF-8?q?Fix:=20=ED=8C=8C=ED=8B=B0=20=ED=98=84?= =?UTF-8?q?=ED=99=A9,=20=EC=B4=88=EB=8C=80=20=EC=9A=94=EC=B2=AD=20api=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/profile/index.tsx | 31 ++++++- src/api/getPartyCurrentSituation.ts | 1 - src/api/getPartyJoin.ts | 1 - .../hoc/QuerySuspenseErrorBoundary.tsx | 75 +++++++++-------- src/components/profile/PartyList.tsx | 82 ------------------ .../profile/PartySituationItemList.tsx | 36 -------- src/components/profile/ProfileInfo.tsx | 3 - src/components/profile/ProfileTab.tsx | 10 +-- src/components/profile/ProfileTabPanel.tsx | 33 -------- src/components/profile/ToggleButton.tsx | 48 ----------- src/components/profile/party/PartyList.tsx | 83 +++++++++++++++++++ .../{ => party/request}/PartyRequest.tsx | 76 ++++++----------- .../{ => party/request}/PartyRequestCard.tsx | 0 .../request}/PartyRequestItemList.tsx | 3 +- .../{ => party/status}/PartySituation.tsx | 71 +++++++++++----- .../party/status/PartySituationItemList.tsx | 67 +++++++++++++++ .../{ => review}/ProfileReviewList.tsx | 6 +- src/lib/axios/defaultRequest.ts | 76 ++++++++--------- 18 files changed, 338 insertions(+), 364 deletions(-) delete mode 100644 src/components/profile/PartyList.tsx delete mode 100644 src/components/profile/PartySituationItemList.tsx delete mode 100644 src/components/profile/ProfileTabPanel.tsx delete mode 100644 src/components/profile/ToggleButton.tsx create mode 100644 src/components/profile/party/PartyList.tsx rename src/components/profile/{ => party/request}/PartyRequest.tsx (61%) rename src/components/profile/{ => party/request}/PartyRequestCard.tsx (100%) rename src/components/profile/{ => party/request}/PartyRequestItemList.tsx (95%) rename src/components/profile/{ => party/status}/PartySituation.tsx (59%) create mode 100644 src/components/profile/party/status/PartySituationItemList.tsx rename src/components/profile/{ => review}/ProfileReviewList.tsx (96%) diff --git a/pages/profile/index.tsx b/pages/profile/index.tsx index 94439d0..8baaaa1 100644 --- a/pages/profile/index.tsx +++ b/pages/profile/index.tsx @@ -1,4 +1,5 @@ import BackgroundImage from '@components/common/BackgroundImage'; +import { DefaultButton } from '@components/common/DefaultButton'; import { DefaultHeader } from '@components/common/DefaultHeader'; import { HeaderBackButton } from '@components/common/HeaderBackButton'; import QuerySuspenseErrorBoundary from '@components/hoc/QuerySuspenseErrorBoundary'; @@ -10,6 +11,7 @@ import ProfileTab from '@components/profile/ProfileTab'; import styled from '@emotion/styled'; import { GetServerSideProps } from 'next'; import Link from 'next/link'; +import { useRouter } from 'next/router'; import { useRef } from 'react'; import { useScroll } from 'react-use'; @@ -28,7 +30,6 @@ const Container = styled.div` const ProfileInfoContainer = styled.div` display: flex; flex-direction: column; - height: 200px; width: 100%; `; @@ -40,6 +41,15 @@ const RightAreaContainer = styled.div` cursor: pointer; `; +const ProfileLoginButtonWrapper = styled.div` + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + height: 120px; + padding: 0 30px; +`; + const RightArea = () => { return ( @@ -51,6 +61,10 @@ const RightArea = () => { const Profile = () => { const scrollRef = useRef(null); const { y } = useScroll(scrollRef); + const { push } = useRouter(); + const onClickLoginButton = () => { + push('/signin'); + }; return ( @@ -58,7 +72,18 @@ const Profile = () => { { + if (error?.response?.status === 401) { + return ( + + + + ); + } + }} suspenseFallback={} > @@ -79,7 +104,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { return { redirect: { permanent: false, - destination: '/profile?category=situation&role=HOST', + destination: '/profile?category=situation&role=HOST&status=RECRUIT', }, }; } diff --git a/src/api/getPartyCurrentSituation.ts b/src/api/getPartyCurrentSituation.ts index 17c1001..b3cfec4 100644 --- a/src/api/getPartyCurrentSituation.ts +++ b/src/api/getPartyCurrentSituation.ts @@ -6,7 +6,6 @@ export type GetPartyCurrentSituationRequestStatus = 'RECRUIT' | 'RECRUIT_FINISH' interface GetPartyCurrentSituationParameter { page: number; size: number; - sort: string; role: GetPartyCurrentSituationRequestRole; status: GetPartyCurrentSituationRequestStatus; } diff --git a/src/api/getPartyJoin.ts b/src/api/getPartyJoin.ts index ffe8ad6..4364b7f 100644 --- a/src/api/getPartyJoin.ts +++ b/src/api/getPartyJoin.ts @@ -6,7 +6,6 @@ type GetPartyJoinRequestRole = 'HOST' | 'VOLUNTEER'; interface GetPartyJoinParameter { page: number; size: number; - sort: string; role: GetPartyJoinRequestRole; } diff --git a/src/components/hoc/QuerySuspenseErrorBoundary.tsx b/src/components/hoc/QuerySuspenseErrorBoundary.tsx index ee2cb12..11a93ce 100644 --- a/src/components/hoc/QuerySuspenseErrorBoundary.tsx +++ b/src/components/hoc/QuerySuspenseErrorBoundary.tsx @@ -1,41 +1,48 @@ -import DefaultError from "@components/common/DefaultError"; -import DefaultLoading from "@components/common/DefaultLoading"; -import { Suspense } from "@suspensive/react"; -import { QueryErrorResetBoundary } from "@tanstack/react-query"; // (*) -import { FC, PropsWithChildren, ReactEventHandler } from "react"; -import { ErrorBoundary } from "react-error-boundary"; +import DefaultError from '@components/common/DefaultError'; +import DefaultLoading from '@components/common/DefaultLoading'; +import { Suspense } from '@suspensive/react'; +import { QueryErrorResetBoundary } from '@tanstack/react-query'; // (*) +import { AxiosError } from 'axios'; +import { FC, PropsWithChildren, ReactEventHandler } from 'react'; +import { ErrorBoundary } from 'react-error-boundary'; interface QuerySuspenseErrorBoundaryProps { - children: React.ReactNode; - suspenseFallback?: React.ReactNode | string; - errorFallback?: ( - resetErrorBoundary: ReactEventHandler - ) => React.ReactNode; + children: React.ReactNode; + suspenseFallback?: React.ReactNode | string; + errorFallback?: ({ + resetErrorBoundary, + error, + }: { + resetErrorBoundary: ReactEventHandler; + error?: AxiosError; + }) => React.ReactNode; } -const QuerySuspenseErrorBoundary: FC< - PropsWithChildren -> = ({ children, suspenseFallback, errorFallback }) => { - return ( - - {({ reset }) => ( - - errorFallback ? ( - errorFallback(resetErrorBoundary) - ) : ( - - ) - } - > - }> - {children} - - - )} - - ); +const QuerySuspenseErrorBoundary: FC> = ({ + children, + suspenseFallback, + errorFallback, +}) => { + return ( + + {({ reset }) => ( + + errorFallback ? ( + errorFallback({ resetErrorBoundary, error }) + ) : ( + + ) + } + > + }> + {children} + + + )} + + ); }; export default QuerySuspenseErrorBoundary; diff --git a/src/components/profile/PartyList.tsx b/src/components/profile/PartyList.tsx deleted file mode 100644 index 3d53bb2..0000000 --- a/src/components/profile/PartyList.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React from "react"; -import styled from "@emotion/styled"; -import { DefaultText } from "@components/common/DefaultText"; -import Image from "next/image"; -import { PartyDetailResponse } from "types/party/detail/PartyDetailResponse"; -import Link from "next/link"; -import dayjs from "dayjs"; -interface PartyListProps { - data: PartyDetailResponse; -} - -const Container = styled.div` - display: flex; - flex-direction: row; - gap: 16px; - padding: 16px; - border-bottom: 1px solid #ebebeb; - transition: all 0.1s; - &:hover { - background-color: #dddddd; - } - cursor: pointer; -`; - -const PartyDetail = styled.div` - display: flex; - flex-direction: column; - gap: 8px; - padding: 8px; -`; -const Title = styled.div` - display: flex; - flex-direction: row; -`; -const Address = styled.div` - display: flex; - flex-direction: row; - gap: 4px; -`; -const Time = styled.div` - display: flex; - flex-direction: row; - gap: 4px; -`; - -const PartyList = ({ data }: PartyListProps) => { - const { partyId, partyTitle, partyTime, address, thumbnail } = data; - - return ( - - - 프로필사진 - - - <DefaultText text={partyTitle} size={16} weight={500} /> - -
- -
- -
-
- - ); -}; - -export default PartyList; diff --git a/src/components/profile/PartySituationItemList.tsx b/src/components/profile/PartySituationItemList.tsx deleted file mode 100644 index 3ec460c..0000000 --- a/src/components/profile/PartySituationItemList.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useSuspenseQuery } from '@tanstack/react-query'; -import { FC } from 'react'; -import getPartyStatus, { API_GET_PARTY_STATUS_KEY } from 'src/api/getPartyCurrentSituation'; -import { PartySituationRole } from './PartySituation'; -import PartyList from './PartyList'; -import { DefaultText } from '@components/common/DefaultText'; -import styled from '@emotion/styled'; - -interface PartySituationItemListProps { - selectedRole: PartySituationRole; -} - -const Container = styled.div` - width: 100%; - display: flex; - justify-content: center; - align-items: center; - height: 200px; -`; - -const PartySituationItemList: FC = ({ selectedRole }) => { - const statusList = useSuspenseQuery({ - queryKey: [API_GET_PARTY_STATUS_KEY, { role: selectedRole }], - queryFn: () => getPartyStatus({ role: selectedRole }), - }); - if (!statusList.data.length) { - return ( - - - - ); - } - return statusList.data.map((status) => ); -}; - -export default PartySituationItemList; diff --git a/src/components/profile/ProfileInfo.tsx b/src/components/profile/ProfileInfo.tsx index d324c2b..2c85610 100644 --- a/src/components/profile/ProfileInfo.tsx +++ b/src/components/profile/ProfileInfo.tsx @@ -64,9 +64,6 @@ const ProfileDetail = styled.div` gap: 8px; `; -const userId = 11; -// 로그인 기능 연결후 userid 받아올 예정 - const ProfileInfo = () => { const { data } = useSuspenseQuery({ queryKey: [API_GET_PROFILE_KEY], diff --git a/src/components/profile/ProfileTab.tsx b/src/components/profile/ProfileTab.tsx index da4b23d..a209254 100644 --- a/src/components/profile/ProfileTab.tsx +++ b/src/components/profile/ProfileTab.tsx @@ -2,11 +2,11 @@ import styled from '@emotion/styled'; import { useRouter } from 'next/router'; import { useMemo } from 'react'; import { useSearchParam } from 'react-use'; -import PartySituation from './PartySituation'; +import PartySituation from './party/status/PartySituation'; import TabComponent from './TabComponent'; -import PartyRequest from './PartyRequest'; -import ReviewList from './ProfileReviewList'; -import ProfileReviewList from './ProfileReviewList'; +import PartyRequest from './party/request/PartyRequest'; +import ReviewList from './review/ProfileReviewList'; +import ProfileReviewList from './review/ProfileReviewList'; interface CategoryItemType { id: CategoryIdType; @@ -78,7 +78,7 @@ const ProfileTab = () => { replace({ query: { category: id, role: 'SENDER' } }); return; } - replace({ query: { category: id, role: 'HOST' } }); + replace({ query: { category: id, role: 'VOLUNTEER', status: 'RECRUIT' } }); }; return ( diff --git a/src/components/profile/ProfileTabPanel.tsx b/src/components/profile/ProfileTabPanel.tsx deleted file mode 100644 index d8cc737..0000000 --- a/src/components/profile/ProfileTabPanel.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import Box from "@mui/material/Box"; -import QuerySuspenseErrorBoundary from "@components/hoc/QuerySuspenseErrorBoundary"; -import ProfileError from "./ProfileError"; -import ProfileLoading from "./ProfileLoading"; - -interface TabPanelProps { - children?: React.ReactNode; - index: number; - value: number; -} - -const ProfileTabPanel = (props: TabPanelProps) => { - const { children, value, index, ...other } = props; - - return ( - } - > - - - ); -}; - -export default ProfileTabPanel; diff --git a/src/components/profile/ToggleButton.tsx b/src/components/profile/ToggleButton.tsx deleted file mode 100644 index eb54c34..0000000 --- a/src/components/profile/ToggleButton.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import styled from "@emotion/styled"; -import { DefaultButton } from "@components/common/DefaultButton"; - -interface ToggleButtonProps { - partystate: string; - setPartystate: React.Dispatch>; -} - -const Container = styled.div` - display: flex; - background-color: white; - padding: 16px; - flex-direction: row; - gap: 10px; - transition: top 0.3s ease; - z-index: 9; -`; - -const ButtonList = [ - { - text: "모집중", - value: "host", - }, - { - text: "참가중", - value: "member", - }, -]; - -const ToggleButton = ({ partystate, setPartystate }: ToggleButtonProps) => { - return ( - - {ButtonList.map(({ text, value }) => ( - { - setPartystate(value); - }} - /> - ))} - - ); -}; - -export default ToggleButton; diff --git a/src/components/profile/party/PartyList.tsx b/src/components/profile/party/PartyList.tsx new file mode 100644 index 0000000..5657711 --- /dev/null +++ b/src/components/profile/party/PartyList.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import styled from '@emotion/styled'; +import { DefaultText } from '@components/common/DefaultText'; +import Image from 'next/image'; +import { PartyDetailResponse } from 'types/party/detail/PartyDetailResponse'; +import Link from 'next/link'; +import dayjs from 'dayjs'; +import { GetPartyCurrentSituationResponse } from 'types/party'; +interface PartyListProps { + data: PartyDetailResponse | GetPartyCurrentSituationResponse; +} + +const Container = styled.div` + display: flex; + flex-direction: row; + gap: 16px; + padding: 16px; + border-bottom: 1px solid #ebebeb; + transition: all 0.1s; + &:hover { + background-color: #dddddd; + } + cursor: pointer; +`; + +const PartyDetail = styled.div` + display: flex; + flex-direction: column; + gap: 8px; + padding: 8px; +`; +const Title = styled.div` + display: flex; + flex-direction: row; +`; +const Address = styled.div` + display: flex; + flex-direction: row; + gap: 4px; +`; +const Time = styled.div` + display: flex; + flex-direction: row; + gap: 4px; +`; + +const PartyList = ({ data }: PartyListProps) => { + const { partyId, partyTitle, partyTime, address, thumbnail } = data; + + return ( + + + 프로필사진 + + + <DefaultText text={partyTitle} size={16} weight={500} /> + +
+ +
+ +
+
+ + ); +}; + +export default PartyList; diff --git a/src/components/profile/PartyRequest.tsx b/src/components/profile/party/request/PartyRequest.tsx similarity index 61% rename from src/components/profile/PartyRequest.tsx rename to src/components/profile/party/request/PartyRequest.tsx index 643eef2..f9ff1f9 100644 --- a/src/components/profile/PartyRequest.tsx +++ b/src/components/profile/party/request/PartyRequest.tsx @@ -1,6 +1,6 @@ import React, { useMemo, useState } from 'react'; import styled from '@emotion/styled'; -import ButtonList from './ProfileTabSortingButton'; +import ButtonList from '../../ProfileTabSortingButton'; import { useQuery } from '@tanstack/react-query'; import getPartyJoin from 'src/api/getPartyJoin'; import { API_GET_PARTY_JOIN_KEY } from 'src/api/getPartyJoin'; @@ -8,10 +8,11 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import postPartyDecision from 'src/api/postPartyDecision'; import postParticipate from 'src/api/postParticipate'; import { useRouter } from 'next/router'; -import ProfileTabSortingButton from './ProfileTabSortingButton'; +import ProfileTabSortingButton from '../../ProfileTabSortingButton'; import QuerySuspenseErrorBoundary from '@components/hoc/QuerySuspenseErrorBoundary'; import PartyRequestItemList from './PartyRequestItemList'; import { useSearchParam } from 'react-use'; +import { DefaultText } from '@components/common/DefaultText'; type PartyRequestType = '받은요청' | '보낸요청'; export type PartyRequestRole = 'HOST' | 'VOLUNTEER'; @@ -43,6 +44,11 @@ const PartyRequestContainer = styled.div` padding: 0 16px; overflow: auto; `; +const LoginRequiredTextWrapper = styled.div` + display: flex; + justify-content: center; + width: 100%; +`; function isPartyRequestRole(value: unknown): value is PartyRequestRole { return value === 'HOST' || value === 'VOLUNTEER'; @@ -58,55 +64,6 @@ const PartyRequest = () => { return requestRole; }, [requestRole]); - // const queryClient = useQueryClient(); - - // const { data } = useQuery({ - // queryKey: [API_GET_PARTY_JOIN_KEY, { role }], - // queryFn: () => getPartyJoin({ role }), - // enabled: !!role, - // }); - - // const postDecisionMutate = useMutation({ - // mutationFn: postPartyDecision, - // onSuccess: () => { - // queryClient.invalidateQueries({ - // queryKey: [API_GET_PARTY_JOIN_KEY, { role }], - // }); - // }, - // }); - - // const postParticipateMutate = useMutation({ - // mutationFn: postParticipate, - // onSuccess: () => { - // queryClient.invalidateQueries({ - // queryKey: [API_GET_PARTY_JOIN_KEY, { role }], - // }); - // }, - // }); - - // const joinDecision = (id: number, nickname: string, status: boolean) => { - // if (role === 'HOST') { - // return postDecisionMutate.mutate({ - // nickname: nickname, - // partyId: Number(id), - // status: `${status ? 'ACCEPT' : 'REFUSE'}`, - // }); - // } - // postParticipateMutate.mutate({ - // partyId: Number(id), - // status: `${status ? 'APPLY' : 'CANCEL'}`, - // }); - // }; - - // const setButtonState = (state: string) => { - // router.replace({ - // query: { - // ...router.query, - // role: state, - // }, - // }); - // }; - return ( @@ -127,7 +84,22 @@ const PartyRequest = () => { - + { + if (error?.response?.status === 401) { + return ( + + + + ); + } + }} + > diff --git a/src/components/profile/PartyRequestCard.tsx b/src/components/profile/party/request/PartyRequestCard.tsx similarity index 100% rename from src/components/profile/PartyRequestCard.tsx rename to src/components/profile/party/request/PartyRequestCard.tsx diff --git a/src/components/profile/PartyRequestItemList.tsx b/src/components/profile/party/request/PartyRequestItemList.tsx similarity index 95% rename from src/components/profile/PartyRequestItemList.tsx rename to src/components/profile/party/request/PartyRequestItemList.tsx index f2f92e5..281d3da 100644 --- a/src/components/profile/PartyRequestItemList.tsx +++ b/src/components/profile/party/request/PartyRequestItemList.tsx @@ -27,7 +27,6 @@ const PartyRequestItemList: FC = ({ role }) => { getPartyJoin({ page: pageParam, role, - sort: '', size: 5, }), initialPageParam: 0, @@ -40,7 +39,7 @@ const PartyRequestItemList: FC = ({ role }) => { if (!partyRequestList.data.pages[0].partyList.length) { return ( - + ); } diff --git a/src/components/profile/PartySituation.tsx b/src/components/profile/party/status/PartySituation.tsx similarity index 59% rename from src/components/profile/PartySituation.tsx rename to src/components/profile/party/status/PartySituation.tsx index 99284ca..c108d48 100644 --- a/src/components/profile/PartySituation.tsx +++ b/src/components/profile/party/status/PartySituation.tsx @@ -1,18 +1,19 @@ import QuerySuspenseErrorBoundary from '@components/hoc/QuerySuspenseErrorBoundary'; import styled from '@emotion/styled'; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import PartySituationItemList from './PartySituationItemList'; -import ProfileTabSortingButton from './ProfileTabSortingButton'; +import ProfileTabSortingButton from '../../ProfileTabSortingButton'; import { useRouter } from 'next/router'; import { useSearchParam } from 'react-use'; import { PartyCurrentSituationRequestStatus } from 'types/party'; +import { DefaultText } from '@components/common/DefaultText'; -type PartySituationType = '모집중' | '참가중'; -export type PartySituationRole = 'HOST' | 'VOLUNTEER'; -type PartyCurrentStatus = '모집중' | '모집완료' | '파티종료'; +type PartySituationType = '참여' | '개설'; +export type PartyStatusRole = 'HOST' | 'VOLUNTEER'; +type PartyCurrentStatus = '모집중' | '모집마감' | '종료'; interface CategoryItemType { - id: PartySituationRole; + id: PartyStatusRole; label: PartySituationType; } @@ -44,21 +45,27 @@ const TabSection = styled.section` gap: 10px; `; +const LoginRequiredTextWrapper = styled.div` + display: flex; + justify-content: center; + width: 100%; +`; + const categoryTab: CategoryItemType[] = [ - { id: 'HOST', label: '모집중' }, - { id: 'VOLUNTEER', label: '참가중' }, + { id: 'VOLUNTEER', label: '참여' }, + { id: 'HOST', label: '개설' }, ]; const statusTab: StatusItemType[] = [ { id: 'RECRUIT', label: '모집중' }, { id: 'RECRUIT_FINISH', - label: '모집완료', + label: '모집마감', }, - { id: 'PARTY_FINISH', label: '파티종료' }, + { id: 'PARTY_FINISH', label: '종료' }, ]; -function isPartySituationRole(value: unknown): value is PartySituationRole { +function isPartyRole(value: unknown): value is PartyStatusRole { return value === 'HOST' || value === 'VOLUNTEER'; } function isPartySituation(value: unknown): value is PartyCurrentSituationRequestStatus { @@ -67,21 +74,21 @@ function isPartySituation(value: unknown): value is PartyCurrentSituationRequest const PartySituation = () => { const { replace, query } = useRouter(); - const situationRole = useSearchParam('role'); - const situation = useSearchParam('situation'); + const role = useSearchParam('role'); + const status = useSearchParam('status'); const selectedRole = useMemo(() => { - if (!situationRole || !isPartySituationRole(situationRole)) { + if (!role || !isPartyRole(role)) { return; } - return situationRole; - }, [situationRole]); + return role; + }, [role]); - const selectedSituation = useMemo(() => { - if (!situation || !isPartySituation(situation)) { + const selectedStatus = useMemo(() => { + if (!status || !isPartySituation(status)) { return; } - return situation; - }, [situation]); + return status; + }, [status]); return ( @@ -112,7 +119,7 @@ const PartySituation = () => { ); @@ -121,8 +128,26 @@ const PartySituation = () => { - - + { + if (error?.response?.status === 401) { + return ( + + + + ); + } + }} + > + diff --git a/src/components/profile/party/status/PartySituationItemList.tsx b/src/components/profile/party/status/PartySituationItemList.tsx new file mode 100644 index 0000000..a30f7de --- /dev/null +++ b/src/components/profile/party/status/PartySituationItemList.tsx @@ -0,0 +1,67 @@ +import { useSuspenseInfiniteQuery, useSuspenseQuery } from '@tanstack/react-query'; +import { FC } from 'react'; +import getPartyStatus, { API_GET_PARTY_STATUS_KEY } from 'src/api/getPartyCurrentSituation'; +import { PartyStatusRole } from './PartySituation'; +import PartyList from '../PartyList'; +import { DefaultText } from '@components/common/DefaultText'; +import styled from '@emotion/styled'; +import { PartyCurrentSituationRequestStatus } from 'types/party'; +import { ObserverTrigger } from '@components/hoc/ObserverTrigger'; + +interface PartySituationItemListProps { + selectedRole: PartyStatusRole; + selectedStatus: PartyCurrentSituationRequestStatus; +} + +const Container = styled.div` + width: 100%; + display: flex; + justify-content: center; + align-items: center; + height: 200px; +`; + +const PartySituationItemList: FC = ({ + selectedRole, + selectedStatus, +}) => { + // const statusList = useSuspenseQuery({ + // queryKey: [API_GET_PARTY_STATUS_KEY, { role: selectedRole }], + // queryFn: () => getPartyStatus({ role: selectedRole }), + // }); + + const partyStatusList = useSuspenseInfiniteQuery({ + queryKey: [API_GET_PARTY_STATUS_KEY, , { role: selectedRole }], + queryFn: ({ pageParam = 0 }) => + getPartyStatus({ + page: pageParam, + role: selectedRole, + status: selectedStatus, + size: 5, + }), + initialPageParam: 0, + getNextPageParam: (lastPage) => lastPage?.pageInfo?.page, + }); + const onObserve = () => { + if (partyStatusList.hasNextPage) partyStatusList.fetchNextPage(); + }; + if (!partyStatusList.data.pages[0].partyList.length) { + return ( + + + + ); + } + + return ( + + {partyStatusList.data.pages.map((status) => + status.partyList.map((individualStatus) => ( + + )), + )} + + ); +}; + +export default PartySituationItemList; diff --git a/src/components/profile/ProfileReviewList.tsx b/src/components/profile/review/ProfileReviewList.tsx similarity index 96% rename from src/components/profile/ProfileReviewList.tsx rename to src/components/profile/review/ProfileReviewList.tsx index f04e86a..0f00c91 100644 --- a/src/components/profile/ProfileReviewList.tsx +++ b/src/components/profile/review/ProfileReviewList.tsx @@ -2,16 +2,16 @@ import styled from '@emotion/styled'; import { useRouter } from 'next/router'; import { FC, useMemo } from 'react'; import { useSearchParam } from 'react-use'; -import ProfileTabSortingButton from './ProfileTabSortingButton'; +import ProfileTabSortingButton from '../ProfileTabSortingButton'; import QuerySuspenseErrorBoundary from '@components/hoc/QuerySuspenseErrorBoundary'; import { useSuspenseQuery } from '@tanstack/react-query'; import { ProfileReviewListRequestType } from 'src/api/getProfileReviewList'; -import ProfileReviewCard from '../common/card/ReviewCard'; +import ProfileReviewCard from '../../common/card/ReviewCard'; import { GetReviewListResponse, ImageType } from 'types/review'; import dayjs from 'dayjs'; import React from 'react'; import { useModalContext } from '@mantine/core/lib/components/Modal/Modal.context'; -import ReviewCard from '../common/card/ReviewCard'; +import ReviewCard from '../../common/card/ReviewCard'; interface ProfileReviewListProps {} type ProfileReviewListType = '보낸리뷰' | '받은리뷰'; diff --git a/src/lib/axios/defaultRequest.ts b/src/lib/axios/defaultRequest.ts index 50bd1fa..d207971 100644 --- a/src/lib/axios/defaultRequest.ts +++ b/src/lib/axios/defaultRequest.ts @@ -1,50 +1,50 @@ -import axios from "axios"; -import { getCookie } from "cookies-next"; +import axios from 'axios'; +import { getCookie } from 'cookies-next'; const defaultRequest = axios.create({ - baseURL: process.env.MATITTING_HOST_URL, - headers: { - "Content-Type": "application/json", - }, - withCredentials: true, + baseURL: process.env.MATITTING_HOST_URL, + headers: { + 'Content-Type': 'application/json', + }, + withCredentials: true, }); defaultRequest.interceptors.response.use( - async function (response) { - return response; - }, - async function (error) { - if (error.response && error.response.status === 401) { - const refreshToken = getCookie("refreshToken"); + async function (response) { + return response; + }, + async function (error) { + if (error.response && error.response.status === 401) { + const refreshToken = getCookie('refreshToken'); - if (!refreshToken) { - await alert("로그인이 필요합니다. 로그인 해 주세요."); - window.location.href = "/signin"; - return Promise.reject(error); - } + if (!refreshToken) { + // await alert('로그인이 필요합니다. 로그인 해 주세요.'); + // window.location.href = '/signin'; + return Promise.reject(error); + } - try { - const response = await defaultRequest.get("/oauth2/renew-token", { - headers: { - "Authorization-Refresh": refreshToken, - }, - }); + try { + const response = await defaultRequest.get('/oauth2/renew-token', { + headers: { + 'Authorization-Refresh': refreshToken, + }, + }); - // 토큰 갱신 성공 시 새로운 토큰으로 요청 재시도 - defaultRequest.defaults.headers["Authorization"] = - response.headers["authorization"]; - return defaultRequest.request(error.config); - } catch (refreshError) { - // 토큰 갱신에 실패하면 에러 반환 - await alert("토큰 갱신에 실패했습니다. 로그인 페이지로 이동합니다."); - window.location.href = "/signin"; // Redirect to sign-in page - return Promise.reject(refreshError); - } - } + // 토큰 갱신 성공 시 새로운 토큰으로 요청 재시도 + defaultRequest.defaults.headers['Authorization'] = + response.headers['authorization']; + return defaultRequest.request(error.config); + } catch (refreshError) { + // 토큰 갱신에 실패하면 에러 반환 + // await alert('토큰 갱신에 실패했습니다. 로그인 페이지로 이동합니다.'); + // window.location.href = '/signin'; // Redirect to sign-in page + return Promise.reject(refreshError); + } + } - // 401 상태 코드가 아닌 경우에는 그대로 오류 반환 - return Promise.reject(error); - } + // 401 상태 코드가 아닌 경우에는 그대로 오류 반환 + return Promise.reject(error); + }, ); export default defaultRequest;