diff --git a/next.config.js b/next.config.js index 0900d4ff..175063cb 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,10 @@ /** @type {import('next').NextConfig} */ const nextConfig = { images: { + domains: [ + 'team-01-bucket.s3.ap-northeast-2.amazonaws.com', + 'dummyimage.com', + ], remotePatterns: [ { protocol: 'https', diff --git a/src/app/(root)/(routes)/(home)/error.tsx b/src/app/(root)/(routes)/(home)/error.tsx index 55de7d35..bec79a27 100644 --- a/src/app/(root)/(routes)/(home)/error.tsx +++ b/src/app/(root)/(routes)/(home)/error.tsx @@ -18,7 +18,7 @@ export default function ErrorPage({ const { toast } = useToast() useEffect(() => { // Log the error to an error reporting service - console.log(error.digest, error.message, error.name) + if (error.message === ErrorMessages.Forbidden) { console.log('ForbiddenError') toast({ diff --git a/src/app/(root)/(routes)/cards/[cardId]/modify/error.tsx b/src/app/(root)/(routes)/cards/[cardId]/modify/error.tsx index 2cdc24d0..afffdf69 100644 --- a/src/app/(root)/(routes)/cards/[cardId]/modify/error.tsx +++ b/src/app/(root)/(routes)/cards/[cardId]/modify/error.tsx @@ -2,6 +2,10 @@ // Error components must be Client Components import { useEffect } from 'react' +import { useRouter } from 'next/navigation' +import AppPath from '@/config/appPath' +import ErrorMessages from '@/config/errorMessages' +import { useToast } from '@/hooks/useToast' export default function Error({ error, @@ -10,14 +14,41 @@ export default function Error({ error: Error & { digest?: string } reset: () => void }) { + const router = useRouter() + const { toast } = useToast() useEffect(() => { - // Log the error to an error reporting service - console.error(error) - }, [error]) + console.log(error.digest, error.message, error.name) + if (error.message === ErrorMessages.Forbidden) { + console.log('ForbiddenError') + toast({ + title: 'Forbidden', + description: ErrorMessages.Forbidden, + duration: 2000, + }) + } + if (error.message === ErrorMessages.Unauthorized) { + router.push(AppPath.login()) + toast({ + title: 'Unauthorized', + description: ErrorMessages.Unauthorized, + duration: 2000, + }) + } + if (error.message === ErrorMessages.NotFound) { + toast({ + title: 'Not Found', + description: ErrorMessages.NotFound, + duration: 2000, + }) + } + }, [error, router, toast]) return (

Something went wrong!

+

+ Error: {error.message} ({error?.name}) +

))} diff --git a/src/app/(root)/(routes)/cards/page.tsx b/src/app/(root)/(routes)/cards/page.tsx index 08adf368..f07604ce 100644 --- a/src/app/(root)/(routes)/cards/page.tsx +++ b/src/app/(root)/(routes)/cards/page.tsx @@ -1,15 +1,16 @@ import { FunctionComponent } from 'react' +import MaxWidthWrapper from '@/components/domain/max-width-wrapper' import PageTitle from '@/components/domain/page-title' -import CardList from './components/card-list' +import CardListContent from './components/card-list-content' interface CardListPageProps {} const CardListPage: FunctionComponent = ({}) => { return ( -
+ - -
+ + ) } diff --git a/src/app/(root)/(routes)/mypage/components/UserInfo.tsx b/src/app/(root)/(routes)/mypage/components/UserInfo.tsx index 4b5c16cd..18720d94 100644 --- a/src/app/(root)/(routes)/mypage/components/UserInfo.tsx +++ b/src/app/(root)/(routes)/mypage/components/UserInfo.tsx @@ -3,33 +3,52 @@ import React, { useState } from 'react' import AvatarEditable from '@/components/domain/avatar-editable' import TextEditable from '@/components/domain/text-editable' -import { useAuth } from '@/contexts/AuthProvider' +import { useToast } from '@/hooks/useToast' +import { postImageFile } from '@/services/images' import { putUserNickname, putUserProfile } from '@/services/user/user' +import { User } from '@/types/user' -const UserInfo = () => { - const { currentUser } = useAuth() +type UserInfoProps = { + user: User +} + +const UserInfo = ({ user }: UserInfoProps) => { + const { toast } = useToast() const [isProfileChanged, setIsProfileChanged] = useState(true) const [isNicknameChanged, setIsNicknameChanged] = useState(true) const fileChangeHandler = async (file: File) => { setIsProfileChanged(true) try { - const _data = await putUserProfile({ file: file }) + const resUpload = await postImageFile(file) + const resProfile = await putUserProfile(resUpload.data) + window.location.reload() + return resProfile.data } catch (error) { setIsProfileChanged(false) console.log(error) - // TODO: toast error message 추가 + toast({ + title: '프로필 이미지 변경 실패', + description: '프로필 이미지 변경에 실패했습니다.', + variant: 'destructive', + }) } } const nicknameChangeHandler = async (nickname: string) => { setIsNicknameChanged(true) try { - const _data = await putUserNickname(nickname) + const res = await putUserNickname(nickname) + window.location.reload() + return res.data } catch (error) { setIsNicknameChanged(false) console.log(error) - //TODO: toast error message 추가 + toast({ + title: '닉네임 변경 실패', + description: '닉네임 변경에 실패했습니다.', + variant: 'destructive', + }) } } @@ -39,12 +58,12 @@ const UserInfo = () => {
) diff --git a/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list-content/MyTradeHistoryListContent.tsx b/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list-content/MyTradeHistoryListContent.tsx new file mode 100644 index 00000000..bf951512 --- /dev/null +++ b/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list-content/MyTradeHistoryListContent.tsx @@ -0,0 +1,45 @@ +'use client' + +import { useEffect, useRef, Fragment } from 'react' +import ExceptionBoundary from '@/components/domain/exception-boundary' +import { useMyTradeHistoryQuery } from '@/hooks/api/queries/useMyTradeHistoriesQuery' +import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' +import MyTradeHistoryList from '../my-trade-history-list/MyTradeHistoryList' + +const MyTradeHistoryListContent = () => { + const { data, fetchNextPage, isLoading, isError, isFetchingNextPage } = + useMyTradeHistoryQuery() + + const lastElementRef = useRef(null) + const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) + + useEffect(() => { + if (isFetchingNextPage) { + return + } + + if (entry?.isIntersecting) { + fetchNextPage() + } + }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage]) + + const isEmpty = data?.pages[0].data.historyList.length === 0 + + return ( + <> +
+ + + +
+ +
+ + ) +} +export default MyTradeHistoryListContent diff --git a/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list-content/index.tsx b/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list-content/index.tsx new file mode 100644 index 00000000..e52acc8d --- /dev/null +++ b/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list-content/index.tsx @@ -0,0 +1,3 @@ +import MyTradeHistoryListContent from './MyTradeHistoryListContent' + +export default MyTradeHistoryListContent diff --git a/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list/MyTradeHistoryList.tsx b/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list/MyTradeHistoryList.tsx index ead08b76..3395a5d8 100644 --- a/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list/MyTradeHistoryList.tsx +++ b/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list/MyTradeHistoryList.tsx @@ -1,59 +1,27 @@ -'use client' - -import { useEffect, useRef, Fragment } from 'react' -import HistoryCard from '@/components/domain/card/trade-history-card' -import ExceptionBoundary from '@/components/domain/exception-boundary' -import MaxWidthWrapper from '@/components/domain/max-width-wrapper' -import { useMyTradeHistoryQuery } from '@/hooks/api/queries/useMyTradeHistoriesQuery' -import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' -import { MyHistoryRes } from '@/services/history/history' - -const MyCardList = () => { - const { data, fetchNextPage, isLoading, isError, isFetchingNextPage } = - useMyTradeHistoryQuery() - - const lastElementRef = useRef(null) - const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) - - useEffect(() => { - if (isFetchingNextPage) { - return - } - - if (entry?.isIntersecting) { - fetchNextPage() - } - }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage]) - - const isEmpty = data?.pages[0].length === 0 - - return ( - -
- - <> - {data?.pages.map((currentPage, pageIndex) => ( - - {currentPage.map((myHistory: MyHistoryRes) => ( -
- -
- ))} -
- ))} - -
- - {/*TODO: 로딩 부분에 대한 처리 논의 후 구체적으로 적용 할 것 => 를 사용할지, isLoading으로 처리할지 논의 */} -
- -
- - ) -} -export default MyCardList +import { Fragment } from 'react' +import { InfiniteData } from '@tanstack/react-query' +import TradeHistoryCard from '@/components/domain/card/trade-history-card' +import { GetMyTradeHistoryListRes } from '@/services/history/history' +import { TradeHistory } from '@/types/tradeHistory' + +const MyTradeHistoryList = ({ + data, +}: { + data: InfiniteData | undefined +}) => ( + <> + {data?.pages.map( + ({ data: { historyList } }: GetMyTradeHistoryListRes, pageIndex) => ( + + {historyList.map((myHistory: TradeHistory) => ( +
+ +
+ ))} +
+ ), + )} + +) + +export default MyTradeHistoryList diff --git a/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list/index.tsx b/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list/index.tsx index fc4b2257..708901ca 100644 --- a/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list/index.tsx +++ b/src/app/(root)/(routes)/mypage/histories/components/my-trade-history-list/index.tsx @@ -1,3 +1,3 @@ -import HistoryList from './MyTradeHistoryList' +import MyTradeHistoryList from './MyTradeHistoryList' -export default HistoryList +export default MyTradeHistoryList diff --git a/src/app/(root)/(routes)/mypage/histories/page.tsx b/src/app/(root)/(routes)/mypage/histories/page.tsx index cc1208ed..098b4300 100644 --- a/src/app/(root)/(routes)/mypage/histories/page.tsx +++ b/src/app/(root)/(routes)/mypage/histories/page.tsx @@ -1,17 +1,16 @@ import { FunctionComponent } from 'react' +import MaxWidthWrapper from '@/components/domain/max-width-wrapper' import PageTitle from '@/components/domain/page-title' -import MyTradeHistoryList from './components/my-trade-history-list' +import MyTradeHistoryList from './components/my-trade-history-list-content' interface MyHistoryListPageProps {} const MyHistoryListPage: FunctionComponent = ({}) => { return ( -
-
- -
+ + -
+ ) } diff --git a/src/app/(root)/(routes)/mypage/mycards/components/my-card-list-content/MyCardListContent.tsx b/src/app/(root)/(routes)/mypage/mycards/components/my-card-list-content/MyCardListContent.tsx new file mode 100644 index 00000000..133f488c --- /dev/null +++ b/src/app/(root)/(routes)/mypage/mycards/components/my-card-list-content/MyCardListContent.tsx @@ -0,0 +1,57 @@ +'use client' + +import { useEffect, useRef, useState } from 'react' +import ExceptionBoundary from '@/components/domain/exception-boundary' +import MaxWidthWrapper from '@/components/domain/max-width-wrapper' +import { useMyCardsQuery } from '@/hooks/api/queries/useMyCardsQuery' +import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' +import { TradeStatus } from '@/types/card' +import MyCardList from '../my-card-list/MyCardList' +import TradeStatusTabs from '../trade-status-tabs' + +const MyCardListContent = () => { + const [tradeStatus, setTradeStatus] = useState('TRADE_AVAILABLE') + + const { data, fetchNextPage, isLoading, isError, isFetchingNextPage } = + useMyCardsQuery({ + tradeStatus, + }) + + const lastElementRef = useRef(null) + const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) + + useEffect(() => { + if (isFetchingNextPage) { + return + } + + if (entry?.isIntersecting) { + fetchNextPage() + } + }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage]) + + const isEmpty = data?.pages[0].data.cardList.length === 0 + return ( + <> +
+ +
+
+ + + +
+ +
+ + ) +} +export default MyCardListContent diff --git a/src/app/(root)/(routes)/mypage/mycards/components/my-card-list-content/index.tsx b/src/app/(root)/(routes)/mypage/mycards/components/my-card-list-content/index.tsx new file mode 100644 index 00000000..1489b118 --- /dev/null +++ b/src/app/(root)/(routes)/mypage/mycards/components/my-card-list-content/index.tsx @@ -0,0 +1,3 @@ +import MyCardListContent from './MyCardListContent' + +export default MyCardListContent diff --git a/src/app/(root)/(routes)/mypage/mycards/components/my-card-list/MyCardList.tsx b/src/app/(root)/(routes)/mypage/mycards/components/my-card-list/MyCardList.tsx index 21a69a2c..c82e950d 100644 --- a/src/app/(root)/(routes)/mypage/mycards/components/my-card-list/MyCardList.tsx +++ b/src/app/(root)/(routes)/mypage/mycards/components/my-card-list/MyCardList.tsx @@ -1,69 +1,23 @@ -'use client' - -import { useEffect, useRef, Fragment, useState } from 'react' -import ExceptionBoundary from '@/components/domain/exception-boundary' -import MaxWidthWrapper from '@/components/domain/max-width-wrapper' -import { useMyCardsQuery } from '@/hooks/api/queries/useMyCardsQuery' -import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' +import { Fragment } from 'react' +import { InfiniteData } from '@tanstack/react-query' +import { GetMyCardListRes } from '@/services/card/card' import { Card } from '@/types/card' -import { TradeStatus } from '@/types/card' import MyCard from '../my-card' -import TradeStatusTabs from '../trade-status-tabs' - -const MyCardList = () => { - const [tradeStatus, setTradeStatus] = useState('TRADE_AVAILABLE') - - const { data, fetchNextPage, isLoading, isError, isFetchingNextPage } = - useMyCardsQuery({ - tradeStatus, - }) - - const lastElementRef = useRef(null) - const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) - - useEffect(() => { - if (isFetchingNextPage) { - return - } - - if (entry?.isIntersecting) { - fetchNextPage() - } - }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage]) - - const isEmpty = data?.pages[0].length === 0 - - return ( - -
- -
-
- - <> - {data?.pages.map((currentPage, pageIndex) => ( - - {currentPage.map((card: Card) => ( - - ))} - - ))} - - - {/*TODO: 로딩 부분에 대한 처리 논의 후 구체적으로 적용 할 것 => 를 사용할지, isLoading으로 처리할지 논의 */} -
+const MyCardList = ({ + data, +}: { + data: InfiniteData | undefined +}) => ( + <> + {data?.pages.map(({ data: { cardList } }: GetMyCardListRes, pageIndex) => ( + + {cardList.map((myCard: Card) => ( + + ))} + + ))} + +) -
- - ) -} export default MyCardList diff --git a/src/app/(root)/(routes)/mypage/mycards/components/my-card/MyCard.tsx b/src/app/(root)/(routes)/mypage/mycards/components/my-card/MyCard.tsx index ea4a164c..245b4169 100644 --- a/src/app/(root)/(routes)/mypage/mycards/components/my-card/MyCard.tsx +++ b/src/app/(root)/(routes)/mypage/mycards/components/my-card/MyCard.tsx @@ -18,7 +18,7 @@ const MoveToItemListPageButton = ({ priceRange }: { priceRange: string }) => ( ) const MoveToSuggestCheckPageButton = ({ cardId }: { cardId: number }) => ( - + arrow-circle-right{' '} 제안 확인 diff --git a/src/app/(root)/(routes)/mypage/mycards/page.tsx b/src/app/(root)/(routes)/mypage/mycards/page.tsx index 8cd658ab..601d8a01 100644 --- a/src/app/(root)/(routes)/mypage/mycards/page.tsx +++ b/src/app/(root)/(routes)/mypage/mycards/page.tsx @@ -1,15 +1,16 @@ import { FunctionComponent } from 'react' +import MaxWidthWrapper from '@/components/domain/max-width-wrapper' import PageTitle from '@/components/domain/page-title' -import MyCardList from './components/my-card-list' +import MyCardListContent from './components/my-card-list-content' interface MyCardListPageProps {} const MyCardListPage: FunctionComponent = ({}) => { return ( -
+ - -
+ + ) } diff --git a/src/app/(root)/(routes)/mypage/page.tsx b/src/app/(root)/(routes)/mypage/page.tsx index cc1cf823..72d67381 100644 --- a/src/app/(root)/(routes)/mypage/page.tsx +++ b/src/app/(root)/(routes)/mypage/page.tsx @@ -1,14 +1,31 @@ import React from 'react' +import PageTitle from '@/components/domain/page-title' +import ApiEndPoint from '@/config/apiEndPoint' +import apiClient from '@/services/apiClient' +import { User } from '@/types/user' +import { getServerCookie } from '@/utils/getServerCookie' import UserInfo from './components/UserInfo' -const MyPage = () => { +const getUserInfo = async (): Promise => { + const token = getServerCookie() + const res = await apiClient.get( + ApiEndPoint.getValidateUser(), + {}, + { + Authorization: `${token}`, + }, + ) + return res.data.userInfo +} + +const MyPage = async () => { + const userInfo = await getUserInfo() + return (
-
-

마이페이지

-
+
- +
{/* TODO: 각 내용에 대한 라우팅 추가 */}
diff --git a/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-card-description-section/MyCardDescriptionSection.tsx b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-card-description-section/MyCardDescriptionSection.tsx new file mode 100644 index 00000000..5fff3a7d --- /dev/null +++ b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-card-description-section/MyCardDescriptionSection.tsx @@ -0,0 +1,70 @@ +import formatDistanceToNow from 'date-fns/formatDistanceToNow' +import koLocale from 'date-fns/locale/ko' +import Link from 'next/link' +import Badge from '@/components/ui/badge' +import { CardFlex, CardImage, CardText } from '@/components/ui/card' +import AppPath from '@/config/appPath' +import { CardDetail } from '@/types/card' + +const TradeAvailableBadge = () => 거래가능 +const ReservedBadge = () => 예약중 + +type MyCardDescriptionSection = { + card: CardDetail +} + +const MyCardDescriptionSection = ({ + card: { + cardId, + thumbnail, + cardTitle, + status, + itemName, + priceRange, + createdAt, + }, +}: MyCardDescriptionSection) => ( + +
+ +
+ +
+ + + + {cardTitle} + {status === 'TRADE_AVAILABLE' ? ( + + ) : ( + + )} + + {itemName} + {priceRange} + + {formatDistanceToNow(new Date(createdAt), { locale: koLocale })} + + +
+
+ +) + +export default MyCardDescriptionSection diff --git a/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-card-description-section/index.tsx b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-card-description-section/index.tsx new file mode 100644 index 00000000..01b4021d --- /dev/null +++ b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-card-description-section/index.tsx @@ -0,0 +1,3 @@ +import MyCardDescriptionSection from './MyCardDescriptionSection' + +export default MyCardDescriptionSection diff --git a/src/app/(root)/(routes)/mypage/suggestions/components/my-suggestion-card/MySuggestionCard.tsx b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-card/MySuggestionCard.tsx similarity index 71% rename from src/app/(root)/(routes)/mypage/suggestions/components/my-suggestion-card/MySuggestionCard.tsx rename to src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-card/MySuggestionCard.tsx index 6ad59098..a1427af3 100644 --- a/src/app/(root)/(routes)/mypage/suggestions/components/my-suggestion-card/MySuggestionCard.tsx +++ b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-card/MySuggestionCard.tsx @@ -1,26 +1,22 @@ import { formatDistanceToNow } from 'date-fns' import koLocale from 'date-fns/locale/ko' import Image from 'next/image' -import { useSearchParams } from 'next/navigation' +import { useParams } from 'next/navigation' import Button from '@/components/ui/button' import { CardFlex, CardText, Card, CardImage } from '@/components/ui/card/Card' import Assets from '@/config/assets' import { useMySuggestionUpdateMutation } from '@/hooks/api/mutations/useMySuggestionUpdateMutation' -import { MySuggestionRes } from '@/services/suggestion/suggestion' -import { - DirectionType, - SuggestionStatus, - SuggestionType, -} from '@/types/suggestion' +import { Card as CardInfo } from '@/types/card' +import { DirectionType, Suggestion, SuggestionType } from '@/types/suggestion' const SuggestionButtons = ({ handleMySuggestionUpdate, }: { - handleMySuggestionUpdate: (suggestionStatus: SuggestionStatus) => void + handleMySuggestionUpdate: (isAccepted: boolean) => void }) => ( <> handleMySuggestionUpdate('ACCEPTED')} + onClick={() => handleMySuggestionUpdate(true)} className="cursor-pointer" align={'center'} gap={'space'} @@ -29,7 +25,7 @@ const SuggestionButtons = ({ 수락 handleMySuggestionUpdate('REFUSED')} + onClick={() => handleMySuggestionUpdate(false)} className="cursor-pointer" align={'center'} gap={'space'} @@ -59,37 +55,35 @@ const WaitingButton = () => { ) } -type SuggestCheckCardProps = { - mySuggestionListResponseData: MySuggestionRes & { pageInfo: number } +type MySuggestionCardProps = { + mySuggestion: { + suggestionInfo: Suggestion + cardInfo: CardInfo + } suggestionTypeState: SuggestionType directionTypeState: DirectionType } const MySuggestionCard = ({ - mySuggestionListResponseData: { - suggestionInfo: { - suggestionId, - suggestionStatus, - directionType, - createdAt, - }, - cardInfo: { cardTitle, itemName, priceRange, thumbnail }, - pageInfo, + mySuggestion: { + suggestionInfo: { suggestionStatus, directionType, createdAt }, + cardInfo: { cardId, cardTitle, itemName, priceRange, thumbnail }, }, suggestionTypeState, directionTypeState, -}: SuggestCheckCardProps) => { - const searchParams = useSearchParams() +}: MySuggestionCardProps) => { + const { myCardId } = useParams() const { mutate } = useMySuggestionUpdateMutation( suggestionTypeState, directionTypeState, - searchParams.get('cardId'), + myCardId, ) - const handleMySuggestionUpdate = ( - suggestionId: string, - suggestionStatus: SuggestionStatus, - ) => { - mutate({ suggestionId, suggestionStatus, currentPage: pageInfo }) + const handleMySuggestionUpdate = (cardId: number, isAccepted: boolean) => { + mutate({ + fromCardId: cardId, + toCardId: myCardId, + isAccepted, + }) } return ( @@ -120,10 +114,8 @@ const MySuggestionCard = ({ {suggestionStatus === 'WAITING' ? ( directionType === 'RECEIVE' ? ( - handleMySuggestionUpdate(suggestionId, suggestionStatus) + handleMySuggestionUpdate={(isAccepted: boolean) => + handleMySuggestionUpdate(cardId, isAccepted) } /> ) : ( diff --git a/src/app/(root)/(routes)/mypage/suggestions/components/my-suggestion-card/index.tsx b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-card/index.tsx similarity index 100% rename from src/app/(root)/(routes)/mypage/suggestions/components/my-suggestion-card/index.tsx rename to src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-card/index.tsx diff --git a/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-list-content/MySuggestionListContent.tsx b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-list-content/MySuggestionListContent.tsx new file mode 100644 index 00000000..aa4f7a38 --- /dev/null +++ b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-list-content/MySuggestionListContent.tsx @@ -0,0 +1,63 @@ +'use client' + +import { useEffect, useRef, useState } from 'react' +import { useParams } from 'next/navigation' +import ExceptionBoundary from '@/components/domain/exception-boundary' +import { useMySuggestionsQuery } from '@/hooks/api/queries/useMySuggestionsQuery' +import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' +import { DirectionType, SuggestionType } from '@/types/suggestion' +import MySuggestionList from '../my-suggestion-list/MySuggestionList' +import SuggestionStatusTabs from '../suggestion-status-tabs' + +const MySuggestionListContent = () => { + const [suggestionTypeState, setSuggestionTypeState] = + useState('OFFER') + const [directionTypeState, setDirectionTypeState] = + useState('RECEIVE') + const { myCardId } = useParams() + + const { data, fetchNextPage, isLoading, isError, isFetchingNextPage } = + useMySuggestionsQuery(suggestionTypeState, directionTypeState, myCardId) + + const lastElementRef = useRef(null) + const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) + + useEffect(() => { + if (isFetchingNextPage) { + return + } + + if (entry?.isIntersecting) { + fetchNextPage() + } + }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage]) + + const isEmpty = data?.pages[0].data.suggestionList.length === 0 + return ( + <> +
+ +
+
+ + + +
+ +
+ + ) +} +export default MySuggestionListContent diff --git a/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-list-content/index.tsx b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-list-content/index.tsx new file mode 100644 index 00000000..3395bec3 --- /dev/null +++ b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-list-content/index.tsx @@ -0,0 +1,3 @@ +import MySuggestionListContent from './MySuggestionListContent' + +export default MySuggestionListContent diff --git a/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-list/MySuggestionList.tsx b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-list/MySuggestionList.tsx new file mode 100644 index 00000000..0821787d --- /dev/null +++ b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-list/MySuggestionList.tsx @@ -0,0 +1,37 @@ +import { Fragment } from 'react' +import { InfiniteData } from '@tanstack/react-query' +import { GetMySuggestionListRes } from '@/services/suggestion/suggestion' +import { Card } from '@/types/card' +import { DirectionType, Suggestion, SuggestionType } from '@/types/suggestion' +import MySuggestionCard from '../my-suggestion-card' + +const MySuggestionList = ({ + data, + suggestionTypeState, + directionTypeState, +}: { + data: InfiniteData | undefined + suggestionTypeState: SuggestionType + directionTypeState: DirectionType +}) => ( + <> + {data?.pages.map( + ({ data: { suggestionList } }: GetMySuggestionListRes, pageIndex) => ( + + {suggestionList.map( + (mySuggestion: { cardInfo: Card; suggestionInfo: Suggestion }) => ( + + ), + )} + + ), + )} + +) + +export default MySuggestionList diff --git a/src/app/(root)/(routes)/mypage/suggestions/components/my-suggestion-list/index.tsx b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-list/index.tsx similarity index 100% rename from src/app/(root)/(routes)/mypage/suggestions/components/my-suggestion-list/index.tsx rename to src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/my-suggestion-list/index.tsx diff --git a/src/app/(root)/(routes)/mypage/suggestions/components/suggestion-status-tabs/SuggestionStatusTabs.tsx b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/suggestion-status-tabs/SuggestionStatusTabs.tsx similarity index 100% rename from src/app/(root)/(routes)/mypage/suggestions/components/suggestion-status-tabs/SuggestionStatusTabs.tsx rename to src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/suggestion-status-tabs/SuggestionStatusTabs.tsx diff --git a/src/app/(root)/(routes)/mypage/suggestions/components/suggestion-status-tabs/index.tsx b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/suggestion-status-tabs/index.tsx similarity index 100% rename from src/app/(root)/(routes)/mypage/suggestions/components/suggestion-status-tabs/index.tsx rename to src/app/(root)/(routes)/mypage/suggestions/[myCardId]/components/suggestion-status-tabs/index.tsx diff --git a/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/page.tsx b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/page.tsx new file mode 100644 index 00000000..6b229fa7 --- /dev/null +++ b/src/app/(root)/(routes)/mypage/suggestions/[myCardId]/page.tsx @@ -0,0 +1,33 @@ +import MaxWidthWrapper from '@/components/domain/max-width-wrapper' +import PageTitle from '@/components/domain/page-title' +import { getCardInfo } from '@/services/card/card' +import { CardDetail } from '@/types/card' +import MyCardDescriptionSection from './components/my-card-description-section' +import MySuggestionListContent from './components/my-suggestion-list-content' + +async function getMyCardInfo(cardId: string) { + try { + const res = await getCardInfo(Number(cardId)) + return res.data.cardInfo + } catch (e) { + console.log(e) + } +} + +const SuggestCheckListPage = async ({ + params: { myCardId }, +}: { + params: { myCardId: string } +}) => { + const myCard = await getMyCardInfo(myCardId) + + return ( + + + + + + ) +} + +export default SuggestCheckListPage diff --git a/src/app/(root)/(routes)/mypage/suggestions/components/my-suggestion-list/MySuggestionList.tsx b/src/app/(root)/(routes)/mypage/suggestions/components/my-suggestion-list/MySuggestionList.tsx deleted file mode 100644 index fe6b9bbd..00000000 --- a/src/app/(root)/(routes)/mypage/suggestions/components/my-suggestion-list/MySuggestionList.tsx +++ /dev/null @@ -1,89 +0,0 @@ -'use client' - -import { useEffect, useRef, Fragment, useState } from 'react' -import { useSearchParams } from 'next/navigation' -import ExceptionBoundary from '@/components/domain/exception-boundary' -import MaxWidthWrapper from '@/components/domain/max-width-wrapper' -import { useMySuggestionsQuery } from '@/hooks/api/queries/useMySuggestionsQuery' -import { useIntersectionObserver } from '@/hooks/useIntersectionObserver' -import { MySuggestionRes } from '@/services/suggestion/suggestion' -import { DirectionType, SuggestionType } from '@/types/suggestion' -import MySuggestionCard from '../my-suggestion-card' -import SuggestionStatusTabs from '../suggestion-status-tabs' - -const MySuggestionList = () => { - const [suggestionTypeState, setSuggestionTypeState] = - useState('OFFER') - const [directionTypeState, setDirectionTypeState] = - useState('RECEIVE') - const searchParams = useSearchParams() - - const { data, fetchNextPage, isLoading, isError, isFetchingNextPage } = - useMySuggestionsQuery( - suggestionTypeState, - directionTypeState, - searchParams.get('cardId'), - ) - - const lastElementRef = useRef(null) - const entry = useIntersectionObserver(lastElementRef, { threshold: 1.0 }) - - useEffect(() => { - if (isFetchingNextPage) { - return - } - - if (entry?.isIntersecting) { - fetchNextPage() - } - }, [entry?.isIntersecting, fetchNextPage, isFetchingNextPage]) - - const isEmpty = data?.pages[0].length === 0 - - return ( - -
- -
-
- - <> - {data?.pages.map((currentPage, pageIndex) => ( - - {currentPage.map( - ( - mySuggestionListResponseData: MySuggestionRes & { - pageInfo: number - }, - ) => ( - - ), - )} - - ))} - - -
- -
- - ) -} -export default MySuggestionList diff --git a/src/app/(root)/(routes)/mypage/suggestions/page.tsx b/src/app/(root)/(routes)/mypage/suggestions/page.tsx deleted file mode 100644 index 96ca2a5b..00000000 --- a/src/app/(root)/(routes)/mypage/suggestions/page.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { FunctionComponent } from 'react' -import PageTitle from '@/components/domain/page-title' -import { getCardInfo } from '@/services/card/card' -// import MyItemSummaryCard from './components/my-item-summary-card' -import MySuggestionList from './components/my-suggestion-list' - -interface SuggestCheckListPageProps { - params: { - itemId: string - } -} - -async function getItemValue(cardId: number) { - try { - const res = await getCardInfo(cardId) - const data = await res - data.data.cardInfo.thumbnail = - 'https://cdn.cetizen.com/CDN/market/market_large_crop/202203/20220318/220318152808_1_2913635.jpg' - data.data.cardInfo.createdAt = '2023-11-01T08:08:00' - return data.data.cardInfo - } catch (e) { - console.log(e) - } -} - -const SuggestCheckListPage: FunctionComponent< - SuggestCheckListPageProps -> = async ({ params }: SuggestCheckListPageProps) => { - const data = await getItemValue(3) - - return ( -
- - {/* */} - {JSON.stringify(data)} - -
- ) -} - -export default SuggestCheckListPage diff --git a/src/components/domain/header/Header.tsx b/src/components/domain/header/Header.tsx index 546da759..0606a4bd 100644 --- a/src/components/domain/header/Header.tsx +++ b/src/components/domain/header/Header.tsx @@ -8,14 +8,10 @@ import AppPath from '@/config/appPath' import Assets from '@/config/assets' import { useAuth } from '@/contexts/AuthProvider' import Logo from '../logo' -import { MenuButton, Avatar } from './components/Avatar' - -// type HeaderProps = { -// isLogin?: boolean -// } +import { MenuButton, AvatarWithDropdown } from './components' const Header = () => { - const { isLoggedIn } = useAuth() + const { isLoggedIn, currentUser } = useAuth() return (
@@ -31,7 +27,7 @@ const Header = () => { alarm {/** TODO: 알림 컴포넌트로 변경 */} - + {/** TODO: 아바타 컴포넌트로 변경 */} ) : ( diff --git a/src/components/domain/header/components/Avatar.tsx b/src/components/domain/header/components/AvatarWithDropdown.tsx similarity index 54% rename from src/components/domain/header/components/Avatar.tsx rename to src/components/domain/header/components/AvatarWithDropdown.tsx index 3cb6f895..fcad532c 100644 --- a/src/components/domain/header/components/Avatar.tsx +++ b/src/components/domain/header/components/AvatarWithDropdown.tsx @@ -1,6 +1,6 @@ import Cookies from 'js-cookie' -import Image from 'next/image' -import Link from 'next/link' +import { useRouter } from 'next/navigation' +import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar' import Button from '@/components/ui/button' import { DropdownMenu, @@ -10,32 +10,13 @@ import { DropdownMenuItem, } from '@/components/ui/dropdown-menu' import AppPath from '@/config/appPath' -import Assets from '@/config/assets' import { Environment } from '@/config/environment' +import { DEFAULT_PROFILE_IMG } from '@/constants/image' import apiClient from '@/services/apiClient' -//TODO: 공용 아바타 컴포넌트로 변경 +const AvatarWithDropdown = ({ imageUrl }: { imageUrl?: string }) => { + const router = useRouter() -const MenuButton = () => { - return ( - - - - - - - - 홈으로 - - - - - ) -} - -const Avatar = () => { const onClickLogout = () => { Cookies.remove(Environment.tokenName()) apiClient.setDefaultHeader('Authorization', '') @@ -45,10 +26,22 @@ const Avatar = () => { return ( - + + { + router.push(AppPath.mypage()) + }} + > + 내 정보 + 로그아웃 @@ -56,4 +49,4 @@ const Avatar = () => { ) } -export { MenuButton, Avatar } +export default AvatarWithDropdown diff --git a/src/components/domain/header/components/MenuButton.tsx b/src/components/domain/header/components/MenuButton.tsx new file mode 100644 index 00000000..eb173b80 --- /dev/null +++ b/src/components/domain/header/components/MenuButton.tsx @@ -0,0 +1,33 @@ +import Image from 'next/image' +import Link from 'next/link' +import Button from '@/components/ui/button' +import { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, +} from '@/components/ui/dropdown-menu' +import AppPath from '@/config/appPath' +import Assets from '@/config/assets' + +const MenuButton = () => { + return ( + + + + + + + + 홈으로 + + + + + ) +} + +export default MenuButton diff --git a/src/components/domain/header/components/index.tsx b/src/components/domain/header/components/index.tsx new file mode 100644 index 00000000..350a14be --- /dev/null +++ b/src/components/domain/header/components/index.tsx @@ -0,0 +1,4 @@ +import AvatarWithDropdown from './AvatarWithDropdown' +import MenuButton from './MenuButton' + +export { AvatarWithDropdown, MenuButton } diff --git a/src/components/domain/image-uploader/ImageUploader.tsx b/src/components/domain/image-uploader/ImageUploader.tsx index 5c0434d0..027f0ae4 100644 --- a/src/components/domain/image-uploader/ImageUploader.tsx +++ b/src/components/domain/image-uploader/ImageUploader.tsx @@ -86,7 +86,9 @@ const ImageUploader = ({ isDeletable={isImageDeletable} isThumbnail={isThumbnail} onDeleteHandler={() => { + console.log(images) setImages(images.filter((_, i) => i !== index)) + onFilesChanged(images.filter((_, i) => i !== index)) }} /> ) diff --git a/src/components/domain/max-width-wrapper/MaxWidthWrapper.tsx b/src/components/domain/max-width-wrapper/MaxWidthWrapper.tsx index e6218920..57f82256 100644 --- a/src/components/domain/max-width-wrapper/MaxWidthWrapper.tsx +++ b/src/components/domain/max-width-wrapper/MaxWidthWrapper.tsx @@ -1,5 +1,9 @@ -const MaxWidthWrapper = ({ children }: { children: JSX.Element[] }) => { - return
{children}
+const MaxWidthWrapper = ({ + children, +}: { + children: JSX.Element[] | JSX.Element +}) => { + return
{children}
} export default MaxWidthWrapper diff --git a/src/components/domain/text-editable/TextEditable.tsx b/src/components/domain/text-editable/TextEditable.tsx index e132f92e..4922e0d9 100644 --- a/src/components/domain/text-editable/TextEditable.tsx +++ b/src/components/domain/text-editable/TextEditable.tsx @@ -22,6 +22,7 @@ const TextEditable = ({ const [value, setValue] = useState(defaultText) useEffect(() => { + console.log('이름', defaultText) if (!changedSuccessfully) { setValue(() => defaultText) } diff --git a/src/components/ui/card/Card.tsx b/src/components/ui/card/Card.tsx index 70f5e840..e81c5a49 100644 --- a/src/components/ui/card/Card.tsx +++ b/src/components/ui/card/Card.tsx @@ -1,7 +1,6 @@ import * as React from 'react' import { cva, type VariantProps } from 'class-variance-authority' import Image from 'next/image' -import COLORS from '@/styles/colors' import { TYPOGRAPHY } from '@/styles/sizes' import { cn } from '@/utils' diff --git a/src/config/apiEndPoint.ts b/src/config/apiEndPoint.ts index a7fcdf90..ea4751b3 100644 --- a/src/config/apiEndPoint.ts +++ b/src/config/apiEndPoint.ts @@ -1,32 +1,61 @@ -import { TradeStatus } from '@/types/card' -import { DirectionType, SuggestionType } from '@/types/suggestion' +import { GetCardListReq, GetMyCardListReq } from '@/services/card/card' +import { GetMyTradeHistoryListReq } from '@/services/history/history' +import { GetMySuggestionListReq } from '@/services/suggestion/suggestion' +import { getQueryParams } from '@/utils/getQueryParams' const ApiEndPoint = { getValidateUser: () => '/users', test: () => '/test', getCardInfo: (cardId: number) => `/cards/${cardId}`, deleteCard: (cardId: number) => `/cards/${cardId}`, - getCardList: (cursorId: number) => - `/cards/?category&priceRange&cardTitle&cursorId=${cursorId}&status&size`, // TODO: category,priceRange,cardTitle,status,size 적용 - getMyCardList: (status: TradeStatus, cursorId: number) => - `/cards/${status}/my-cards?&size&cursorId=${cursorId}`, // TODO: status 적용 + getCardList: ({ + category, + priceRange, + cardTitle, + cursorId, + }: GetCardListReq) => + `/cards/?${getQueryParams({ + category, + priceRange, + cardTitle, + cursorId, + status: 'TRADE_AVAILABLE,RESERVED', + size: '5', + })}`, + getMyCardList: ({ tradeStatus, cursorId }: GetMyCardListReq) => { + return `/cards/${tradeStatus}/my-cards?${getQueryParams({ + cursorId, + size: '5', + })}` + }, postDibs: (cardId: number) => `/dibs/${cardId}`, deleteDibs: (cardId: number) => `/dibs/${cardId}`, getAvailableCardSuggestionList: (cardId: number) => - `/cards/${cardId}/available-cards`, - getMySuggestionList: ( - directionType: DirectionType, - suggestionType: SuggestionType, - cardId: string | null, - cursorId: number, - ) => - `/suggestions/${directionType}/${suggestionType}/${cardId}/?&size={}&cursorId=${cursorId}`, //TODO: 변수 적용 + `cards/${cardId}/available-cards`, + getMySuggestionList: ({ + directionType, + suggestionType, + cardId, + cursorId, + }: GetMySuggestionListReq) => { + return `/suggestions/${directionType}/${suggestionType}/${cardId}/?${getQueryParams( + { + cursorId, + size: '5', + }, + )}` + }, + putUserProfile: () => '/users/profile-image', putUserNickname: () => '/users/nickname', postSuggestion: (suggestionType: string) => `/suggestions/${suggestionType}`, getMyDibsList: (cursorId: number) => `/dibs/?cursorId=${cursorId}`, - getMyTradeHistoryList: (cursorId: number) => - `/complete-requests/user/?size&cursorId=${cursorId}`, + getMyTradeHistoryList: ({ cursorId }: GetMyTradeHistoryListReq) => { + return `/complete-requests/user/?${getQueryParams({ + cursorId, + size: '5', + })}` + }, postImageFile: () => '/s3/upload/single', postCard: () => '/cards', putCard: (cardId: string) => `/cards/${cardId}`, @@ -37,6 +66,7 @@ const ApiEndPoint = { getRecentTradeHistoryList: (size: number) => `/complete-requests/?size=${size}`, getPopularCardList: () => '/cards/popular', + putMySuggestionStatus: () => `/suggestions/decision`, } as const export default ApiEndPoint diff --git a/src/config/appPath.ts b/src/config/appPath.ts index a5c60269..cd258658 100644 --- a/src/config/appPath.ts +++ b/src/config/appPath.ts @@ -8,8 +8,8 @@ const AppPath = { newCard: () => '/cards/new' as const, cardDetail: (cardId: string) => `/cards/${cardId}` as const, mypage: () => '/mypage' as const, - myItems: () => '/mypage/my-items' as const, - suggestChecks: () => '/mypage/suggest-checks' as const, + myCards: () => '/mypage/mycards' as const, + mySuggestions: () => '/mypage/suggestions' as const, kakaoLogin: () => `${Environment.apiAddress()}/users/oauth2/authorize/kakao/login` as const, googleLogin: () => diff --git a/src/hooks/api/mutations/useMySuggestionUpdateMutation.ts b/src/hooks/api/mutations/useMySuggestionUpdateMutation.ts index 472a53ce..208153ff 100644 --- a/src/hooks/api/mutations/useMySuggestionUpdateMutation.ts +++ b/src/hooks/api/mutations/useMySuggestionUpdateMutation.ts @@ -1,59 +1,53 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' -import { MySuggestionRes } from '@/services/suggestion/suggestion' import { - DirectionType, - SuggestionStatus, - SuggestionType, -} from '@/types/suggestion' + PutMySuggestionStatusReq, + putMySuggestionStatus, +} from '@/services/suggestion/suggestion' +import { DirectionType, SuggestionType } from '@/types/suggestion' export const useMySuggestionUpdateMutation = ( suggestionType: SuggestionType, directionType: DirectionType, - cardId: string | null, + cardId: string | string[], ) => { const queryClient = useQueryClient() return useMutation({ - onMutate: async ({ - suggestionId, - suggestionStatus, - currentPage, - }: { - suggestionId: string - suggestionStatus: SuggestionStatus - currentPage: number - }) => { - await queryClient.cancelQueries({ + mutationFn: async ({ + fromCardId, + toCardId, + isAccepted, + }: PutMySuggestionStatusReq) => { + await putMySuggestionStatus({ fromCardId, toCardId, isAccepted }) + }, + onMutate: async (variables: any) => { + console.log('onMutate', variables) + // const oldMySuggestionList = queryClient.getQueryData([ + // 'my-suggestions', + // suggestionType, + // directionType, + // myCardId, + // ]) + // console.log(oldMySuggestionList) + // }, + // onError: (error, variable, rollback) => { + // if (rollback) rollback() + // else console.log(error) + // }, + // onSettled: () => { + // queryClient.invalidateQueries([ + // 'my-suggestions', + // suggestionType, + // directionType, + // cardId, + // ]) + // }, + // TODO : 에러처리 및 각 종 Optimistic Update 관련 기능 처리하기 + }, + + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ['my-suggestions', suggestionType, directionType, cardId], }) - console.log( - suggestionType, - directionType, - cardId, - suggestionId, - currentPage, - ) - const oldMySuggestionList = queryClient.getQueryData([ - 'my-suggestions', - suggestionType, - directionType, - cardId, - ]) - - const newMySuggestionList: any = structuredClone(oldMySuggestionList) - const currentPageMySuggstionList = newMySuggestionList.pages[currentPage] - const currentMySuggestionList = currentPageMySuggstionList.find( - (mySuggestion: MySuggestionRes) => - mySuggestion.suggestionInfo.suggestionId === suggestionId, - ) - currentMySuggestionList.suggestionInfo.suggestionStatus = suggestionStatus - console.log(newMySuggestionList) - - queryClient.setQueryData( - ['my-suggestions', suggestionType, directionType, cardId], - newMySuggestionList, - ) - return { oldMySuggestionList, newMySuggestionList } }, - // TODO : 에러처리 및 각 종 Optimistic Update 관련 기능 처리하기 }) } diff --git a/src/hooks/api/queries/useCardsQuery.ts b/src/hooks/api/queries/useCardsQuery.ts index b2a5db76..3096c9e9 100644 --- a/src/hooks/api/queries/useCardsQuery.ts +++ b/src/hooks/api/queries/useCardsQuery.ts @@ -1,10 +1,10 @@ import { useInfiniteQuery } from '@tanstack/react-query' -import { getCardList } from '@/services/card/card' -import { Category, PriceRange } from '@/types/card' +import { GetCardListRes, getCardList } from '@/services/card/card' +import { CategoryObjs, PriceRangeObjs } from '@/types/card' export type UseCardsQueryParams = { - category: Category - priceRange: PriceRange + category: CategoryObjs['key'] + priceRange: PriceRangeObjs['key'] cardTitle: string } @@ -16,19 +16,16 @@ export const useCardsQuery = ({ return useInfiniteQuery({ queryKey: ['cards', category, priceRange, cardTitle], - queryFn: async ({ pageParam = 0 }) => + queryFn: async ({ pageParam = undefined }) => await getCardList({ category, priceRange, cardTitle, cursorId: pageParam, }), - initialPageParam: 0, - getNextPageParam: (lastPage, allPages, lastPageParam) => { - if (lastPage.length === 0) { - return undefined - } - return lastPageParam + 1 + initialPageParam: undefined, + getNextPageParam: (lastPage: GetCardListRes) => { + return lastPage.data.nextCursorId }, }) } diff --git a/src/hooks/api/queries/useMyCardsQuery.ts b/src/hooks/api/queries/useMyCardsQuery.ts index bb88118f..7095462d 100644 --- a/src/hooks/api/queries/useMyCardsQuery.ts +++ b/src/hooks/api/queries/useMyCardsQuery.ts @@ -1,5 +1,5 @@ import { useInfiniteQuery } from '@tanstack/react-query' -import { getMyCardList } from '@/services/card/card' +import { GetMyCardListRes, getMyCardList } from '@/services/card/card' import { TradeStatus } from '@/types/card' export type UseMyCardsQueryParams = { @@ -9,17 +9,14 @@ export type UseMyCardsQueryParams = { export const useMyCardsQuery = ({ tradeStatus }: UseMyCardsQueryParams) => { return useInfiniteQuery({ queryKey: ['my-cards', tradeStatus], - queryFn: async ({ pageParam = 0 }) => + queryFn: async ({ pageParam = undefined }) => await getMyCardList({ tradeStatus, cursorId: pageParam, }), - initialPageParam: 0, - getNextPageParam: (lastPage, allPages, lastPageParam) => { - if (lastPage.length === 0) { - return undefined - } - return lastPageParam + 1 + initialPageParam: undefined, + getNextPageParam: (lastPage: GetMyCardListRes) => { + return lastPage.data.nextCursorId }, }) } diff --git a/src/hooks/api/queries/useMySuggestionsQuery.ts b/src/hooks/api/queries/useMySuggestionsQuery.ts index 93a42360..97221d85 100644 --- a/src/hooks/api/queries/useMySuggestionsQuery.ts +++ b/src/hooks/api/queries/useMySuggestionsQuery.ts @@ -1,39 +1,24 @@ import { useInfiniteQuery } from '@tanstack/react-query' import { getMySuggestionList } from '@/services/suggestion/suggestion' -import { MySuggestionRes } from '@/services/suggestion/suggestion' import { DirectionType, SuggestionType } from '@/types/suggestion' export const useMySuggestionsQuery = ( suggestionType: SuggestionType, directionType: DirectionType, - cardId: string | null, + cardId: string | string[], ) => { return useInfiniteQuery({ queryKey: ['my-suggestions', suggestionType, directionType, cardId], - queryFn: async ({ pageParam = 0 }) => { - const data: MySuggestionRes[] = await getMySuggestionList({ + queryFn: async ({ pageParam = undefined }) => + await getMySuggestionList({ suggestionType, directionType, cardId, cursorId: pageParam, - }) - - const dataContainingPageInfo = data.map( - (mySuggestion: MySuggestionRes) => ({ - ...mySuggestion, - pageInfo: pageParam, - }), - ) - - return dataContainingPageInfo - }, - - initialPageParam: 0, - getNextPageParam: (lastPage, allPages, lastPageParam) => { - if (lastPage.length === 0) { - return undefined - } - return lastPageParam + 1 + }), + initialPageParam: undefined, + getNextPageParam: (lastPage: any) => { + return lastPage.data.nextCursorId }, }) } diff --git a/src/hooks/api/queries/useMyTradeHistoriesQuery.ts b/src/hooks/api/queries/useMyTradeHistoriesQuery.ts index 9a128a53..6232e5e6 100644 --- a/src/hooks/api/queries/useMyTradeHistoriesQuery.ts +++ b/src/hooks/api/queries/useMyTradeHistoriesQuery.ts @@ -1,19 +1,19 @@ import { useInfiniteQuery } from '@tanstack/react-query' -import { getMyTradeHistoryList } from '@/services/history/history' +import { + GetMyTradeHistoryListRes, + getMyTradeHistoryList, +} from '@/services/history/history' export const useMyTradeHistoryQuery = () => { return useInfiniteQuery({ queryKey: ['my-trade-histories'], - queryFn: async ({ pageParam = 0 }) => + queryFn: async ({ pageParam = undefined }) => await getMyTradeHistoryList({ cursorId: pageParam, }), - initialPageParam: 0, - getNextPageParam: (lastPage, allPages, lastPageParam) => { - if (lastPage.length === 0) { - return undefined - } - return lastPageParam + 1 + initialPageParam: undefined, + getNextPageParam: (lastPage: GetMyTradeHistoryListRes) => { + return lastPage.data.nextCursorId }, }) } diff --git a/src/middleware.ts b/src/middleware.ts index d6a21c74..d32be041 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -37,5 +37,5 @@ export async function middleware(request: NextRequest) { // See "Matching Paths" below to learn more export const config = { - matcher: ['/test-auth-only'], + matcher: ['/mypage', '/cards/:path/modify', '/cards/new'], } diff --git a/src/services/card/card.ts b/src/services/card/card.ts index 71e4cc4d..07b7a758 100644 --- a/src/services/card/card.ts +++ b/src/services/card/card.ts @@ -1,21 +1,15 @@ import { CardUploadFormValues } from '@/app/(root)/(routes)/cards/new/hooks/useCardUploadForm' import ApiEndPoint from '@/config/apiEndPoint' import type { + Card, + CategoryObjs, CardDetail, - Category, - PriceRange, TradeStatus, + PriceRangeObjs, } from '@/types/card' import { User } from '@/types/user' import apiClient from '../apiClient' -export type Getcards = { - category: Category - priceRange: PriceRange - cardTitle: string - cursorId: number -} - type putCardReq = { cardId: string cardReq: CardUploadFormValues @@ -57,14 +51,30 @@ const putCard = async ({ cardId, cardReq }: putCardReq) => { return response } -// TODO: 현재 cardsHandler(가짜 API)는 필터에 대한 처리가 이루어져 있지 않으므로, cursorId만 넘겨주고 있음 => 실 API를 받을시 교체 작업 해주기 +export type GetCardListReq = { + category: CategoryObjs['key'] + priceRange: PriceRangeObjs['key'] + cardTitle: string + cursorId: string | undefined +} +export type GetCardListRes = { + code: string + message: string + data: { + cardList: Card[] + nextCursorId: string + } +} + const getCardList = async ({ category, priceRange, cardTitle, cursorId, -}: Getcards) => { - const response = await apiClient.get(ApiEndPoint.getCardList(cursorId)) +}: GetCardListReq) => { + const response: GetCardListRes = await apiClient.get( + ApiEndPoint.getCardList({ category, priceRange, cardTitle, cursorId }), + ) return response } export type CardInfoRes = { @@ -73,23 +83,32 @@ export type CardInfoRes = { data: { cardInfo: CardDetail; userInfo: User } } -const getCardInfo = async (cardId: number) => { - const response: CardInfoRes = await apiClient.get( - ApiEndPoint.getCardInfo(cardId), - ) +const getCardInfo = async ( + cardId: number, +): Promise<{ data: { cardInfo: CardDetail; userInfo: User } }> => { + const response = await apiClient.get(ApiEndPoint.getCardInfo(cardId), { + cache: 'no-store', + }) + return response } -const getMyCardList = async ({ - tradeStatus, - cursorId, -}: { +export type GetMyCardListReq = { tradeStatus: TradeStatus - cursorId: number -}) => { - const response = await apiClient.get( - ApiEndPoint.getMyCardList(tradeStatus, cursorId), - {}, + cursorId: string | undefined +} +export type GetMyCardListRes = { + code: string + message: string + data: { + cardList: Card[] + nextCursorId: string + } +} + +const getMyCardList = async ({ tradeStatus, cursorId }: GetMyCardListReq) => { + const response: GetMyCardListRes = await apiClient.get( + ApiEndPoint.getMyCardList({ tradeStatus, cursorId }), ) return response } diff --git a/src/services/history/history.ts b/src/services/history/history.ts index 1e047297..97381b33 100644 --- a/src/services/history/history.ts +++ b/src/services/history/history.ts @@ -2,11 +2,23 @@ import ApiEndPoint from '@/config/apiEndPoint' import { TradeHistory } from '@/types/tradeHistory' import apiClient from '../apiClient' -export type MyHistoryRes = TradeHistory +export type GetMyTradeHistoryListReq = { + cursorId: string | undefined +} +export type GetMyTradeHistoryListRes = { + code: string + message: string + data: { + historyList: TradeHistory[] + nextCursorId: string + } +} -const getMyTradeHistoryList = async ({ cursorId }: { cursorId: number }) => { - const response: MyHistoryRes[] = await apiClient.get( - ApiEndPoint.getMyTradeHistoryList(cursorId), +const getMyTradeHistoryList = async ({ + cursorId, +}: GetMyTradeHistoryListReq) => { + const response: GetMyTradeHistoryListRes = await apiClient.get( + ApiEndPoint.getMyTradeHistoryList({ cursorId }), ) return response } diff --git a/src/services/suggestion/suggestion.ts b/src/services/suggestion/suggestion.ts index 942e6d9d..10d39bba 100644 --- a/src/services/suggestion/suggestion.ts +++ b/src/services/suggestion/suggestion.ts @@ -40,16 +40,22 @@ const postSuggestion = async ({ return response } -type GetSuggestChecksParams = { +export type GetMySuggestionListReq = { suggestionType: SuggestionType directionType: DirectionType - cardId: string | null - cursorId: number + cardId: string | string[] + cursorId: string | undefined } - -export type MySuggestionRes = { - cardInfo: Card - suggestionInfo: Suggestion +export type GetMySuggestionListRes = { + code: string + message: string + data: { + suggestionList: { + cardInfo: Card + suggestionInfo: Suggestion + }[] + nextCursorId: string + } } const getMySuggestionList = async ({ @@ -57,16 +63,59 @@ const getMySuggestionList = async ({ directionType, cardId, cursorId, -}: GetSuggestChecksParams) => { - const response: MySuggestionRes[] = await apiClient.get( - ApiEndPoint.getMySuggestionList( +}: GetMySuggestionListReq) => { + const response: GetMySuggestionListRes = await apiClient.get( + ApiEndPoint.getMySuggestionList({ directionType, suggestionType, cardId, cursorId, - ), + }), ) return response } -export { getAvailableCardSuggestionList, getMySuggestionList, postSuggestion } +export type PutMySuggestionStatusReq = { + fromCardId: number + toCardId: string | string[] + isAccepted: boolean +} +export type PutMySuggestionStatusRes = { + code: string + message: string + data: { + suggestionList: { + cardInfo: Card + suggestionInfo: Suggestion + }[] + nextCursorId: string + } +} + +const putMySuggestionStatus = async ({ + fromCardId, + toCardId, + isAccepted, +}: PutMySuggestionStatusReq) => { + const response: PutMySuggestionStatusRes = await apiClient.put( + ApiEndPoint.putMySuggestionStatus(), + { + fromCardId, + toCardId, + isAccepted, + }, + {}, + { + 'Content-Type': 'application/json', + }, + ) + + return response +} + +export { + getAvailableCardSuggestionList, + getMySuggestionList, + postSuggestion, + putMySuggestionStatus, +} diff --git a/src/services/user/user.ts b/src/services/user/user.ts index 200db439..595f7643 100644 --- a/src/services/user/user.ts +++ b/src/services/user/user.ts @@ -1,19 +1,16 @@ +import { revalidateTag } from 'next/cache' import ApiEndPoint from '@/config/apiEndPoint' import apiClient from '../apiClient' -type putUserProfileReq = { - file: File -} - -const putUserProfile = async ({ file }: putUserProfileReq) => { - const formData = new FormData() - formData.append('profile', file) +const putUserProfile = async (imageUrl: string) => { const response = await apiClient.put( ApiEndPoint.putUserProfile(), - formData, + { + imageUrl, + }, {}, { - 'Content-Type': 'multipart/form-data', + 'Content-Type': 'application/json', }, ) @@ -21,9 +18,16 @@ const putUserProfile = async ({ file }: putUserProfileReq) => { } const putUserNickname = async (nickname: string) => { - const response = await apiClient.put(ApiEndPoint.putUserNickname(), { - nickname, - }) + const response = await apiClient.put( + ApiEndPoint.putUserNickname(), + { + nickname, + }, + {}, + { + 'Content-Type': 'application/json', + }, + ) return response } diff --git a/src/types/card.ts b/src/types/card.ts index 960fc4c7..3d67a992 100644 --- a/src/types/card.ts +++ b/src/types/card.ts @@ -2,8 +2,8 @@ import { CATEGORY, TRADE_TYPE, CARD_TRADE_STATUS, - PRICE_RANGE, CATEGORY_OBJS, + PRICE_RANGE, PRICE_RANGE_OBJS, TRADE_STATUS_OBJS, TRADE_TYPE_OBJS, diff --git a/src/types/user.ts b/src/types/user.ts index e8d50bb2..7106d951 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -2,7 +2,7 @@ interface User { userId: number accountId: string nickname: string - role: 'USER' | 'ADMIN' + role: 'ROLE_USER' | 'ROLE_ADMIN' createdDate: string modifiedDate: string imageUrl?: string diff --git a/src/utils/getQueryParams.ts b/src/utils/getQueryParams.ts new file mode 100644 index 00000000..5cddbf18 --- /dev/null +++ b/src/utils/getQueryParams.ts @@ -0,0 +1,11 @@ +export const getQueryParams = (paramsObject: any) => { + const params = new URLSearchParams() + + for (const [key, value] of Object.entries(paramsObject)) { + if (value) { + params.append(key, value.toString()) + } + } + + return params.toString() +}